How to deserealize multiple nested elements in Jackson?

偶尔善良 提交于 2019-12-24 07:39:32

问题


I need to build a parser to parse an XML file to a Java object. I use Jackson to do this and followed the steps provided in THIS tutorial.

In the tutorial is a section 'Manipulating Nested Elements and Lists in XML'. I followed it, but unfortunately I can't get the desired output of all my required elements - I want to output first and last of all my authors. And I only get it for my last author in the XML-file like this:

[{nameList={person={first=Karl, last=S}}}]

My XML file looks like this.

<sources>
<Doi>123456789</Doi>
<Title>Title</Title>
<author>
    <editor>
        <nameList>
            <person>
                <first>Peter</first>
                <last>Parker</last>
            </person>
        </nameList>
    </editor>
</author>
<Source>
    <SourceType>Book</SourceType>
    <ShortTitle>Book Title</ShortTitle>
    <Author>
        <Editor>
            <NameList />
        </Editor>
    </Author>
</Source>
<author>
    <bookAuthor>
        <nameList>
            <person>
                <first>Karl</first>
                <last>S</last>
            </person>
        </nameList>
    </bookAuthor>
</author>
<Source>
    <SourceType>Journal</SourceType>
    <ShortTitle>ABC Journal</ShortTitle>
</Source>
</sources>

How can I deserealize the entire XML file?

My code looks like this: MyClass.java

private static void jacksonXmlFileToObject() throws IOException {

    System.out.println("jacksonXmlFileToObject");

    InputStream xmlFile = Publication.class.getClassLoader().getResourceAsStream("test.xml");
    ObjectMapper mapper = new XmlMapper();

    // Configure
    mapper
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    try {

        Sources deserializedData = mapper.readValue(xmlFile, Sources.class);

        System.out.println(deserializedData);

    } catch (IOException e) {
        e.printStackTrace();
    }
}

Sources.java

@EqualsAndHashCode
@JacksonXmlRootElement(localName = "sources") public class Sources {
@JacksonXmlElementWrapper(localName = "author")
@Getter
@Setter
private Object[] author;

@Override
public String toString() {
    return Arrays.toString(author);
}

public Sources() {
}
}

I would be very happy about some help.

Thank you!


回答1:


It looks like JacksonXmlElementWrapper does not work when the same elements are not following each other. Regular XML should contain the same nodes listed one after another. When other node starts it's mean previous node section is finished. To handle your case we need to write custom deserialiser: manually read all authors and skip the rest of nodes. Example code could look like this:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class XmlMapperApp {

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

        XmlMapper mapper = new XmlMapper();

        System.out.println(mapper.readValue(xmlFile, Sources.class));
    }
}

class SourcesJsonDeserializer extends JsonDeserializer<Sources> {

    private final JsonPointer EDITOR = JsonPointer.compile("/editor/nameList/person");
    private final JsonPointer BOOK_AUTHOR = JsonPointer.compile("/bookAuthor/nameList/person");

    @Override
    public Sources deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        List<JsonNode> authors = new ArrayList<>();
        JsonToken token;
        while ((token = p.currentToken()) != null) {
            if (token == JsonToken.FIELD_NAME) {
                if ("author".equals(p.getText())) {
                    authors.add(getPersonObject(p));
                }
            }
            p.nextToken();
        }

        Sources sources = new Sources();
        sources.setAuthors(authors);

        return sources;
    }

    private JsonNode getPersonObject(JsonParser p) throws IOException {
        // read start object
        p.nextToken();

        // read the whole object as node
        ObjectNode author = p.readValueAsTree();

        // try to evaluate /editor/* path
        JsonNode pair = author.at(EDITOR);
        if (pair.isMissingNode()) {
            // must be bookAuthor
            pair = author.at(BOOK_AUTHOR);
        }

        return pair;
    }
}

@JsonDeserialize(using = SourcesJsonDeserializer.class)
class Sources {

    private List<JsonNode> authors;

    public List<JsonNode> getAuthors() {
        return authors;
    }

    public void setAuthors(List<JsonNode> authors) {
        this.authors = authors;
    }

    @Override
    public String toString() {
        return authors + "";
    }
}

Above code prints:

[{"first":"Peter","last":"Parker"}, {"first":"Karl","last":"S"}]


来源:https://stackoverflow.com/questions/57251726/how-to-deserealize-multiple-nested-elements-in-jackson

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!