问题
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