Десериализовать плоский массив в XML Джексона в список Pojo - PullRequest
0 голосов
/ 25 февраля 2019

Можете ли вы помочь мне разобрать следующий XML-файл?

<?xml version="1.0" encoding="UTF-8"?>
<dataset xmlns="http:/foo.com">
   <date>2017-10-25T09:13:54+02:00</date>
   <element>
      <id>1</id>
      <name>Stuart</name>
      <age>34</age>
      <regdate><date>2017-10-25T09:13:54+02:00</date></regdate>
   </element>
   <element>
      <id>2</id>
      <name>Lora</name>
      <age>12</age>
      <regdate><date>2017-10-25T09:13:54+02:00</date></regdate>
   </element>
   <element>
      <id>3</id>
      <name>Ben</name>
      <age>50</age>
      <regdate><date>2017-10-25T09:13:54+02:00</date></regdate>
   </element>
</dataset >

Я попытался создать pojo следующим образом:

@Getter
@Setter
@JacksonXmlRootElement(localName = "element")
public class ElementXML {
    @JacksonXmlProperty(localName = "id")
    private Long id;    
    @JacksonXmlProperty(localName = "name")
    private String name;
    @JacksonXmlProperty(localName = "age")
    private Long age;
    @JacksonXmlProperty(localName = "regdate")
    private LocalDateTime regdate;
}

и механизм парсинга, который я использовал здесь:

    XMLInputFactory f = XMLInputFactory.newFactory();
    File inputFile = new File("some path");
    XMLStreamReader sr = f.createXMLStreamReader(new FileInputStream(inputFile));


    ObjectMapper xmlMapper = new XmlMapper();

Там я застрял, потому что не знаю, как разобрать только теги элементов в Список созданного мной pojo ElementXML.У вас есть идеи, как это решить?Спасибо за совет.

Редактировать трассировку после отредактированного анализа ответов

com.fasterxml.jackson.databind.JsonMappingException: Expected END_ELEMENT, got event of type 1 (through reference chain: com.xml.Dataset["element"]->java.lang.Object[][1])

    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:394)
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:365)
    at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:206)
    at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:21)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
    at com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer.deserialize(WrapperHandlingDeserializer.java:113)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2902)
    at com.xml.data.ParseXmlTest.test(ParseXmlTest.java:62)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.CommandLineWrapper.main(CommandLineWrapper.java:67)
Caused by: java.io.IOException: Expected END_ELEMENT, got event of type 1
    at com.fasterxml.jackson.dataformat.xml.deser.XmlTokenStream.skipEndElement(XmlTokenStream.java:190)
    at com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser.nextToken(FromXmlParser.java:584)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:283)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
    at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:195)
    ... 43 more

Ответы [ 2 ]

0 голосов
/ 26 февраля 2019

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

1.Модель не подходит для XML полезной нагрузки.

Прежде всего, вам необходимо создать модель, которая соответствует вашему payload.Это не зависит от формата, потому что для JSON и XML это будет почти одинаково.Для этого я предлагаю всегда начинать с процесса сериализации.Намного проще построить модель в Java и попытаться ее сериализовать.В случае, если он не выглядит так, как ожидалось, вам нужно обновить модель.Вы повторяете эти шаги: update и serialise, пока не найдете действительную модель.После этого вы можете без проблем десериализовать данный payload.

2.Аннотации Джексона.

Несмотря на то, что аннотации Jackson велики, не используйте их без причины.Если свойство POJO совпадает с именем узла в XML, вам не нужно добавлять аннотацию JacksonXmlProperty.Вы должны добавить его, когда имена в POJO и payload различны.В других случаях это усложняет структуру POJO.Мы должны сделать это как можно проще.Вам нужно использовать одну хитрую аннотацию: JacksonXmlElementWrapper.Он используется, когда у нас есть коллекции узлов, но они развернуты.

После этих двух простых абзацев, чтобы не десериализовать ваш случай.Нам нужно расширить вашу POJO структуру, и она должна выглядеть следующим образом:

class Dataset {

    private LocalDateTime date;

    @JacksonXmlProperty(localName = "element")
    @JacksonXmlElementWrapper(useWrapping = false)
    private List<Element> elements;

    public LocalDateTime getDate() {
        return date;
    }

    public void setDate(LocalDateTime date) {
        this.date = date;
    }

    public List<Element> getElements() {
        return elements;
    }

    public void setElements(List<Element> element) {
        this.elements = element;
    }

    @Override
    public String toString() {
        return "Dataset{" +
                "date=" + date +
                ", element=" + elements +
                '}';
    }
}

class Element {

