描述
近期与供应商对接的时候,对方的接口还是比较稳定的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()); } // 递归遍历当前节点所有的子节点 ListlistElement = 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 staticT 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 ListAPID; @JacksonXmlProperty(localName = "APID") @JacksonXmlElementWrapper(useWrapping = false) public List getAPID() { return APID; } 开始APID这个list一直被包装了两层!正确结果应该是111 但是得到的是111 问题出在JacksonXmlElementWrapper 如果不指定的话这个值默认是true
总结
目前感觉解析xml的api还是比较多的,针对xml的各种奇奇怪怪的格式,并不是都能兼容到,目前感觉最好用的还是jackson+Lombok能比较好的快速解决问题,当然要注意lombok的侵入性和jdk的版本选择最好的方式去解决,有些方式虽然不方便,但是它就是能解决问题……
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)