Десериализация XML Джексона пропускает поле при использовании нескольких useWrapping = false

Я пытаюсь десериализовать следующий XML:

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

Мои классы Джексона:

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

Бар идентичен, только другое имя класса. (В реальном коде они разные, это всего лишь пример).

И корневой класс

@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 самая последняя.

Что здесь происходит, и как это исправить?


person Michael    schedule 02.08.2020    source источник


Ответы (2)


Для любого свойства вы можете указать метод, который будет использоваться в качестве установщика или получателя, используя аннотации Джексона (JsonSetter и JsonGetter). Когда вам просто нужно немного изменить то, что делает Джексон, это кажется проще, чем писать собственный deserializer / serializer для всего класса. У Джексона также есть ссылка JsonAnySetter, которая является резервным вариантом для использования чего-то, не указанного в вашем классе (я обнаружил, что это иногда бывает удобно; я использовал это, чтобы поместить все атрибуты XML типа элемент в одну карту вместо того, чтобы иметь свойства для каждого возможного атрибута).

Вы можете добавить собственные методы десериализации XML в свой корневой класс. Что-то вроде этого:

@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);
}

Использование Джексона для десериализации 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]]
person jnorman    schedule 10.08.2020

Я должен признать, что обычно я использую только Jackson для обработки JSON, но кажется, что это известное ограничение библиотеки jackson-dataformat-xml:

До версии 2.12 (еще не выпущенной по состоянию на май 2020 г.) обработка повторяющихся элементов XML была проблематичной (она могла сохранять только последний прочитанный элемент), но # 403 улучшает обработку

Возможно, эта проблема может быть связана с вашей проблемой.

В качестве обходного пути, если возможно, вы можете применить какое-то преобразование XSLT к XML-документам перед их обработкой, чтобы все узлы с одинаковыми именами были сгруппированы вместе, и посмотреть, соответствует ли результат ожидаемому.

Вы также можете попробовать другие альтернативы десериализации, в основном JAXB или прямую обработку XML.

person jccampanero    schedule 06.08.2020
comment
Большое спасибо @Michael. Я очень рад, что ответ оказался полезным. - person jccampanero; 10.08.2020