Currently I'am facing problem when trying to parse a xml document to a Java Object. The only necessary information which i want to keep is the iid and time list.
xml test file:
14
20200714100100
20200714101500
2
20200714101600
20200714103000
2
20200714103100
20200714104500
2
20200714104600
20200714110000
2
I have created two Java Objects classes which contains the iid and the time list. When parsing the xml file only the field iid gets filled and the list object is null. What do I missing ?
@JsonIgnoreProperties(ignoreUnknown = true)
@XmlRootElement(name = "item")
@JacksonXmlRootElement(localName = "item")
public class SubProduct implements Serializable {
private String iid;
@JacksonXmlElementWrapper(localName = "times")
@JacksonXmlProperty(localName = "time")
private List times;
}
@JsonIgnoreProperties(ignoreUnknown = true)
@JacksonXmlRootElement(localName = "time")
public class TimePeriod implements Serializable {
@JsonProperty(value = "timeentry")
String timeEntry;
@JsonProperty(value = "timetill")
String timeTill;
@JsonProperty(value = "timemaxcount")
String timeMaxCount;
}
service layer:
...
NodeList itemList = document.getElementsByTagName("item");
List subProducts = new ArrayList<>();
for (int i = 0; i < nodes.getLength(); i++) {
SubProduct value = xmlMapper.readValue(nodeToString(nodes.item(i)), SubProduct.class);
subProducts.add(value);
}
return subProducts;
...
public static String nodeToString(Node node) throws Exception{
StringWriter sw = new StringWriter();
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.transform(new DOMSource(node), new StreamResult(sw));
return sw.toString();
}
reponse:
{
"iid": "9",
"times": null
},
解决方案
You do not need to mix Jackson with JAXB or Transformer class. You can directly deserialise given XML payload to POJO model. items, options and times nodes represent List of nodes. We can map the to below model:
@Data
@ToString
class Item {
private int iid;
private List options;
}
@Data
@ToString
class Option {
private List times;
}
@Data
@ToString
class Time {
@JsonFormat(pattern = "yyyyMMddHHmmss")
@JsonProperty("timeentry")
private LocalDateTime entry;
@JsonFormat(pattern = "yyyyMMddHHmmss")
@JsonProperty("timetill")
private LocalDateTime till;
@JsonProperty("timemaxcount")
private int maxCount;
}
I used Lombok annotations to avoid boilerplate code. Simple usage:
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.Data;
import lombok.ToString;
import java.io.File;
import java.time.LocalDateTime;
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 = XmlMapper.xmlBuilder()
.addModule(new JavaTimeModule())
.build();
List items = mapper.readValue(xmlFile, new TypeReference>() {
});
items.forEach(item -> {
System.out.println("Id => " + item.getIid());
System.out.println("Times => ");
item.getOptions().stream().flatMap(o -> o.getTimes().stream())
.forEach(System.out::println);
});
}
}
Above code prints:
Id => 14
Times =>
Time(entry=2020-07-14T10:01, till=2020-07-14T10:15, maxCount=2)
Time(entry=2020-07-14T10:16, till=2020-07-14T10:30, maxCount=2)
Time(entry=2020-07-14T10:31, till=2020-07-14T10:45, maxCount=2)
Time(entry=2020-07-14T10:46, till=2020-07-14T11:00, maxCount=2)
See also: