Jackson 忽略@Ignore 注释
Jackson ignores @Ignore annotations
我试图让 Jackson 忽略 DTO 的某些属性,但我似乎做不到。
我有一个相当大的项目,有很多依赖项(Lombok、Spring、GWT、Gemfire 等等),
而且我无法更改这些依赖项(也许我可以更改版本,但这不是我的决定)。
我准备了一个测试用例,这里是:
这是我的测试dto class,它有一个地图,只是有用的
服务器端。此 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
}
测试模型从超类继承了一些字段class
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>
原来这是一起Lombok和Jackson互动不好的案例。
Lombok 注解 @AllArgsConstructor
生成一个用 @ConstructorProperties
注解的构造函数,它依次列出在 class 中声明的所有属性。
当要使用默认反序列化器时,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 生成 public 所有参数构造函数。反序列化时,Jackson 将尝试使用它。如果您将 MyClass 的注释更改为
@AllArgsConstructor(access = AccessLevel.PRIVATE)
...应该可以正常工作。祝你好运!
我试图让 Jackson 忽略 DTO 的某些属性,但我似乎做不到。 我有一个相当大的项目,有很多依赖项(Lombok、Spring、GWT、Gemfire 等等), 而且我无法更改这些依赖项(也许我可以更改版本,但这不是我的决定)。
我准备了一个测试用例,这里是:
这是我的测试dto class,它有一个地图,只是有用的 服务器端。此 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
}
测试模型从超类继承了一些字段class
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>
原来这是一起Lombok和Jackson互动不好的案例。
Lombok 注解 @AllArgsConstructor
生成一个用 @ConstructorProperties
注解的构造函数,它依次列出在 class 中声明的所有属性。
当要使用默认反序列化器时,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 生成 public 所有参数构造函数。反序列化时,Jackson 将尝试使用它。如果您将 MyClass 的注释更改为
@AllArgsConstructor(access = AccessLevel.PRIVATE)
...应该可以正常工作。祝你好运!