Customize Jackson parser to complete the parsing of complex format XML

Time:2021-8-4

Like it first and then watch it. Form a good habit

background

A while ago, the company’s channel platform docked a new channel and used XML format message interaction. Although the XML format is a little complex and “old”, the good thing is that it is powerful, supports attributes and comments, and is more intuitive and clear than JSON.

However, there are some disadvantages as well as powerful functions. Some complex XML cannot be directly mapped to entity classes like JSON, because XML supports attributes. If a tag has multiple attributes or attributes and values at the same time, it is not good for entity class mapping, such as the following message:

<Root> 
  <extendInfos> 
    <extendInfo key="birthday" value="19870101"/>  
    < extendinfo key = "address" value = "Beijing" / >  
    <extendInfo key="gender" value="M"/>  
    < extendinfo key = "username" value = "weekly off" / > 
  </extendInfos> 
</Root>

Solution

There are solutions for more complex messages like the above, but they are not very elegant. For example, I can create an extendinfo class, define two attributes of key / value, and then define extendinfos as a list. I can also complete the parsing:

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

But this data format is obviously a key value pair format. Is it a little silly to get a list to store it? If only map could be used to receive data from extendinfos

Jackson is a very powerful serialization library. In addition to supporting JSON, it also supports many other formats, such as XML. Moreover, Jackson can customize the enhancements to the parser throughJsonDeserializerThe extension of the interface can complete the analysis of more complex data:

Based on Jackson, you can customize the parser to complete the parsing of the above complex data, and parse extendinfos into a map to facilitate the processing of the program

First define an attrmap to mark our special data type. Just inherit the 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 Parser – attrmapdeserializer, in which the message and attrmap are mapped

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 && curToken.id() == JsonTokenId.ID_FIELD_NAME){
            //skip start token
            p.nextToken();
            String key = null,value = null;
            while ((curToken = p.nextToken()) != null
                    &&
                    curToken.id()== JsonTokenId.ID_FIELD_NAME){
                String attrName = p.getCurrentName();
                String attrValue = p.nextTextValue();
                if("key".equals(attrName)){
                    key = attrValue;
                }
                //Handle < attr key = "any" value = "any" / > and < attr key = "any" > 123213 < / attr >
                if("value".equals(attrName) || "".equals(attrName)){
                    value = attrValue;
                }
            }
            attrMap.put(key,value);
        }
        return attrMap;
    }
}

Well, it’s done. Let’s test it:

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 = \" Zhouguan \ "/ > \ 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 = Zhou Guan}}

appendix

Jackson XML module

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

Jackson XML Wiki

https://github.com/FasterXML/…

Original is not easy, unauthorized reprint is prohibited. If my article is helpful to you, please like / collect / pay attention to encourage and support it ❤❤❤❤❤❤

Recommended Today

Supervisor

Supervisor [note] Supervisor – H view supervisor command help Supervisorctl – H view supervisorctl command help Supervisorctl help view the action command of supervisorctl Supervisorctl help any action to view the use of this action 1. Introduction Supervisor is a process control system. Generally speaking, it can monitor your process. If the process exits abnormally, […]