问题
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">gandikotam@gmail.com
</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;
}
}
回答1:
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}
{uploader_mail=gandikotam@gmail.com}
}}
{initiator=kieserver}
来源:https://stackoverflow.com/questions/60678696/recursive-nodes-in-xml-generated-by-jbpm-7