【问题标题】:Implicit default values when deserializing JSON using Jackson使用 Jackson 反序列化 JSON 时的隐式默认值
【发布时间】:2015-04-28 20:03:59
【问题描述】:

在反序列化各种 JSON 消息时,我想为某种类型的属性提供一个默认值。 generally suggested 可以简单地指定 Class 中的值,但如果您必须跨多个 Class 执行此操作,则很容易出错。您可能会忘记一个并以null 而不是默认值结束。我的意图是将Optional<T> 的每个属性设置为Optional.absent。由于null 正是Optional 试图消除的东西,因此与杰克逊一起使用它们已被证明是令人沮丧的。

Jackson 允许您自定义反序列化过程的大多数功能都集中在作为输入的 JSON 上,而不是围绕实例化您要反序列化的对象的过程。我似乎最接近通用解决方案的是构建自己的ValueInstantiator,但我还有两个问题:

  • 如何让它只将Optional 实例化为absent 而不会干扰其余的实例化过程?
  • 如何将最终结果连接到我的ObjectMapper

更新:我想澄清一下,我正在寻找的解决方案涉及修改包含Optional'的每个类s。我反对违反 DRY 原则。每次我们将Optional's 添加到新的或现有的类时,我或我的同事都不必考虑必须做一些额外的事情。我想说,“让我反序列化的每个类中的每个可选字段,预填充Absent”,只需要一次,就可以完成。

这意味着以下内容:

  • 抽象父类(需要声明)
  • 自定义 Builder/Creator/JsonDeserializer(需要对每个适用的类进行注释)
  • MixIn 的?我试过这个,结合反射,但我不知道如何访问我正在混入的类......

【问题讨论】:

标签: java json jackson deserialization


【解决方案1】:

专门针对 java.lang.Optional,Jackson 人自己有一个模块:https://github.com/FasterXML/jackson-datatype-jdk8

Guava Optional 被https://github.com/FasterXML/jackson-datatype-guava覆盖

它将为 null 创建一个 Optional.absent,但不会为缺少的 JSON 值创建一个:-(.

参见https://github.com/FasterXML/jackson-databind/issues/618https://github.com/FasterXML/jackson-datatype-jdk8/issues/2

所以你被困在初始化你的 Optionals 就像你应该初始化集合一样。这是一个很好的做法,因此您应该能够执行它。

private Optional<Xxx> xxx = Optional.absent();
private List<Yyy> yyys = Lists.newArrayList();

【讨论】:

  • 不,我使用的是后者,它使缺失值为空。我认为本地人不会有其他情况,但我可以检查一下。
  • 是的,同样的问题。没有值没有骰子:(
  • 实际上 Guava Optional 反序列化器确实应该将 JSON null 反序列化为“不存在”,并使用最新版本。至少使用 2.5.3。然而,序列化是另一回事。缺席值被认为是“空等效”,因此根据默认包含,它们可能会被剥离。但是在 JSON 中没有任何属性值(Jackson 没有做任何事情)和显式 JSON null 之间是有区别的。
  • @LászlóvandenHoek 不幸的是,杰克逊没有办法做到这一点。
  • 啊。实际上,我正要提到可能通过ValueInstantiator... :-) - 所以是的,那将是最有可能到达那里的途径。如果可能,最好通过邮件列表进行讨论。但是,是的,应该可以只处理实例化,让实际的反序列化继续进行。
【解决方案2】:

您可以编写自定义反序列化程序来处理默认值。实际上,您将为要反序列化的对象类型扩展适当的反序列化器,获取反序列化的值,如果是 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);
        }
    }
}

要将其与ObjectMapper 一起使用,您可以在实例上注册模块:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new DefaultStringModule());

为了处理 JSON 中不存在的字段的默认值,我通常看到这是通过使用构建器类来完成的,该构建器类将使用提供的值构造类并为缺失的字段添加任何默认值。然后,在反序列化的类(例如MyClass)上,添加@JsonDeserialize(builder = MyClass.Builder.class)注解,指示Jackson通过构建器类反序列化MyClass

【讨论】:

  • 这个问题是如果属性不在 JSON 中,则不会调用 deserialize()。我也希望将它们映射到Optional.Absent
  • 对不起,错过了那部分。我在答案的末尾添加了一个段落来建议我这样做的一种方法。
  • 更好,但是我仍然必须注释每个具有 Optional 属性的类。我希望它是透明的。
  • 查看@greyfairer 的回答,了解如何为 Optional 获得更好的反序列化支持。
  • 不幸的是,jackson-datatype-guava 与您的第一个示例存在相同的问题 - 它仅将显式 null 映射到 Absent。缺少的值保留为null。迷茫了吗? :)
【解决方案3】:

您的值对象应将这些值初始化为不存在。这是确保默认值没有空值的方法。 Guava 模块的 Optional 处理程序实际上应该只将它们反序列化为“不存在”(即使有明确的 JSON 空值),并且永远不要作为空值,在更高版本中。 但是由于 Jackson 只对存在的 JSON 属性进行操作(而不是对可能存在但不存在的事物),POJO 仍然需要默认缺席分配。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-06-01
    • 2014-10-06
    • 1970-01-01
    • 2021-09-07
    • 2018-07-15
    • 2017-01-31
    • 2022-10-15
    • 1970-01-01
    相关资源
    最近更新 更多