【发布时间】:2017-06-08 07:00:56
【问题描述】:
在我的 Java 应用程序中使用 Jackson 进行序列化(POJO 到 JSON)和反序列化(JSON 到 POJO)时,我通常希望保留所有字段,因此使用(默认)JsonInclude.Value.ALWAYS。
为了允许通过应用程序的 Rest API 进行部分更新,我还想区分设置为 null 的值和保持不变的值。为此,Java8 Optional<?> 类似乎是正确的选择。
为了获得适当的支持,我必须将Jdk8Module 添加到ObjectMapper。一切都很简单。
开箱即用的反序列化行为正是我想要的。不存在的字段保持其默认值(此处为:null),并且显式提供的 null 值被反序列化为 Optional.empty()(或 Optional.ofNullable(null))。
我想要的是从生成的 JSON 中排除具有显式值 null 的 Optional<?> 字段,但始终包含任何其他字段(例如普通的 Integer)(甚至如果是null)。
MixIn 是众多可用选项之一。不幸的是,MixIn 可能适用于其他注释,但不适用于 @JsonInclude(这似乎是 Jackson 中的一个错误)。
public class OptionalObjectMappingTest {
public static class MyBean {
public Integer one;
public Optional<Integer> two;
public Optional<Integer> three;
public Optional<Integer> four;
}
@JsonInclude(JsonInclude.Include.NOT_NULL)
public static class OptionalMixIn {}
private ObjectMapper initObjectMapper() {
return new ObjectMapper()
.registerModule(new Jdk8Module())
.setSerialisationInclusion(JsonInclude.Include.ALWAYS)
.addMixIn(Optional.class, OptionalMixIn.class);
}
@Test
public void testDeserialisation() {
String json = "{\"one\":null,\"two\":2,\"three\":null}";
MyBean bean = initObjectMapper().readValue(json, MyBean.class);
Assert.assertNull(bean.one);
Assert.assertEquals(Optional.of(2), bean.two);
Assert.assertEquals(Optional.empty(), bean.three);
Assert.assertNull(bean.four);
}
@Test
public void testSerialisation() {
MyBean bean = new MyBean();
bean.one = null;
bean.two = Optional.of(2);
bean.three = Optional.empty();
bean.four = null;
String result = initObjectMapper().writeValueAsString(bean);
String expected = "{\"one\":null,\"two\":2,\"three\":null}";
// FAILS, due to result = "{one:null,two:2,three:null,four:null}"
Assert.assertEquals(expected, result);
}
}
有很多方法可以(动态地)包含/排除字段及其值,但似乎没有一种“官方”方法可以解决问题:
-
每个
Optional<?>字段上的@JsonInclude注释实际上可以满足我的需求,但这太容易忘记和麻烦了。 - 自定义
MixIn应允许对每种类型的JsonInclude注释进行全局定义,但显然未应用(根据上述示例测试)。 -
@JsonIgnore(及相关)注解是静态的,不关心字段的值。 -
@JsonFilter需要在每个包含Optional字段的类上设置,并且您需要知道PropertyFilter中每个受影响的类型。 IE。甚至比在每个Optional字段上添加JsonInclude更有价值。 -
@JsonView不允许基于给定 bean 实例的字段值动态包含/排除字段。 - 通过
ObjectMapper.setNullValueSerializer()注册的自定义JsonSerializer<?>仅在插入字段名称后调用,即如果我们什么都不做,生成的JSON是无效的。 - 在将字段名称插入 JSON 之前涉及自定义
BeanSerializerModifier,但它无权访问该字段的值。
【问题讨论】:
标签: java serialization java-8 jackson optional