Jackson XML десериализация пропускает поле при использовании нескольких useWrapping = false - PullRequest
8 голосов
/ 02 августа 2020

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

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

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

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

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

А класс root равен

@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, используя это code

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, который является последним.

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

Ответы [ 2 ]

5 голосов
/ 10 августа 2020

Для любого свойства вы можете указать метод, который будет использоваться в качестве установщика или получателя, используя аннотации Джексона ( JsonSetter и JsonGetter ). Когда вам просто нужно немного изменить то, что делает Джексон, это кажется проще, чем писать собственный deserializer / serializer для всего класса. У Джексона также есть аннотация 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);
}

Использование Джексона для десериализации 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 голосов
/ 07 августа 2020

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...