The following XML
is what JBPM
spits out for variables used in a process. In other words, it is machine generated. I have tried for several hours to parse this with Jackson
and has gone nowhere. Below you can find Java
classes I am using. I am attaching a typical serializer that I have used with various debugging in eclipse without luck.
XML:
<map-type> <entries> <entry> <key>document</key> <value xsi:type="jaxbMap" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <entries> <entry> <key>org.jbpm.document.service.impl.DocumentImpl</key> <value xsi:type="jaxbMap"> <entries> <entry> <key>identifier</key> <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"> fc3a87c9-d22c-449b-b772-756fcc9a385d </value> </entry> <entry> <key>size</key> <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">1186</value> </entry> <entry> <key>name</key> <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"> dmv-registration.txt </value> </entry> <entry> <key>link</key> <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"> fc3a87c9-d22c-449b-b772-756fcc9a385d </value> </entry> <entry> <key>attributes</key> </entry> <entry> <key>lastModified</key> <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">Mon Mar 09 18:19:25 PDT 2020 </value> </entry> <entry> <key>content</key> <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"></value> </entry> </entries> </value> </entry> <entry> <key>uploader_name</key> <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">murthy</value> </entry> <entry> <key>uploader_mail</key> <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">[emailprotected] </value> </entry> </entries> </value> </entry> <entry> <key>initiator</key> <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">kieserver </value> </entry> </entries></map-type>
Java
classes:
package com.murthy;import java.util.List;import com.fasterxml.jackson.annotation.JsonProperty;import com.fasterxml.jackson.databind.annotation.JsonDeserialize;public class ProcessVariableMapType { @JsonProperty("entries") @JsonDeserialize(using = CustomEntriesDeserializer.class) List<ProcessVariableEntries> entries; public List<ProcessVariableEntries> getEntries() { return entries; } public void setEntries(List<ProcessVariableEntries> entries) { this.entries = entries; }}package com.murthy;import java.util.List;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;import com.fasterxml.jackson.annotation.JsonProperty;import com.fasterxml.jackson.databind.annotation.JsonDeserialize;@JsonIgnoreProperties(ignoreUnknown = true)public class ProcessVariableEntries { @JsonProperty("entry") @JsonDeserialize(using = CustomEntryDeserializer.class) List<ProcessVariableEntry> processVariableEntries; public List<ProcessVariableEntry> getProcessVariableEntries() { return processVariableEntries; } public void setProcessVariableEntries(List<ProcessVariableEntry> processVariableEntries) { this.processVariableEntries = processVariableEntries; }}package com.murthy;import java.util.List;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;import com.fasterxml.jackson.annotation.JsonProperty;import com.fasterxml.jackson.databind.annotation.JsonDeserialize;@JsonIgnoreProperties(ignoreUnknown = true)public class ProcessVariableEntry { @JsonProperty("key") List<String> key; @JsonProperty("value") @JsonDeserialize(using = CustomListDeserializer.class) List<ProcessVariableStringValue> value; public List<String> getKey() { return key; } public void setKey(List<String> key) { this.key = key; } public List<ProcessVariableStringValue> getValue() { return value; } public void setValue(List<ProcessVariableStringValue> value) { this.value = value; }}package com.murthy;import java.util.List;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;import com.fasterxml.jackson.annotation.JsonProperty;import com.fasterxml.jackson.databind.annotation.JsonDeserialize;@JsonIgnoreProperties(ignoreUnknown = true)public class ProcessVariableStringValue extends ProcessVariableParent{ @JsonProperty("entries")// @JsonDeserialize(using = CustomEntriesDeserializer.class) List<ProcessVariableEntries> entries; @JsonProperty("key") String key; @JsonProperty("value") String value; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public List<ProcessVariableEntries> getEntries() { return entries; } public void setEntries(List<ProcessVariableEntries> entries) { this.entries = entries; }}package com.murthy;import java.io.IOException;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import com.fasterxml.jackson.core.JsonParser;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.DeserializationContext;import com.fasterxml.jackson.databind.JsonDeserializer;import com.fasterxml.jackson.databind.JsonNode;import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule;import com.fasterxml.jackson.dataformat.xml.XmlMapper;public class CustomEntriesDeserializer extends JsonDeserializer<List<ProcessVariableEntries>> {// public CustomListDeserializer() {// this(null);// }// public CustomListDeserializer(Class<?> vc) {// super(vc);// }// @Override public List<ProcessVariableEntries> deserialize( JsonParser jp, DeserializationContext context) throws IOException, JsonProcessingException {// ObjectCodec oc = jsonparser.getCodec(); JsonNode node = jp.getCodec().readTree(jp); Iterator<JsonNode> iter = node.elements(); while(iter.hasNext()) { JsonNode n = iter.next(); System.out.println("json node" + n); }// ObjectMapper mapper = (ObjectMapper) jp.getCodec();// Map<String, Object> m=mapper.readValue(jp, new TypeReference<Map<String, Object>>() {});// for (Map.Entry<String, Object> me : m.entrySet()) {// System.out.println("key=" + me.getKey() + " val=" + me.getValue());// }// ObjectNode root = (ObjectNode) mapper.readTree(jp); JacksonXmlModule module = new JacksonXmlModule(); module.setDefaultUseWrapper(false); XmlMapper xmlMapper = new XmlMapper(module); ProcessVariableEntries pve = xmlMapper.readValue(jp,ProcessVariableEntries.class);// ProcessVariableEntries pve = mapper.readValue(jp, ProcessVariableEntries.class);// List<ProcessVariableEntries> pveList = mapper.readValues(jp, ProcessVariableEntries.class); List<ProcessVariableEntries> pveList = new ArrayList<ProcessVariableEntries>(); pveList.add(pve);// System.out.println("root=" + root);// ProcessVariableEntry pve2 = null;// pve2=mapper.readValue(jp, ProcessVariableEntry.class);// ProcessVariableStringValue pvs=null;// if (pve2 == null) {// pvs = mapper.readValue(jp, ProcessVariableStringValue.class);// }// if (pvs == null) {// // } System.out.println(pveList); if (1==1) return pveList; Class<? extends ProcessVariableParent> instanceClass = null; if(1==2) { System.out.println("in proc var"); instanceClass = ProcessVariableValue.class; } else { System.out.println("in proc var string"); instanceClass = ProcessVariableStringValue.class; } if (instanceClass == null){ return null; } System.out.println("instance found!" );// Iterator<JsonNode> iter = root.elements();// while(iter.hasNext()) {// JsonNode n = iter.next();// System.out.println("json node" + n);// } return null;// mapper.readValue(jp, instanceClass );// Map<String, Object> m=oc.readValue(jsonparser, new TypeReference<Map<String, Object>>() {});// for (Map.Entry<String, Object> me : m.entrySet()) {// System.out.println("key=" + me.getKey() + " val=" + me.getValue());// }// if (jsonparser.getCodec().readTree(jsonparser) instanceof ObjectNode)// node = oc.readTree(jsonparser);// else// s = jsonparser.getCodec().readTree(jsonparser).toString();// System.out.println("code=" + jsonparser.getCodec().readTree(jsonparser));// JsonNode node1 = oc.readTree(jsonparser);// System.out.println("node1=" + node1 + node1.get(2) + " " + node1.get(1)); /* Iterator<JsonNode> iter = node1.get(2).elements(); while(iter.hasNext()) { JsonNode node = iter.next(); System.out.println("json node" + node); } */// String type = (String)m.get("type");// System.out.println("returning new process variable value");// ProcessVariableValue pvv = new ProcessVariableValue();// pvv.setValue(m == null ? "" : (String)m.get(""));// return pvv; }}
答案:
If XML
payload contains recursive structure you need to build similar Java
POJO
model classes. All values could extends the same interface
or abstract class
. Take a look on below example model:
@JsonTypeName("map-type")class Root { @JacksonXmlProperty(localName = "entry") @JacksonXmlElementWrapper(localName = "entries") private List<Entry> entries; public List<Entry> getEntries() { return entries; } public void setEntries(List<Entry> entries) { this.entries = entries; }}@JsonTypeInfo(include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type", use = JsonTypeInfo.Id.NAME)@JsonSubTypes({ @JsonSubTypes.Type(name = "xs:string", value = StringType.class), @JsonSubTypes.Type(name = "jaxbMap", value = MapType.class)})interface JBPMValue {}class StringType implements JBPMValue { private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } @JsonAnySetter public void anySetter(String key, String value) { this.value = value.trim(); } @Override public String toString() { return value; }}class MapType implements JBPMValue { @JacksonXmlProperty(localName = "entry") @JacksonXmlElementWrapper(localName = "entries") private List<Entry> entries; public List<Entry> getEntries() { return entries; } public void setEntries(List<Entry> entries) { this.entries = entries; } @Override public String toString() { final StringBuilder sb = new StringBuilder("MapType{"); sb.append(System.lineSeparator()); entries.forEach(e -> sb.append(e.toString()).append(System.lineSeparator())); sb.append('}'); return sb.toString(); }}class Entry { private String key; private JBPMValue value; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public JBPMValue getValue() { return value; } public void setValue(JBPMValue value) { this.value = value; } @Override public String toString() { return "{" + key + "=" + value + "}"; }}
As you can see I used @JsonTypeInfo
and @JsonSubTypes
to define POJO
classes for each type in XML
. @JacksonXmlProperty
and @JacksonXmlElementWrapper
annotation are used to represent wrapped collections.
Now, we can create simple example how to use it:
import com.fasterxml.jackson.annotation.JsonAnySetter;import com.fasterxml.jackson.annotation.JsonSubTypes;import com.fasterxml.jackson.annotation.JsonTypeInfo;import com.fasterxml.jackson.annotation.JsonTypeName;import com.fasterxml.jackson.dataformat.xml.XmlMapper;import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;import java.io.File;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().build(); Root root = mapper.readValue(xmlFile, Root.class); root.getEntries().forEach(System.out::println); }}
Above code for your XML
payload prints:
{document=MapType{{org.jbpm.document.service.impl.DocumentImpl=MapType{{identifier=fc3a87c9-d22c-449b-b772-756fcc9a385d}{size=1186}{name=dmv-registration.txt}{link=fc3a87c9-d22c-449b-b772-756fcc9a385d}{attributes=null}{lastModified=Mon Mar 09 18:19:25 PDT 2020}{content=null}}}{uploader_name=murthy}{[emailprotected]}}}{initiator=kieserver}