2016-06-10 89 views
0

我想让杰克逊忽略DTO的某些属性,但是我似乎无法做到。 我有一个相当大的项目与许多依赖项(龙目,春季,GWT,Gemfire和更多), ,我不能改变这些依赖关系(也许我可以改变版本,但它不是我的电话)。杰克逊忽略@Ignore注释

我已经准备了一个测试案例,那就是:

,这是我的测试DTO类,它有一个地图是唯一有用的 服务器端。该dto的副本被序列化以发送到gwt 以显示(实施未完成,仅显示相关部分 )。

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 
} 

测试模型从一个超类继承某些领域

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; 
} 

这里是单元测试我准备

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()); 
    } 

} 

所有的测试与

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

所以问题是: 为什么杰克逊试图为无法访问的地图的键找到解串器(因为没有getter和setter),并且用@JsonIgnore注解了? 更重要的是,我怎么能告诉它不要搜索反序列化器?

这些都是我的POM相关的依赖关系,如果它可以是任何帮助:

<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> 

回答

1

事实证明,这是龙目岛和杰克逊之间的互动不良的情况。 龙目岛注释@AllArgsConstructor生成一个构造函数,该构造函数使用@ConstructorProperties进行注释,该构造函数反过来列出在该类中声明的所有属性。 当默认的解串器被使用时,Jackson就会使用它。 在这种情况下,没有考虑setter和getter的存在以及@JsonIgnore注释的存在。

解决方法是指定@AllArgsConstructor与属性suppressConstructorProperties设置为true:

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

您还可以在'lombok.config'中限制整个包或项目的ConstructorProperties的生成:'lombok.anyConstructor.suppressConstructorProperties = false'。 –

2

棘手的,确实如此。我认为正在发生的事情是你正在用Lombok产生一个公开的所有参数构造函数。反序列化时,这是Jackson将尝试使用的那个。如果您将您的MyClass注释更改为

@AllArgsConstructor(access = AccessLevel.PRIVATE) 

...应该可以正常工作。祝你好运!