杰克逊 JsonUnwrapped 属性碰撞

Jackson JsonUnwrapped properties collision

我有两个 类 具有相似的结构:

class Outer {
    String id;
    String someOuterProp;

    @JsonUnwrapped
    @JsonInclude(JsonInclude.Include.NON_NULL)
    Inner inner;
}

class Inner {
    String id;
    String someOuterProp;
}

Inner 对象可以在某些情况下独立创建,而在其他情况下可以嵌套在 Outer 中。可以看出两者都有一个具有相同 (id) 名称的字段。

Outer 对象被序列化时 - 如果 inner 不为空 - Inner 属性应该被解包,因此 @JsonUnwrapped 用法。

序列化时,我希望保留 Outer id 值,但是此断言失败:

Outer o = new Outer();
o.setId("outerId");
Inner i = new Inner();
i.setId("innerId");
o.setInner(i);
    
Map<?,?> map = objectMapper.convertValue(o, Map.class);
assertEquals("outerId", map.get("id"));

所以,我的问题是,如何在 属性 名称冲突时展平嵌套对象属性,同时保留包含的对象值?

这里有两件事你可以做。

解决方案 1:订购您的属性
只需指定序列化属性的顺序即可强制Outer.id最后写入。在反序列化过程中最后读取该字段时,它将覆盖前面的值(假设您正在反序列化为地图):

@JsonPropertyOrder({ "inner", "id", "someOuterProp" })
class Outer {

    String id;
    String someOuterProp;

    @JsonUnwrapped
    @JsonInclude(JsonInclude.Include.NON_NULL)
    Inner inner;
}

@JsonPropertyOrder({ "inner", "id"}) 强制首先写入从 inner 扁平化的属性。

解决方案 2:使用不同的 JSON 属性 名称
只需为 Inner 字段添加前缀名称。这将使它使用唯一的字段名称写入值:

class Outer {
    String id;
    String someOuterProp;

    @JsonUnwrapped(prefix = "inner.")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    Inner inner;
}

当你从这个class序列化一个对象时,你会得到类似

的东西
{
  "id" : "a",
  "someOuterProp" : "some",
  "inner.id" : "A",
  "inner.someOuterProp" : "SOME"
}

并将其反序列化为 Outer 对象会将正确的值放在它们所属的位置。这同样适用于您调用 convertValue:

生成的地图
{id=a, someOuterProp=some, inner.id=A, inner.someOuterProp=SOME}