来自xml的吐槽

来自xml的吐槽,第1张

来自xml的吐槽

描述

近期与供应商对接的时候,对方的接口还是比较稳定的xml接口,与当下流行的json的便捷性相比还是不太方便,中间我也使用了几种工具类,很难有完全适配的,下文我将讲述我解析xml的历程

初次接触

第一次接触xml接口是在对接物流渠道商的时候,当时他们使用的对接方式为soap格式,刚接触解析起来简直要了老命,什么是soap呢?是基于xml的简易协议,常用于webservie,它有自己的一套编码规则,如下

这种请求网上解析方式就很少,很多只有解析没有构造,文本也不太全,目前见过写的比较好的解析如下:https://blog.csdn.net/RUANJIAOXIAOZI/article/details/90770534

当然还有xsd模式的xml

因为这次讲的是xml解析,上面只是举个栗子,让你们体会一下xml的变种有多难

这种接口不过分的说,至少十年往上的架构了

使用dom4j/jsoup解析

dom4j应该是最经典解析xml的api了,性能优异,功能强大。但是使用起来还是略为麻烦,有点像用java的jsoup去爬取网页,需要一个一个节点的去找

Dom4j获取xml的三种方式
1.读取xml文件,获得document对象
    SAXReader reader = new SAXReader();
    document document = reader.read(new File("test.xml"));

2.直接解析xml形式的文本
    String text = "";
	document document = documentHelper.parseText(text);

3.主动创建document对象
    document document = documentHelper.createdocument();
	Element root = document.addElement("tag");
jsoup解析html方式
document document = Jsoup.parse(html);
Element postList = document.getElementById("post_list");
Elements titleEle = postItem.select(".post_item_body a[class='titlelnk']");

以上这种解析方式,属于所见即所得随时可取,但是往往可读性比较差,如图

当然如果你不嫌麻烦,jsoup也可以解析xml的

public class JosupTest {
    public static void main(String[] args) throws IOException {
        //1. 获取document对象,根据xml文档获取
        //2. 获取user.xml的path
        String path = Objects.requireNonNull(JosupTest.class.getClassLoader().getResource("User.xml")).getPath();
        //3. 解析xml文档,加载文档进内存,获取dom树----->document
        document document = Jsoup.parse(new File(path),"UTF-8");
        //4. 获取元素对象Elements(类型为ArrayList)
        Elements elements = document.getElementsByTag("name");
        //5. 测试获取元素的个数是否符合,xml文件中的个数
        System.out.println(elements.size());
        //5. 测试获取第一个元素
        Element element = elements.get(0);
        //5. 测试获取第一个元素的文本内容
        String name = element.text();
        //5. 测试获取第一个元素的名字是否正确
        System.out.println(name);
    }
}

利用fastjson进行转换

fastjson和gson这两种解析方式是我使用最多的两种解析方式

gson:快速,高效,代码量少,面向对象,但是相对fastjson和jackjson,它的各方面性能都被碾压

fastjson:性能最高,支持多种类型解析,由于fastjson太侧重性能,对于部分高级特性支持不够,有一部分自定义特性完全偏离了json和js规范,可能导致与其他框架不兼容的bug,并且文档缺失较多,而且代码缺少注释较为晦涩,近几年也出现过一些高危漏洞

如果要使用fastjson解析xml为json格式就需要使用工具类的形式进行转换

package com.service.zl.model;

import java.util.List;

import org.dom4j.Attribute;
import org.dom4j.document;
import org.dom4j.documentException;
import org.dom4j.documentHelper;
import org.dom4j.Element;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

public class XmlTools {

    
    public static document strTodocument(String xml) throws documentException {
        return documentHelper.parseText(xml);
    }

    
    public static JSONObject documentToJSONObject(String xml) throws documentException {
        return elementToJSONObject(strTodocument(xml).getRootElement());
    }

    
    public static JSONObject elementToJSONObject(Element node) {
        JSONObject result = new JSONObject();
        // 当前节点的名称、文本内容和属性
        List listAttr = node.attributes();// 当前节点的所有属性的list
        for (Attribute attr : listAttr) {// 遍历当前节点的所有属性
            result.put(attr.getName(), attr.getValue());
        }
        // 递归遍历当前节点所有的子节点
        List listElement = node.elements();// 所有一级子节点的list
        if (!listElement.isEmpty()) {
            for (Element e : listElement) {// 遍历所有一级子节点
                if (e.attributes().isEmpty() && e.elements().isEmpty()) // 判断一级节点是否有属性和子节点
                    result.put(e.getName(), e.getTextTrim());// 沒有则将当前节点作为上级节点的属性对待
                else {
                    if (!result.containsKey(e.getName())) // 判断父节点是否存在该一级节点名称的属性
                        result.put(e.getName(), new JSONArray());// 没有则创建
                    ((JSONArray) result.get(e.getName())).add(elementToJSONObject(e));// 将该一级节点放入该节点名称的属性对应的值中
                }
            }
        }
        return result;
    }
}

上面工具类可以直接把xml转为json格式,非常方便,但是局限性太大 只能单向解析,所以最好的方式还是建立实体类的方式

jaxb和jackson

