【问题标题】:How to not serialize values which are initialized in a POJO?如何不序列化在 POJO 中初始化的值?
【发布时间】:2021-11-27 14:10:12
【问题描述】:

如果我有 POJO

class Night {

    int stars = 10;
    // getter and setter for stars
}

我用 Jackson 序列化它,它将序列化 stars 字段。但是因为字段是在创建对象时设置的,所以不需要对字段进行序列化,除非值与10不同。

我最初认为@JsonInclude(value = Include.NON_DEFAULT) 会这样做,但后来意识到“默认”是关于类型的,所以 0 表示 int,而不是关于 POJO。

我不想序列化很多在反序列化时不需要设置的值,因为无论如何都会发生这种情况。我只想序列化改变的值。除了创建自定义序列化程序之外,还有其他方法吗?

我正在使用 Jackson 2.13,我认为它是最新的。

【问题讨论】:

  • 我对您对您要达到的目标的解释感到有些困惑,并且想知道您是否在您的意思是“反序列化”的地方使用了“序列化”这个词。您能否提供清晰的场景来准确显示您正在尝试做什么?
  • 反序列化所有内容。您的反序列化数据不应是“反序列化值”和“仅在代码中已知的值”的组合;它应该是所有领域。您将无法区分默认值和缺失值(尽管您可以序列化非原始字段的空值以指示缺失)。此外,如果将来您的默认值发生更改,您的反序列化数据将不完整/不正确。我想不出有任何理由有条件地跳过反序列化;这样做会导致后悔。最后,我认为您正确使用了“序列化”和“反序列化”。
  • @Paul “如果您的默认值在未来发生变化,您的反序列化数据将不完整/不正确。”这是一个很好的观点,我也会考虑的。谢谢

标签: java jackson


【解决方案1】:

如果我正确理解您的问题,您希望 jackson 仅在不等于默认值 10 时写入 stars 字段,对吗?

根据documentation@JsonInclude(JsonInclude.Include.NON_DEFAULT) 在类级别指定时正是这样做的:

public static final JsonInclude.Include NON_DEFAULT

此设置的含义取决于上下文:是否为 POJO 类型(类)指定了注解。在后一种情况下,注释要么用作全局默认值,要么用作属性覆盖。

当用于 POJO 时,定义是只包含与 POJO 属性的默认值不同的值。这是通过使用零参数构造函数创建 POJO 实例并访问属性值来完成的:使用 equals() 方法将值用作默认值,但属性具有 null 值的情况除外,在这种情况下直接为 null使用检查。

当不用于 POJO(即,作为全局默认值,或作为属性覆盖)时,定义如下:

  • 排除所有被视为“空”的值(根据 NON_EMPTY)
  • 不包括原始/包装器默认值
  • 时间戳(long 自纪元以来的毫秒值,请参阅日期)为0L 的日期/时间值被排除在外

基本上,当序列化 POJO 时,它使用 NoArgsConstructor 构造一个默认对象,将字段与默认构造函数生成的字段进行比较,并且不包含与默认构造函数匹配的字段。

我自己尝试过,(在 Jackson 2.12.4 中),注释只包含与对象中设置的默认值不同的值。

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
public class TestClass { 
    private long id = 10;
    private long id2 = 10;
    private String name = "Fred";
    private String name2 = "Fred";
}

public class TestClass2 {
    public static void main(String[] args) {
        ObjectMapper mapper = new ObjectMapper();
        TestClass temp1 = new TestClass();
        temp1.setId(5);
        temp1.setName2("Jeff");
        String json = json.writeValueAsString(temp1);
        ...
    }
}

这将生成以下仅具有非默认值的 Json: {"id":5,"name2":"Jeff"} 当使用mapper.readValue(json, TestClass.class); 映射回一个对象时,这将生成一个具有以下字段的 TestClass 对象: id=5, id2=10, name="Fred", name2="Jeff"

重要提示:正如 Paul 在 cmets 中指出的那样,依赖像这样的序列化可能会有缺点。您实际上无法区分默认值 10 和完全缺失的字段,因为两者都映射到相同的输出。此外,您的其他微服务和应用程序也需要具有相同的默认值。这会产生代码可维护性问题,因为对一个微服务中默认值的任何更改也必须反映在其他微服务和任何其他下游应用程序中,没有好的方法来强制执行一致的默认值。

如果可以的话,我建议将您拥有的任何实际默认值映射到常规 json 默认值,例如 0 或 null。但是@JsonInclude(JsonInclude.Include.NON_DEFAULT) 也适用于其他默认值。

【讨论】:

  • 我只是复制粘贴并测试了您的代码,我得到的 JSON 是 {"id":5,"id2":10,"name":"Fred","name2":"Jeff"} ,这与您的不同。不知道为什么,也许 Jackson 2.12.4 与 2.13 不同?另外你有一个错字json.writeValueAsString 应该是mapper.writeValueAsString,但那是微不足道的。如果我在课堂上更改private long id2 = 0;,那么id2 不会被序列化,所以它会按照我在问题中的描述进行。
  • 这很奇怪。也许版本之间存在一些细微的差异,或者可能已经设置了一些环境变量并且忘记了它正在改变它的行为。我将不得不在不久的某个时候更仔细地研究它。抱歉,目前这对您不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-16
  • 2017-04-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多