Jackson XML反序列化在使用多个useWrapping = false时跳过字段。

huangapple go评论97阅读模式
英文:

Jackson XML deserialization skips field when using multiple useWrapping = false

问题

I'm here to help with the translation. Here's the translated code and content you provided:

我正试图反序列化以下XML
```xml
<root>
    <foo name="AAA" />
    <bar name="BBB" />
    <foo name="CCC" />
</root>

我的Jackson类如下:

@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,这是最新版本。

这里发生了什么,我应该如何修复它?


<details>
<summary>英文:</summary>

I am trying to deserialize the following XML:
```xml
&lt;root&gt;
    &lt;foo name=&quot;AAA&quot; /&gt;
    &lt;bar name=&quot;BBB&quot; /&gt;
    &lt;foo name=&quot;CCC&quot; /&gt;
&lt;/root&gt;

My Jackson classes are:

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

Bar is identical, just a different class name. (In the real code they are different, this is just an example).

And the root class is

@Data
public class Root {
    @JacksonXmlProperty(localName = &quot;foo&quot;)
    @JacksonXmlElementWrapper(useWrapping = false)
    private List&lt;Foo&gt; foos;
    @JacksonXmlProperty(localName = &quot;bar&quot;)
    @JacksonXmlElementWrapper(useWrapping = false)
    private List&lt;Bar&gt; bars;
}

When I try to deserialize the XML, using this code

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

The result is this (note the lack of "AAA"):

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

However, if I move the fields in the XML so that both foo tags are next to each other, it prints

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

I'm using jackson-dataformat-xml 2.11.1 which is the latest.

What is going on here, and how can I fix it?

答案1

得分: 9

关于任何属性,您可以使用Jackson注解(JsonSetterJsonGetter)指定要用作setter或getter的方法。当您只需要对Jackson正在执行的操作进行一点修改时,这似乎比为整个类编写自定义deserializer / serializer更容易。Jackson还有一个JsonAnySetter注解,用于处理在您的类中未指定的内容(我有时发现这很方便;我曾经用它将某种类型元素的所有XML属性放入一个单独的Map中,而不必为每个可能的属性都添加属性)。

您可以将自定义的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]]
英文:

For any property, you can specify a method to use as the setter or getter using Jackson annotations (JsonSetter and JsonGetter). When you just a need a little modification to what Jackson is doing, then this seems easier that writing a custom deserializer / serializer for the whole class. Jackson also has a JsonAnySetter annotation that is a fallback to use for something not specified in your class (I've found that to be handy sometimes; I've used that to put all XML attributes of a type of element into a single Map rather than having to have properties for every possible attribute).

You could add custom XML deserialization methods to your Root class. Something like this:

@JsonSetter(value =  &quot;foo&quot;)
public void setFooFromXml(Foo foo) {
	if (this.foos == null) {
		this.foos = new ArrayList&lt;Foo&gt;();
	} 
	this.foos.add(foo);
}

@JsonSetter(value =  &quot;bar&quot;)
public void setBarFromXml(Bar bar) {
	if (this.bars == null) {
		this.bars = new ArrayList&lt;Bar&gt;();
	} 
	this.bars.add(bar);
}

Using Jackson to deserialize the XML like this:

try {
	String input = &quot;&lt;root&gt;&lt;foo name=\&quot;AAA\&quot; /&gt;&lt;bar name=\&quot;BBB\&quot; /&gt;&lt;foo name=\&quot;CCC\&quot; /&gt;&lt;/root&gt;&quot;;
	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();
}

Gives an output of this (after adding some simple toString() and getter methods):

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

答案2

得分: 2

我必须承认,我通常只使用Jackson来处理JSON,但似乎jackson-dataformat-xml库存在一个已知限制

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

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

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

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

英文:

I must recognize that I usually only use Jackson for JSON handling, but it seems that it is a known limitation of the jackson-dataformat-xml library:

> Prior to 2.12 (not yet released as of May 2020), handling of repeated XML elements was problematic (it could only retain the last element read), but #403 improves handling

Maybe this issue could be related with your problem.

As a workaround, if possible, you can apply some kind of XSLT transformation to the XML documents before processing them, so that all nodes with the same name are grouped together, and see if the result is as expected.

You can also try another deserialization alternatives, mainly, JAXB, or direct XML processing.

答案3

得分: 0

我已经找到了一种解决方案,可以使用Jackson将重复的非连续XML字段分组到一个列表中,但它也使用了Lombok。

  1. 创建一个带有'@Builder'注解的Lombok类:https://projectlombok.org/features/Builder
  2. 在类上添加'@Jacksonized'注解,这会使Jackson使用该生成器进行反序列化:https://projectlombok.org/features/experimental/Jacksonized
  3. 在列表字段上添加'@Singular'注解,这会使Lombok创建一个方法来添加单个元素:https://projectlombok.org/features/Builder#singular
  4. 在@Singular注解中放入字段名称,例如'@Singular("item")',这会使带有该字段名称的方法接收一个元素。

这些步骤使得Jackson在到达每个字段时将其传递给Lombok生成器,生成器具有一个累积列表的单一元素生成方法。交错排列是可以的。

英文:

I've found a solution for grouping repeated, non-consecutive XML fields into one list using Jackson, but it also uses Lombok.

  1. Create a Lombok for the class with '@Builder': https://projectlombok.org/features/Builder
  2. Add the '@Jacksonized' annotation to the class, which makes Jackson use that builder to deserialize: https://projectlombok.org/features/experimental/Jacksonized
  3. Add the '@Singular' annotation to the List field, which makes Lombok create a method for adding a single element instead: https://projectlombok.org/features/Builder#singular
  4. Put the field name in the @Singular annotation, e.g. '@Singular("item")', which makes that method that takes one element have the field name

Together, these make Jackson pass each field as it is reached to the Lombok Builder, which has a single-item builder method that accumulates a list. Interleaving is fine.

huangapple
  • 本文由 发表于 2020年8月3日 03:39:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/63220292.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定