jaxb:它是一个业界的标准,是一项可以根据xml生成java类的技术。也可以根据xml实例文档反向生成java对象树的方法,与sax和dom不同,不需要了解xml解析技术,就两种 *** 作java对象转xml和xml转java对象

jackson:它性能介于fastjson和gson之间,但是它是目前最流行的api,规范性高,漏洞也没有fastjson多,还支持json和xml转换,目前市场上最好用的api之一

先来看看jaxb的解析xml方式

@XmlRootElement(name = "ServicesError")
public class TaoBaobaseRequest implements Serializable {
    private String errorCode;

    private String errorMessage;

    @XmlElement(name = "ErrorCode")
    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    @XmlElement(name = "ErrorMessage")
    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }
}

jaxb的解析可以参考这篇文章:https://blog.csdn.net/wn084/article/details/80853587

以下是jackjson的解析

package com.model.taobao;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;


public class test {
    public static void main(String[] args) throws IOException, XMLStreamException {
        String xml = "";
        DataList dataList1 = xmlToObject(xml, DataList.class);
        System.out.println(JSON.toJSONString(dataList1));
    }
    public static String objectToXml(Object object) throws IOException {
        XmlMapper xmlMapper= new XmlMapper();
        xmlMapper.setDefaultUseWrapper(false);
        
        xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        
        xmlMapper.enable(MapperFeature.USE_STD_BEAN_NAMING);
        String resultXml = xmlMapper.writevalueAsString(object);

        return resultXml;
    }
    public static  T xmlToObject(String xml,Class clazz) throws IOException, XMLStreamException {
        XmlMapper xmlMapper= new XmlMapper();
        xmlMapper.setDefaultUseWrapper(false);
        
        xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        
        xmlMapper.enable(MapperFeature.USE_STD_BEAN_NAMING);
        return xmlMapper.readValue(xml, clazz);
    }
}


@JacksonXmlRootElement
class DataList{
    @JacksonXmlProperty(isAttribute = true)
    private String Airlines;
    @JacksonXmlProperty(isAttribute = true)
    private String Dpt;
    @JacksonXmlProperty(isAttribute = true)
    private String Arr;
    @JacksonXmlProperty(isAttribute = true)
    private String Date;
    @JacksonXmlProperty(isAttribute = true)
    private String Carrier;
    @JacksonXmlProperty(isAttribute = true)
    private String Cabin;
    @JacksonXmlProperty(isAttribute = true)
    private String Code;

    public String getAirlines() {
        return Airlines;
    }

    public void setAirlines(String airlines) {
        Airlines = airlines;
    }

    public String getDpt() {
        return Dpt;
    }

    public void setDpt(String dpt) {
        Dpt = dpt;
    }

    public String getArr() {
        return Arr;
    }

    public void setArr(String arr) {
        Arr = arr;
    }

    public String getDate() {
        return Date;
    }

    public void setDate(String date) {
        Date = date;
    }

    public String getCarrier() {
        return Carrier;
    }

    public void setCarrier(String carrier) {
        Carrier = carrier;
    }

    public String getCabin() {
        return Cabin;
    }

    public void setCabin(String cabin) {
        Cabin = cabin;
    }

    public String getCode() {
        return Code;
    }

    public void setCode(String code) {
        Code = code;
    }

    public DataList() {
    }
}

里面写了一个工具类:objectToXml和xmlToObject可以转换xml和实体类
注意踩坑

坑一:大小写问题

@JacksonXmlProperty(localName = "Apid")

private Integer Apid;

private Integer getApid()

{

    return this.Apid

}

把注解放在成员变量上面,会解析出两个apid字段,一个是,另一个是

这是因为Jackson的处理机制会自动从属性方法上获取成员变量名,然而在java中,要么以驼峰命名,要么前两个字母都大写,才能用get方法正确地获取属性,所以使用getApid获取的成员名称就是apid,被jackson解析了出来。又因为成员变量上也加了注解,所以也会被解析。这就造成了xml文件生成了两个apid标签。正确的做法是把注解写到get方法上面

正确写法:

private Integer Apid;

@JacksonXmlProperty(localName = "Apid")

private Integer getApid()

{

    return this.Apid

}

 

坑二:Jackson封装list问题

这个问题排查的时候异常困难……开始以为是封装的问题…………(吐槽一下,这个外包项目使用大量xml交互,但是又用不了webservice就得按照固定格式解析封装xml.....改动也贼困难。。。)……最后还是确定了是jackson的问题

private List APID;

@JacksonXmlProperty(localName = "APID")
@JacksonXmlElementWrapper(useWrapping = false)
public List getAPID() {
return APID;
}

开始APID这个list一直被包装了两层!正确结果应该是111

但是得到的是111

问题出在JacksonXmlElementWrapper
如果不指定的话这个值默认是true

总结

目前感觉解析xml的api还是比较多的,针对xml的各种奇奇怪怪的格式,并不是都能兼容到,目前感觉最好用的还是jackson+Lombok能比较好的快速解决问题,当然要注意lombok的侵入性和jdk的版本选择最好的方式去解决,有些方式虽然不方便,但是它就是能解决问题……

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5719445.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存