First thumb up and then watch, form a good habit


Some time ago, the corporate channel platform was connected to a new channel that uses XML format for message interaction. The XML format is a bit complex and “artefacts,” but fortunately it is powerful, with support for attributes, annotations, and is more intuitive and clear than JSON.

However, it has some disadvantages as well. Some complex XML does not map to entity classes as directly as JSON, because XML supports attributes. If a tag has multiple attributes, or attributes and values, then entity classes do not map well, such as the following message:

<Root> <extendInfos> <extendInfo key="birthday" /> <extendInfo key=" 19870101"/> <extendInfo key=" 19870101"/> <extendInfo key=" Beijing "/> <extendInfo Key ="gender" value="M"/> < ExtendInfos > </Root>

The solution

For more complex messages like the one above, there is also a solution, but it is a little less elegant. For example, I can create an ExtendInfo class with key/value attributes, and then define ExtendInfos as a List, or I can complete the parse:

public class Root {
    private List<ExtendInfo> extendInfos;
    // getter and setters....

But the data format is obviously a key-value pair format. Isn’t it silly to have a List to store it? If only we could use a Map to receive data from ExtendInfos…

Jackson is a very powerful serialization library that supports many formats, such as XML, in addition to JSON. Jackson will also be able to customize the enhancements to the parser by using the
JsonDeserializerInterface extensions that enable parsing of more complex data:

Based on Jackson, you can customize the parser to parse the complex data above, parsing ExtendInfos into a Map for your application to handle

We’ll define an AttrMap to mark our particular data type, which inherits HashMap directly:

public class AttrMap<K,V> extends HashMap<K,V> {

Then change the type in Root to this AttrMap:

public class Root {
    private AttrMap<String,Object> extendInfos;
    // getter and setters....

Then there is the custom type resolver, AttrMapDeserializer, which maps the packet to the AttrMap

public class AttrMapDeserializer extends JsonDeserializer<AttrMap<String,String>> { @Override public AttrMap<String,String> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { JsonToken curToken ; AttrMap<String,String> attrMap = new AttrMap<>(); while ((curToken = p.nextToken()) ! =null && == JsonTokenId.ID_FIELD_NAME){ //skip start token p.nextToken(); String key = null,value = null; while ((curToken = p.nextToken()) ! = null && JsonTokenId.ID_FIELD_NAME){ String attrName = p.getCurrentName(); String attrValue = p.nextTextValue(); if("key".equals(attrName)){ key = attrValue; } / / processing < attr key = "any" value = "any" / > and < attr key = "any" > 123213 < / attr > two forms the if (" value "equals (attrName) | | "".equals(attrName)){ value = attrValue; } } attrMap.put(key,value); } return attrMap; }}

Okay, we’re done, let’s test it out:

String body = "<Root> \n" + " <extendInfos> \n" + " <extendInfo key=\"birthday\" value=\"19870101\"/> \n" + " < extendInfo key = \ "address \" value = \ \ "Beijing" / > \ n "+" < extendInfo key = \ "gender \" value = \ \ "M" / > \ n "+" < extendInfo Key = \ userName \ "value = \" weeks off \ "/ > \ n" + "< / extendInfos > \ n" + "< / Root >"; JacksonXmlModule module = new JacksonXmlModule(); module.addDeserializer(AttrMap.class, new AttrMapDeserializer()); ObjectMapper objectMapper = new XmlMapper(module); Root root = objectMapper.readValue(body, Root.class); System.out.println(root); //output Root{extras={birthday=19870101, address= Beijing, gender=M, userName= C}}

The appendix

Jackson XML module

<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> The < version > 2.12.2 < / version > < / dependency >

Jackson XML Wiki…

Original is not easy, prohibit unauthorized reprint. If my article is helpful to you, please feel free to support me at thumb up/bookmark/follow me