【问题标题】:Jackson XML deserialization skips field when using multiple useWrapping = false使用多个 useWrapping = false 时,Jackson XML 反序列化会跳过字段
【发布时间】:2020-08-02 19:39:01
【问题描述】:

我正在尝试反序列化以下 XML:

<root>
    <foo name="AAA" />
    <bar name="BBB" />
    <foo name="CCC" />
</root>

我的杰克逊课程是:

@Data
public class Foo {
    @JacksonXmlProperty(isAttribute = true)
    private String name;
}

Bar 是相同的,只是类名不同。 (在实际代码中它们是不同的,这只是一个例子)。

而根类是

@Data
public class Root {
    @JacksonXmlProperty(localName = "foo")
    @JacksonXmlElementWrapper(useWrapping = false)
    private List<Foo> foos;
    @JacksonXmlProperty(localName = "bar")
    @JacksonXmlElementWrapper(useWrapping = false)
    private List<Bar> bars;
}

当我尝试反序列化 XML 时,使用此代码

System.out.println(new XmlMapper().readValue(theXml, Root.class));

结果是这样的(注意缺少“AAA”):

Root(foos=[Foo(name=CCC)], bars=[Bar(name=BBB)])

但是,如果我移动 XML 中的字段,使两个 foo 标记彼此相邻,它会打印

Root(foos=[Foo(name=AAA), Foo(name=CCC)], bars=[Bar(name=BBB)])

我正在使用jackson-dataformat-xml 2.11.1,这是最新的。

这是怎么回事,我该如何解决?

【问题讨论】:

    标签: java jackson jackson-dataformat-xml


    【解决方案1】:

    对于任何属性,您都可以使用 Jackson 注释(JsonSetterJsonGetter)指定一个方法用作 setter 或 getter。如果您只需要对杰克逊所做的事情进行一些修改,那么为整个班级编写自定义 deserializer / serializer 似乎更容易。 Jackson 也有一个 JsonAnySetter 注释,它是一个后备,可用于你的类中未指定的东西(我发现它有时很方便;我用它来将一种元素的所有 XML 属性放入单个映射,而不必为每个可能的属性都有属性)。

    您可以将自定义 XML 反序列化方法添加到您的 Root 类。像这样的:

    @JsonSetter(value =  "foo")
    public void setFooFromXml(Foo foo) {
        if (this.foos == null) {
            this.foos = new ArrayList<Foo>();
        } 
        this.foos.add(foo);
    }
    
    @JsonSetter(value =  "bar")
    public void setBarFromXml(Bar bar) {
        if (this.bars == null) {
            this.bars = new ArrayList<Bar>();
        } 
        this.bars.add(bar);
    }
    

    像这样使用 Jackson 反序列化 XML:

    try {
        String input = "<root><foo name=\"AAA\" /><bar name=\"BBB\" /><foo name=\"CCC\" /></root>";
        XmlMapper mapper = new XmlMapper();
        Root root = mapper.readValue(input, Root.class);
        System.out.println(root.getBars());
        System.out.println(root.getFoos());
        
    } catch (Exception e) {
        e.printStackTrace();
    }
    

    给出这个的输出(在添加一些简单的 toString() 和 getter 方法之后):

    [Bar [name=BBB]]
    [Foo [name=AAA], Foo [name=CCC]]
    

    【讨论】:

      【解决方案2】:

      我必须承认我通常只使用Jackson来处理JSON,但它似乎是jackson-dataformat-xml库的known limitation

      在 2.12 之前(截至 2020 年 5 月尚未发布),重复 XML 元素的处理存在问题(它只能保留最后读取的元素),但 #403 改进了处理

      也许这个问题可能与您的问题有关。

      作为一种解决方法,如果可能,您可以在处理 XML 文档之前对其应用某种 XSLT 转换,以便将具有相同名称的所有节点组合在一起,并查看结果是否符合预期。

      您还可以尝试其他反序列化替代方案,主要是 JAXB 或直接 XML 处理。

      【讨论】:

      • 非常感谢@Michael。我很高兴这个答案很有帮助。
      猜你喜欢
      • 2016-02-13
      • 1970-01-01
      • 2019-10-21
      • 2016-02-12
      • 1970-01-01
      • 2018-07-21
      • 2017-09-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多