2016-06-10 3 views
0

J'essaie de laisser Jackson ignorer certaines propriétés d'un DTO, mais je n'arrive pas à le faire. J'ai un assez gros projet avec de nombreuses dépendances (Lombok, Spring, GWT, Gemfire et bien d'autres), et je ne peux pas changer ces dépendances (peut-être que je peux changer de version, mais ce n'est pas mon appel).Jackson ignore les annotations @Ignore

J'ai préparé un test, voici:

ceci est ma classe dpour test, il a une carte qui est seulement côté serveur utile. Une copie de ce dto est sérialisée pour être envoyée à gwt pour afficher (l'implémentation n'est pas complète, seules les parties pertinentes sont montrées).

import com.fasterxml.jackson.annotation.JsonIgnore; 
import com.fasterxml.jackson.annotation.JsonIgnoreType; 
import lombok.*; 

import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 

@Getter 
@Setter 
@Builder 
@NoArgsConstructor 
@AllArgsConstructor 
@EqualsAndHashCode(of = "id", callSuper = true) 
public class MyClass extends MyAbstractClass { 

    @Getter 
    @Setter 
    @Builder 
    public static class AValueClass { 
     int someInt; 
     String SomeString; 
    } 

    @Data 
    @AllArgsConstructor 
    @NoArgsConstructor 
    @JsonIgnoreType 
    public static class MyJsonIgnoreKeyClass { 
     protected Integer anInt; 
     protected String aString; 
    } 

    @JsonIgnore 
    @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) 
    private transient Map<MyJsonIgnoreKeyClass, List<AValueClass>> aMapThatJacksonShouldIgnore = new HashMap<>(); 


    public void addToMap(MyJsonIgnoreKeyClass key, AValueClass value) { 
     List<AValueClass> valueList = aMapThatJacksonShouldIgnore.get(key); 
     if(valueList == null) { 
      valueList = new ArrayList<>(); 
     } 
     valueList.add(value); 
     aMapThatJacksonShouldIgnore.put(key,valueList); 
    } 

    public boolean noMap() { 
     return aMapThatJacksonShouldIgnore == null || aMapThatJacksonShouldIgnore.keySet().isEmpty(); 
    } 

    public void nullifyMap() { 
     aMapThatJacksonShouldIgnore = null; 
    } 

    // other methods operating on maps omitted 
} 

le modèle de test hérite des champs d'une superclasse

import lombok.EqualsAndHashCode; 
import lombok.Getter; 
import lombok.Setter; 

import java.util.Date; 

@Setter 
@Getter 
@EqualsAndHashCode(of = "id") 
public class MyAbstractClass { 

    protected String id; 
    protected Date aDay; 
} 

ici sont les tests unitaires J'ai préparé

public class MyClassJacksonTest { 

    ObjectMapper om; 

    @Before 
    public void setUp() throws Exception { 
     om = new ObjectMapper().registerModule(new Jdk8Module()); 
     om.setSerializationInclusion(JsonInclude.Include.NON_NULL); 
     om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); 
    } 

    @Test 
    public void testWithMapValues() throws Exception { 
     MyClass testClass = new MyClass(); 
     testClass.setADay(new Date()); 
     testClass.setId(UUID.randomUUID().toString()); 
     testClass.addToMap(
       new MyClass.MyJsonIgnoreKeyClass(1,"test"), 
       new MyClass.AValueClass(1,"test")); 

     StringWriter writer = new StringWriter(); 
     om.writeValue(writer,testClass); 
     writer.flush(); 
     String there = writer.toString(); 
     MyClass andBackAgain = om.readValue(there, MyClass.class); 

     assertTrue(andBackAgain.noMap()); 
    } 

    @Test 
    public void testWithEmptyMaps() throws Exception { 
     MyClass testClass = new MyClass(); 
     testClass.setADay(new Date()); 
     testClass.setId(UUID.randomUUID().toString()); 

     StringWriter writer = new StringWriter(); 
     om.writeValue(writer,testClass); 
     writer.flush(); 
     String there = writer.toString(); 
     MyClass andBackAgain = om.readValue(there, MyClass.class); 

     assertTrue(andBackAgain.noMap()); 
    } 

    @Test 
    public void testWithNullMaps() throws Exception { 
     MyClass testClass = new MyClass(); 
     testClass.setADay(new Date()); 
     testClass.setId(UUID.randomUUID().toString()); 
     testClass.nullifyMap(); 

     StringWriter writer = new StringWriter(); 
     om.writeValue(writer,testClass); 
     writer.flush(); 
     String there = writer.toString(); 
     MyClass andBackAgain = om.readValue(there, MyClass.class); 

     assertTrue(andBackAgain.noMap()); 
    } 

} 