    private Long id;
    private String name;
    private Long age;
    private RegDate regdate;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getAge() {
        return age;
    }

    public void setAge(Long age) {
        this.age = age;
    }

    public RegDate getRegdate() {
        return regdate;
    }

    public void setRegdate(RegDate regdate) {
        this.regdate = regdate;
    }

    @Override
    public String toString() {
        return "ElementXML{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", regdate=" + regdate.getDate() +
                '}';
    }
}

class RegDate {

    private LocalDateTime date;

    public RegDate() {
        this(null);
    }

    public RegDate(LocalDateTime date) {
        this.date = date;
    }

    public LocalDateTime getDate() {
        return date;
    }

    public void setDate(LocalDateTime date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "RegDate{" +
                "date=" + date +
                '}';
    }
}

И пример использования:

import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;

import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

public class XmlMapperApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.xml").getAbsoluteFile();

        JavaTimeModule module = new JavaTimeModule();
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ISO_DATE_TIME));

        XmlMapper xmlMapper = new XmlMapper();
        xmlMapper.registerModule(module);
        xmlMapper.enable(SerializationFeature.INDENT_OUTPUT);

        Dataset dataset = xmlMapper.readValue(jsonFile, Dataset.class);
        dataset.getElements().forEach(System.out::println);
    }
}

Над отпечатками кода:

ElementXML{id=1, name='Stuart', age=34, regdate=2017-10-25T09:13:54}
ElementXML{id=2, name='Lora', age=12, regdate=2017-10-25T09:13:54}
ElementXML{id=3, name='Ben', age=50, regdate=2017-10-25T09:13:54}

Два дополнительных комментария к приведенному выше коду.Когда вы работаете с java.time.* классами и Jackson, лучше начать с регистрации JavaTimeModule, которая поступает из модуля jackson-datatype-jsr310 .Поскольку мы используем его, мы можем указать ему использовать ISO_DATE_TIME форматирование для LocalDateTime классов.В других ответах вы можете найти пример, где используется JsonFormat аннотация.Это также хорошее решение, но когда все даты имеют одинаковый формат, гораздо проще определить его.

Для получения дополнительной информации читайте:

0 голосов
/ 26 февраля 2019

Невозможно смоделировать данный контент XML только одним классом POJO.Вам потребуется несколько классов POJO для правильного моделирования содержимого XML.

Для моделирования корневого элемента <dataset> вам нужен класс, назовем его Dataset.

@Getter
@Setter
@JacksonXmlRootElement(localName = "dataset")
public class Dataset {

    @JacksonXmlProperty(localName = "date")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
    private LocalDateTime date;

    @JacksonXmlProperty(localName = "element")
    @JacksonXmlElementWrapper(useWrapping = false)
    private List<ElementXML> elements;
}

Уведомление вкод выше

  • Свойство date помечено @JsonFormat для указания формата даты и времени, который будет использоваться
  • elementsс пометкой @JacksonXmlElementWrapper(useWrapping = false), чтобы сказать парсеру не ожидать дополнительной оболочки вокруг последовательности <element>

Далее, вам нужен класс для моделирования содержимого XML между <element>...</element>, очень похоже на ваш ElementXML класс.

@Getter
@Setter
public class ElementXML {

    @JacksonXmlProperty(localName = "id")
    private Long id;

    @JacksonXmlProperty(localName = "name")
    private String name;

    @JacksonXmlProperty(localName = "age")
    private Long age;

    @JacksonXmlProperty(localName = "regdate")
    private RegDate regdate;
}

Обратите внимание на приведенный выше код, что вам нужно смоделировать свойство regdate более изощренным.Поскольку содержимое XML не выглядит как <regdate>2017-10-25T09:13:54+02:00</regdate>, вы не можете просто объявить его как LocalDate regdate.Вместо этого XML выглядит как <regdate><date>2017-10-25T09:13:54+02:00</date></regdate>.Поэтому вам нужно смоделировать его с помощью еще одного класса (назовем его RegDate)

И, наконец, вот класс RegDate для моделирования содержимого XML между <regdate>...</regdate>.

@Getter
@Setter
public class RegDate {

    @JacksonXmlProperty(localName = "date")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
    private LocalDateTime date;
}

Еще раз обратите внимание на использование @JsonFormat для указания формата даты и времени.

Используя приведенные выше классы, вы можете анализировать XML следующим образом

    File inputFile = new File("some path");
    ObjectMapper xmlMapper = new XmlMapper();
    xmlMapper.registerModule(new JavaTimeModule());
    Dataset dataset = xmlMapper.readValue(inputFile, Dataset.class);
...