
描述
近期与供应商对接的时候,对方的接口还是比较稳定的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的版本选择最好的方式去解决,有些方式虽然不方便,但是它就是能解决问题……
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)