在大型項目編碼推進中,涉及到 XML 解析問題時,大多數程式員都不太會選用底層的解析方式直接編碼。 主要存在編碼複雜性、難擴展、難復用....,但如果你是 super 程式員或是一個人的項目,也不妨一試。 Jdom/Dom4j/Xstream... 基於底層解析方式重新組織封裝的開源類庫,簡潔明瞭的 ...
在大型項目編碼推進中,涉及到 XML 解析問題時,大多數程式員都不太會選用底層的解析方式直接編碼。
主要存在編碼複雜性、難擴展、難復用....,但如果你是 super 程式員或是一個人的項目,也不妨一試。
Jdom/Dom4j/Xstream... 基於底層解析方式重新組織封裝的開源類庫,簡潔明瞭的 API,穩定高效的運行表現。
Dom4j 基於 JAXP 解析方式,性能優異、功能強大、極易使用的優秀框架。想瞭解底層解析方式請翻看:淺談 Java XML 底層解析方式
Jdom 你細看內部代碼,本質也是基於 JAXP 但包結構被重新組織, API 大量使用了 Collections 類,在性能上被 dm4j 壓了好幾個檔次。
Xstream 基於 xmlpull 的 OXMapping 技術,更加傾向於將 XML 解析後映射為 Java 世界中的對象,等會在代碼中會看的很清楚。
如果你是一名大型項目技術負責人,需求中涉及 XML 解析方面的要求,在程式員編碼開始前你需要在充分瞭解需求的前提下。
來拿捏 XML 解析解決方案所採用的技術,請收藏這篇博客,到時將會給你一些幫助和指導。
需要解析的還是上篇的中 demo.xml,中規中矩不複雜也不簡單,實例 demo 地址:https://git.oschina.net/LanboEx/xml-parse-demo.git
<?xml version="1.0"?>
<classGrid>
<classGridlb>
<class_id>320170105000009363</class_id>
<class_number>0301</class_number>
<adviser>018574</adviser>
<studentGrid>
<studentGridlb>
<stu_id>030101</stu_id>
<stu_name>齊天</stu_name>
<stu_age>9</stu_age>
<stu_birthday>2008-11-07</stu_birthday>
</studentGridlb>
<studentGridlb>
<stu_id>030102</stu_id>
<stu_name>張惠</stu_name>
<stu_age>10</stu_age>
<stu_birthday>2009-04-08</stu_birthday>
</studentGridlb>
<studentGridlb>
<stu_id>030103</stu_id>
<stu_name>龍五</stu_name>
<stu_age>9</stu_age>
<stu_birthday>2008-11-01</stu_birthday>
</studentGridlb>
</studentGrid>
</classGridlb>
<classGridlb>
<class_id>420170105000007363</class_id>
<class_number>0302</class_number>
<adviser>018577</adviser>
<studentGrid>
<studentGridlb>
<stu_id>030201</stu_id>
<stu_name>馬寶</stu_name>
<stu_age>10</stu_age>
<stu_birthday>2009-09-02</stu_birthday>
</studentGridlb>
</studentGrid>
</classGridlb>
</classGrid>
demo.xml
1. Jdom
Jdom 基於樹處理 XML,需要將樹載入到記憶體中,所以你懂的大於記憶體的 XML 文件,Jdom 其實是拒絕的。
Jdom 具有 SAX 的 java 規則,可以使用推模型分析 XML,所以在一定程度上解析速度可以保證。
Jdom 沒有向下相容的限制,所以比底層 dom 簡單,但在表示文檔邏輯模型時,不能保證每個位元組真正變換。
mvn 依賴:
<dependency>
<groupId>jdom</groupId>
<artifactId>jdom</artifactId>
<version>1.1</version>
</dependency>
實例 demo (將 demo.xml studentGridlbb 節點的值解析出來,組成業務實體對象) 。
String path = Thread.currentThread().getContextClassLoader().getResource("demo.xml").getPath();
SAXBuilder jdomsaxBuilder = new SAXBuilder(false);
Document doc = jdomsaxBuilder.build(path);
Element rootElement = doc.getRootElement();
List<StudentGridlb> studentGridlbList = new ArrayList<>();
StudentGridlb studentGridlb;
for (Object classGridlb : rootElement.getChildren("classGridlb")) {
Element classGridlbEle = (Element) classGridlb;
for (Object studentGrid : classGridlbEle.getChild("studentGrid").getChildren("studentGridlb")) {
Element studentGridEle = (Element) studentGrid;
studentGridlb = new StudentGridlb();
studentGridlb.setStu_id(studentGridEle.getChildTextTrim("stu_id"));
studentGridlb.setStu_age(Integer.parseInt(studentGridEle.getChildTextTrim("stu_age")));
studentGridlb.setStu_name(studentGridEle.getChildTextTrim("stu_name"));
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
studentGridlb.setStu_birthday(format.parse(studentGridEle.getChildTextTrim("stu_birthday")));
studentGridlbList.add(studentGridlb);
}
}
XMLOutputter outputter = new XMLOutputter();
outputter.output(doc, new FileOutputStream(path));
2. Dom4j
Dom4j 為了支持 XPath、XML Schema、基於事件處理大文檔或流文檔。
Dom4j 為提供構建文檔表示的選項,為可通過 Dom4j-API 和標準底層 dom-API 並行訪問功能。
為實現上述巨集偉目標,Dom4j 使用介面和抽象基本類方法並大量使用 JDK 中 Collections 類。
所以 Dom4j 有豐富的 API,在靈活性上面 Dom4j 更占有優勢,性能方面也無可挑剔。
聲名在外的 Sun-JAXM,大名鼎鼎的 Hibernate 中XML 配置文件解析都使用的是 Dom4j。
mvn 依賴:
<!--MetaStuff dom4j-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
<exclusions>
<exclusion>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
</exclusions>
</dependency>
剔除 xml-apis 的用意 JDK 中已經有對應的類,如不剔除在部署 weblogic 時會出現 Jar 衝突。
實例 demo (將 demo.xml studentGridlbb 節點的值解析出來,組成業務實體對象) 。
String path = Thread.currentThread().getContextClassLoader().getResource("demo.xml").getPath();
SAXReader reader = new SAXReader();
Document document = reader.read(new File(path));
List<StudentGridlb> studentGridlbList = new ArrayList<>();
StudentGridlb studentGridlbVo;
for (Object classGridlb : document.getRootElement().elements("classGridlb")) {
Element classGridlbEle = (Element) classGridlb;
for (Object studentGridlb : classGridlbEle.element("studentGrid").elements("studentGridlb")) {
Element studentGridlbEle = (Element) studentGridlb;
studentGridlbVo = new StudentGridlb();
studentGridlbVo.setStu_id(studentGridlbEle.elementTextTrim("stu_id"));
studentGridlbVo.setStu_age(Integer.parseInt(studentGridlbEle.elementTextTrim("stu_age")));
studentGridlbVo.setStu_name(studentGridlbEle.elementTextTrim("stu_name"));
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
studentGridlbVo.setStu_birthday(format.parse(studentGridlbEle.elementTextTrim("stu_birthday")));
studentGridlbList.add(studentGridlbVo);
}
}
3. Xstream
Xstream 為基於註解不需要其它輔助類或映射文件 的OXMapping 技術,如果你用過 hibernate 或 mybatis 之類的 ORM 框架就不難理解這裡的 OXM。
Xstream 可以將 JavaBean 序列化為 XML,或將 XML 反序列化為 JavaBean,使得XML序列化不再繁瑣。
Xstream 也可以將 JavaBean 序列化成 Json 或反序列化,使用非常方便。
沒有映射文件而且底層使用 xmlpull 推模型解析 XML,高性能、低記憶體占用,結合簡潔明瞭的 API,上手基本是分分鐘的事情。
Xstream 同時也可以定製轉換類型策略並配有詳細的錯誤診斷,能讓你快速定位問題。
使用 Xstream 時,規範和合理的業務對象命名是關鍵,下麵是針對 demo.xml 我抽象的幾個業務實體。
@XStreamAlias("classGrid")
public class ClassGrid {
@XStreamImplicit(itemFieldName="classGridlb")
private List<ClassGridlb> classGridlbList;
public List<ClassGridlb> getClassGridlbList() {
return classGridlbList;
}
public void setClassGridlbList(List<ClassGridlb> classGridlbList) {
this.classGridlbList = classGridlbList;
}
}
ClassGrid
@XStreamAlias("classGridlb")
public class ClassGridlb {
private String class_id;
private String class_number;
private String adviser;
private StudentGrid studentGrid;
public String getClass_id() {
return class_id;
}
public void setClass_id(String class_id) {
this.class_id = class_id;
}
public String getClass_number() {
return class_number;
}
public void setClass_number(String class_number) {
this.class_number = class_number;
}
public String getAdviser() {
return adviser;
}
public void setAdviser(String adviser) {
this.adviser = adviser;
}
public StudentGrid getStudentGrid() {
return studentGrid;
}
public void setStudentGrid(StudentGrid studentGrid) {
this.studentGrid = studentGrid;
}
}
ClassGridlb
@XStreamAlias("studentGrid")
public class StudentGrid {
@XStreamImplicit(itemFieldName="studentGridlb")
private List<StudentGridlb> studentGridlbList;
public List<StudentGridlb> getStudentGridlbList() {
return studentGridlbList;
}
public void setStudentGridlbList(List<StudentGridlb> studentGridlbList) {
this.studentGridlbList = studentGridlbList;
}
}
StudentGrid
@XStreamAlias("studentGridlb")
public class StudentGridlb {
private String stu_id;
private String stu_name;
private Integer stu_age;
@XStreamConverter(value=DateConverter.class)
private Date stu_birthday;
public String getStu_id() {
return stu_id;
}
public void setStu_id(String stu_id) {
this.stu_id = stu_id;
}
public String getStu_name() {
return stu_name;
}
public void setStu_name(String stu_name) {
this.stu_name = stu_name;
}
public Integer getStu_age() {
return stu_age;
}
public void setStu_age(Integer stu_age) {
this.stu_age = stu_age;
}
public Date getStu_birthday() {
return stu_birthday;
}
public void setStu_birthday(Date stu_birthday) {
this.stu_birthday = stu_birthday;
}
@Override
public String toString() {
return "StudentGridlb{" + "stu_id='" + stu_id + '\'' + ", stu_name='" + stu_name + '\'' + ", stu_age=" + stu_age + ", stu_birthday=" + stu_birthday + '}';
}
}
StudentGridlb
抽象好 XML 業務實體之後,接下來就很簡單了,只需要三行代碼。
Xstream Xstream = new Xstream(new DomDriver());
Xstream.processAnnotations(ClassGrid.class);
ClassGrid classGrid = (ClassGrid) Xstream.fromXML(new File(Thread.currentThread().getContextClassLoader().getResource("demo.xml").getPath()));
4. 小結
如果你看到過我上篇寫過的底層解析方式和這篇的開源類庫對比下,封裝優秀類庫,代碼的編寫量會小很多,而且簡化的 API 使用起來很順手。
Dom4j/Jdom 都支持對 XML 文檔的增刪改查動作,畢竟是已樹模型載入到記憶體中進行的操作。
Xstream 專註於 XML 和業務對象之間的序列化和反序列化,刪除和修改原 XML 文檔實踐起來相當困難。
我個人的建議,在項目中同時引入 Dom4j 和 Xstream,在 XMl 複雜情況下,可以考慮使用 Dom4j 獲取出感興趣的部分。
然後抽象出合適的業務實體,使用 Xstream 進行序列化, 進行系統邏輯後續處理。