Java 使用Jackson反序列化JSON时的隐式默认值
当反序列化各种JSON消息时,我想为特定类型的属性提供一个默认值。只需在类中指定值,但如果必须跨多个类执行此操作,则很容易出错。您可能会忘记一个,结果是Java 使用Jackson反序列化JSON时的隐式默认值,java,json,jackson,deserialization,Java,Json,Jackson,Deserialization,当反序列化各种JSON消息时,我想为特定类型的属性提供一个默认值。只需在类中指定值,但如果必须跨多个类执行此操作,则很容易出错。您可能会忘记一个,结果是null而不是默认值。我的意图是将可选的每个属性设置为可选。缺少。由于null正是Optional试图消除的,因此与Jackson一起使用它们被证明是令人沮丧的 Jackson的大多数特性允许您自定义反序列化过程,它们关注的是作为输入的JSON,而不是实例化要反序列化的对象的过程。我似乎最接近通用解决方案的方法是构建自己的ValueInstant
null
而不是默认值。我的意图是将可选的每个属性设置为可选。缺少。由于null
正是Optional
试图消除的,因此与Jackson一起使用它们被证明是令人沮丧的
Jackson的大多数特性允许您自定义反序列化过程,它们关注的是作为输入的JSON,而不是实例化要反序列化的对象的过程。我似乎最接近通用解决方案的方法是构建自己的ValueInstantiator
,但我还有两个问题:
- 如何使其仅实例化
可选
为缺席
,而不干扰实例化过程的其余部分
- 如何将最终结果连接到我的
ObjectMapper
更新:我想澄清一下,我正在寻找一种解决方案,它不涉及修改包含可选的每个类。我反对违反干燥原则。我或我的同事不应该在每次向新的或现有的类添加可选的时都考虑必须做一些额外的事情。我想说,“让我反序列化到的每个类中的每个可选字段都预先填充缺席
”,只做一次,然后完成它
这意味着以下内容已过时:
- 抽象父类(需要声明)
- 自定义生成器/创建者/JsonDeserializer(需要对每个适用类进行注释)
- 米辛的?我尝试了这个,结合了反思,但我不知道如何访问我正在混入的类
您可以编写自定义反序列化程序来处理默认值。实际上,您将针对正在反序列化的对象类型扩展适当的反序列化程序,获取反序列化的值,如果该值为null
,则只返回适当的默认值
这里有一个使用字符串的快速方法:
public class DefaultStringModule extends SimpleModule {
private static final String NAME = "DefaultStringModule";
private static final String DEFAULT_VALUE = "[DEFAULT]";
public DefaultStringModule() {
super(NAME, ModuleVersion.instance.version());
addDeserializer(String.class, new DefaultStringDeserializer());
}
private static class DefaultStringDeserializer extends StdScalarDeserializer<String> {
public DefaultStringDeserializer() {
super(String.class);
}
public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
String deserialized = jsonParser.getValueAsString();
// Use a default value instead of null
return deserialized == null ? DEFAULT_VALUE : deserialized;
}
@Override
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
return deserialize(jp, ctxt);
}
}
}
为了处理JSON中不存在的字段的默认值,我通常通过使用构建器类来实现这一点,该类将使用提供的值构造类,并为缺少的字段添加任何默认值。然后,在反序列化类(例如,MyClass
)上,添加一个@JsonDeserialize(builder=MyClass.builder.class)
注释,以指示Jackson通过builder类反序列化MyClass
。专门针对java.lang。可选的,Jackson自己有一个模块:
番石榴可选包含在
它将为null创建一个可选的.emission,但不为缺少的JSON值创建:-(
见和
所以你必须像初始化集合一样初始化你的选项。这是一个很好的实践,所以你应该能够执行它
private Optional<Xxx> xxx = Optional.absent();
private List<Yyy> yyys = Lists.newArrayList();
private Optional xxx=Optional.缺席();
私有列表yyys=Lists.newArrayList();
您的值对象应该将这些值初始化为不存在。这是确保默认值没有空值的方法。对于更高版本,Guava模块的可选处理程序实际上应该只将它们反序列化为“不存在”(即使是显式JSON空值),而不将它们反序列化为空值。
但由于Jackson只对存在的JSON属性(而不是可能存在但不存在的东西)进行操作,POJO仍然需要有默认的缺席赋值。问题在于反序列化()如果属性不在JSON中,则不调用
。我希望这些属性映射到
可选。也不存在
。很抱歉,错过了这一部分。我在回答的末尾添加了一段,以说明我这样做的一种方法。更好的方法是,但是我仍然必须对每个具有可选属性的类进行注释。我希望它是透明的。请参阅@greyfairer关于如何更好地支持可选数据的反序列化/序列化的回答。不幸的是,jackson数据类型guava与您的第一个示例有相同的问题-它只将显式null
映射到缺失的。缺失的值保留为null
。困惑吗?:)不,我使用的是后一个,它将不存在null值。我不认为这对本地人来说是不一样的,但我可以检查一下。是的,同样的问题。不存在缺省值的骰子:(实际上,番石榴<代码>可选< /COD>反序列化器确实应该将JSON <代码> null <代码>作为“缺省”,使用最新版本。至少使用2.5.3。但是序列化是另一回事,缺省值被认为是“空当量”。因此,根据默认包含,它们可能会被剥离。但是JSON中的属性没有任何值(Jackson没有做任何事情)和显式JSON null之间是有区别的。@LászlóvandenHoek不幸的是,Jackson没有办法做到这一点。啊。实际上,我正要提到这一点,可能是通过ValueInstantiator
…-)--是的,这是最有可能到达那里的路线。如果可能,最好通过邮件列表进行讨论。但是,是的,应该可以只处理实例化,让实际的反序列化继续进行。您有没有找到将可选值保留为空而不是空的解决方案。@Araf现在可能会处理它?我还没有检查,请检查并在这里报告:)
private Optional<Xxx> xxx = Optional.absent();
private List<Yyy> yyys = Lists.newArrayList();