描述
近期与供应商对接的时候,对方的接口还是比较稳定的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去爬取网页,需要一个一个节点的去找
1 2 3 4 5 6 7 8 9 10 11 12
| Dom4j获取xml的三种方式 1.读取xml文件,获得document对象 SAXReader reader = new SAXReader(); Document document = reader.read(new File("test.xml"));
2.直接解析xml形式的文本 String text = "<tag></tag>"; Document document = DocumentHelper.parseText(text);
3.主动创建document对象 Document document = DocumentHelper.createDocument(); Element root = document.addElement("tag");
|
1 2 3 4
| 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的😎
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class JosupTest { public static void main(String[] args) throws IOException { String path = Objects.requireNonNull(JosupTest.class.getClassLoader().getResource("User.xml")).getPath(); Document document = Jsoup.parse(new File(path),"UTF-8"); Elements elements = document.getElementsByTag("name"); System.out.println(elements.size()); Element element = elements.get(0); String name = element.text(); System.out.println(name); } }
|
利用fastjson进行转换
fastjson和gson这两种解析方式是我使用最多的两种解析方式
gson:快速,高效,代码量少,面向对象,但是相对fastjson和jackjson,它的各方面性能都被碾压
fastjson:性能最高,支持多种类型解析,由于fastjson太侧重性能,对于部分高级特性支持不够,有一部分自定义特性完全偏离了json和js规范,可能导致与其他框架不兼容的bug,并且文档缺失较多,而且代码缺少注释较为晦涩,近几年也出现过一些高危漏洞
如果要使用fastjson解析xml为json格式就需要使用工具类的形式进行转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| 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<Attribute> listAttr = node.attributes(); for (Attribute attr : listAttr) { result.put(attr.getName(), attr.getValue()); } List<Element> listElement = node.elements(); 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方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @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的解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
| 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 = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?><DataList Airlines=\"ALL\" Dpt=\"KMG\" Arr=\"JJN\" Date=\"20220301\" Carrier=\"SC\" Cabin = \"U\" Code=\"SC9260\"></DataList>"; 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> T xmlToObject(String xml,Class<T> 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和实体类
==注意踩坑==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| 坑一:大小写问题
@JacksonXmlProperty(localName = "Apid")
private Integer Apid;
private Integer getApid()
{
return this.Apid
}
把注解放在成员变量上面,会解析出两个apid字段,一个是<Apid></Apid>,另一个是<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<Integer> APID;
@JacksonXmlProperty(localName = "APID") @JacksonXmlElementWrapper(useWrapping = false) public List<Integer> getAPID() { return APID; }
开始APID这个list一直被包装了两层!正确结果应该是<APID>111</APID>
但是得到的是<APID><APID>111</APID></APID>
问题出在JacksonXmlElementWrapper 如果不指定的话这个值默认是true
|
总结
目前感觉解析xml的api还是比较多的,针对xml的各种奇奇怪怪的格式,并不是都能兼容到,目前感觉最好用的还是jackson+Lombok能比较好的快速解决问题,当然要注意lombok的侵入性和jdk的版本选择最好的方式去解决,有些方式虽然不方便,但是它就是能解决问题……