Tous les tests échouent avec

com.fasterxml.jackson.databind.JsonMappingException: Can not find a (Map) Key deserializer for type [simple type, class MyClass$MyJsonIgnoreKeyClass] 

Donc les questions sont : Pourquoi Jackson essaie-t-il de trouver un désérialiseur pour les clés d'une carte inaccessible (puisqu'il n'y a pas de getter et setter) et qui est annoté avec @JsonIgnore? Plus important encore, comment puis-je lui dire de ne pas rechercher les désérialiseurs?

Ce sont les dépendances pertinentes sur mon pom, si elle peut être d'aucune aide:

<properties> 
    <!-- ... --> 
    <jackson.version>2.7.4</jackson.version> 
</properties> 

<dependencies> 
    <!-- other dependencies omitted --> 
    <dependency> 
     <groupId>com.fasterxml.jackson.core</groupId> 
     <artifactId>jackson-databind</artifactId> 
     <version>${jackson.version}</version> 
    </dependency> 
    <dependency> 
     <groupId>com.fasterxml.jackson.module</groupId> 
     <artifactId>jackson-module-jsonSchema</artifactId> 
     <version>${jackson.version}</version> 
    </dependency> 
    <dependency> 
     <groupId>com.fasterxml.jackson.datatype</groupId> 
     <artifactId>jackson-datatype-jdk8</artifactId> 
     <version>${jackson.version}</version> 
    </dependency> 
    <dependency> 
     <groupId>com.fasterxml.jackson.datatype</groupId> 
     <artifactId>jackson-datatype-jsr353</artifactId> 
     <version>${jackson.version}</version> 
    </dependency> 
    <dependency> 
     <groupId>com.fasterxml.jackson.dataformat</groupId> 
     <artifactId>jackson-dataformat-xml</artifactId> 
     <version>${jackson.version}</version> 
     <exclusions> 
      <exclusion> 
       <groupId>org.codehaus.woodstox</groupId> 
       <artifactId>stax2-api</artifactId> 
      </exclusion> 
     </exclusions> 
    </dependency> 
</dependencies> 

Répondre

1

Il se trouve que cela est un cas de mauvaise interaction entre Lombok et Jackson. L'annotation Lombok @AllArgsConstructor génère un constructeur annoté avec @ConstructorProperties, qui liste à son tour toutes les propriétés déclarées dans la classe. Ceci est ensuite utilisé par Jackson lorsque le désérialiseur par défaut doit être utilisé. Dans ce cas, l'absence de setters et de getters et la présence d'annotations @JsonIgnore ne sont pas prises en compte.

La solution est simplement de spécifier le @AllArgsConstructor avec l'attribut suppressConstructorProperties valeur true:

@Getter 
@Setter 
@Builder 
@NoArgsConstructor 
@AllArgsConstructor(suppressConstructorProperties = true) 
@EqualsAndHashCode(of = "id", callSuper = true) 
public class MyClass extends MyAbstractClass { 
    // everything else is unchanged 
+0

Vous pouvez également supprimer la génération de ConstructorProperties pour un paquet entier ou un projet dans 'lombok.config':' lombok.anyConstructor.suppressConstructorProperties = false'. –

2

un Tricky, en effet. Ce que je pense est que vous générez un constructeur public de tous les arguments avec Lombok. Lors de la désérialisation, c'est celui que Jackson va essayer d'utiliser. Si vous modifiez votre annotation pour MyClass à

@AllArgsConstructor(access = AccessLevel.PRIVATE) 

... cela devrait fonctionner correctement. Bonne chance!