【问题标题】:Jackson ignores @Ignore annotationsJackson 忽略 @Ignore 注释
【发布时间】:2016-06-10 09:00:29
【问题描述】:

我试图让 Jackson 忽略 DTO 的某些属性,但我似乎无法做到。 我有一个相当大的项目,有很多依赖项(Lombok、Spring、GWT、Gemfire 等等), 而且我无法更改这些依赖项(也许我可以更改版本,但这不是我的决定)。

我准备了一个测试用例,这里是:

这是我的测试 dto 类,它有一个唯一有用的地图 服务器端。此 dto 的副本被序列化以发送到 gwt to display(实现不完整,只有相关部分 显示)。

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]

所以问题是: 为什么 Jackson 试图为无法访问的映射的键(因为没有 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>

【问题讨论】:

    标签: java json jackson lombok


    【解决方案1】:

    事实证明,这是龙目岛和杰克逊之间互动不良的案例。 Lombok 注解@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
    

    【讨论】:

    • 您还可以在lombok.config:lombok.anyConstructor.suppressConstructorProperties=false中禁止为整个包或项目生成ConstructorProperties。
    【解决方案2】:

    确实是一个棘手的问题。我认为正在发生的是您正在使用 Lombok 生成一个公共的所有参数构造函数。反序列化时,Jackson 会尝试使用它。如果您将 MyClass 的注释更改为

    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-03-08
      • 1970-01-01
      • 2018-07-07
      • 1970-01-01
      • 1970-01-01
      • 2015-10-19
      • 2015-05-31
      相关资源
      最近更新 更多