定製 Jackson 解析器來完成對複雜格式 XML 的解析

先點贊再看,養成好習慣

背景

前陣子,公司渠道平臺對接了一個新渠道,使用 XML 格式的報文交互。雖然 XML 格式有點複雜且「古老」,但好在它功能強大,支持屬性、註釋,比 JSON 要加的更直觀和清晰。java

可是吧,功能強大的同時也有一些弊端。某些複雜的 XML 並不能像 JSON 那樣直接和實體類映射,由於 XML 是支持屬性(attribute)的,若是一個標籤同時擁有多個屬性,或者屬性和值,就不太好實體類映射了,好比下面這種報文:git

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

解決方案

像上面這種複雜一點的報文,也是有解決方案的,只是有點不太優雅。好比我能夠建立一個 ExtendInfo 類,定義 key/value 兩個屬性,而後再將 extendInfos 定義爲一個 List,也能夠完成解析:github

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

但是這個數據格式很明顯是個鍵值對格式,弄個 List 來存儲,是否是有點太傻了?要是能用 Map 來接收 extendInfos 的數據該多好……app

Jackson 是個功能很是強大的序列化庫,除了支持 JSON 之外,還支持不少其餘格式,好比 XML。並且 Jackson 還能夠自定義對解析器的加強,經過對 JsonDeserializer 接口的擴展,能夠完成更復雜數據的解析:

基於 Jackson,能夠定製化一下解析器,來完成上面複雜數據的解析,將 extendInfos 解析爲一個 Map,方便程序的處理ide

先定義一個 AttrMap ,用來標記咱們這個特殊的數據類型,直接繼承 HashMap 就好:測試

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

接着將 Root 中的類型修改成這個 AttrMap:code

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

而後是自定義的類型解析器 - AttrMapDeserializer,在這個解析器裏將報文和 AttrMap 映射orm

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;
    }
}

好了,大功告成,來測試一下:xml

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=周關}}

附錄

Jackson XML 模塊繼承

<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/...

原創不易,禁止未受權的轉載。若是個人文章對您有幫助,就請點贊/收藏/關注鼓勵支持一下吧❤❤❤❤❤❤
相關文章
相關標籤/搜索