5
Like it first, then watch it, develop a good habit

background

A while ago, the company channel platform docked a new channel, using XML format message interaction. Although the XML format is a bit complicated and "old", fortunately, it is powerful, supports attributes and comments, and is more intuitive and clearer than JSON.

But, while it's powerful, it also has some drawbacks. 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, entity class mapping is not good, for example The following message:

<Root> 
  <extendInfos> 
    <extendInfo key="birthday" value="19870101"/>  
    <extendInfo key="address" value="北京"/>  
    <extendInfo key="gender" value="M"/>  
    <extendInfo key="userName" value="周关"/> 
  </extendInfos> 
</Root>

solution

There are also solutions for a more complicated message like the above, but it is a bit inelegant. For example, I can create an ExtendInfo class, define two attributes of key/value, and then define extendInfos as a List, which can also complete the analysis:

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

But this data format is obviously a key-value pair format. Isn't it a bit silly to get a List to store it? It would be great if you could use Map to receive extendInfos data...

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

Based on Jackson, you can customize the parser to complete the analysis 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 HashMap directly:

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

Then modify the type in Root to this AttrMap:

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

Then there is a custom type resolver-AttrMapDeserializer, in which the message is mapped 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 && 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;
                }
                //处理<attr key="any" value="any"/>和<attr key="any">123213</attr>两种形式
                if("value".equals(attrName) || "".equals(attrName)){
                    value = attrValue;
                }
            }
            attrMap.put(key,value);
        }
        return attrMap;
    }
}

Well, you're done, let's test it:

String body = "<Root> \n" +
        "  <extendInfos> \n" +
        "    <extendInfo key=\"birthday\" value=\"19870101\"/>  \n" +
        "    <extendInfo key=\"address\" value=\"北京\"/>  \n" +
        "    <extendInfo key=\"gender\" value=\"M\"/>  \n" +
        "    <extendInfo key=\"userName\" value=\"周关\"/> \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=北京, gender=M, userName=周关}}

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/jackson-dataformat-xml/wiki

Originality is not easy, unauthorized reprinting is prohibited. If my article is helpful to you, please like/favorite/follow to encourage and support it ❤❤❤❤❤❤

空无
3.3k 声望4.3k 粉丝

坚持原创,专注分享 JAVA、网络、IO、JVM、GC 等技术干货