Java Debug 笔记:定制 Jackson 解析器来完成对复杂格式 XML 的解析

Posted king哥Java架构

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java Debug 笔记:定制 Jackson 解析器来完成对复杂格式 XML 的解析相关的知识,希望对你有一定的参考价值。

背景

前阵子,公司渠道平台对接了一个新渠道,使用 XML 格式的报文交互。虽然 XML 格式有点复杂且“古老”,但好在它功能强大,支持属性、注释,比 JSON 要加的更直观和清晰。

但是吧,功能强大的同时也有一些弊端。某些复杂的 XML 并不能像 JSON 那样直接和实体类映射,因为 XML 是支持属性(attribute)的,如果一个标签同时拥有多个属性,或者属性和值,就不太好实体类映射了,比如下面这种报文:

<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,也可以完成解析:

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

可是这个数据格式很明显是个键值对格式,弄个 List 来存储,是不是有点太傻了?要是能用 Map 来接收 extendInfos 的数据该多好……

Jackson 是个功能非常强大的序列化库,除了支持 JSON 以外,还支持很多其他格式,比如 XML。而且 Jackson 还可以自定义对解析器的增强,通过对 JsonDeserializer 接口的扩展,可以完成更复杂数据的解析:

基于 Jackson,可以定制化一下解析器,来完成上面复杂数据的解析,将 extendInfos 解析为一个 Map,方便程序的处理

如果你觉得自己学习效率低,缺乏正确的指导,可以加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧!
[Java架构群]
群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的JAVA交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

先定义一个 AttrMap ,用来标记我们这个特殊的数据类型,直接继承 HashMap 就好:

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

接着将 Root 中的类型修改为这个 AttrMap:

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

然后是自定义的类型解析器 - AttrMapDeserializer,在这个解析器里将报文和 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;
    }
} 

好了,大功告成,来测试一下:

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> 

最后

给大家分享一篇一线开发大牛整理的java高并发核心编程神仙文档,里面主要包含的知识点有:多线程、线程池、内置锁、JMM、CAS、JUC、高并发设计模式、Java异步回调、CompletableFuture类等。

文档地址:一篇神文就把java多线程,锁,JMM,JUC和高并发设计模式讲明白了

码字不易,如果觉得本篇文章对你有用的话,请给我一键三连!关注作者,后续会有更多的干货分享,请持续关注!

以上是关于Java Debug 笔记:定制 Jackson 解析器来完成对复杂格式 XML 的解析的主要内容,如果未能解决你的问题,请参考以下文章

JackSon学习笔记

Jackson的使用和定制

定制 Jackson 解析器来完成对复杂格式 XML 的解析

spring boot定制Jackson ObjectMapper,为什么不生效

Spring Boot 自定义Jackson ObjectMapper

Jackson 学习笔记 XML转JSON