詳解Java解析XML的四種方法—DOM/SAX/jdom/dom4j
最近在研究XML檔案的生成和解析,網上資料很多,當然也參差不齊。寫的沒錯誤的通常是單獨介紹了1種方法,介紹全的常常執行不起來。
小哆把4種方法彙總了一下,執行驗證成功。
jar包免費下載:
XML在不同的語言裡解析方式都是一樣的,只不過實現的語法不同而已。基本的解析方式有兩種,一種叫DOM,另一種叫SAX。SAX是基於事件流的解析,DOM是基於XML文件樹結構的解析。假設我們XML的內容和結構如下(demo.xml):
<?xml version="1.0" encoding="UTF-8"?> <employees> <!--An XML Note --> <?target text?> <employee id="lo" name="lele"> <sex>m</sex> <age>23</age> </employee> <employee id="ve" name="fox"> <sex>f</sex> <age>22</age> </employee> </employees>
1.DOM生成和解析XML文件
W3C 規範化了 DOM,它的主要優點是可移植性:它是作為一種 CORBA 介面定義的,被對映到很多語言。因此如果瞭解了 JavaScript 中的 DOM,也就知道了 Java、C++、Perl、Python 和其他語言中的 DOM。
解析器讀入整個文件,然後構建一個駐留記憶體的樹結構,然後程式碼就可以使用
DOM 介面來操作這個樹結構。優點:整個文件樹在記憶體中,便於操作;支援刪除、修改、重新排列等多種功能;缺點:將整個文件調入記憶體(包括無用的節點),浪費時間和空間;使用場合:一旦解析了文件還需多次訪問這些資料;硬體資源充足(記憶體、CPU)。
package cn.main.dom; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * DOM生成與解析XML文件 * * @author 莫小哆_ly 2012-2-20 */ public class DomDemo { /* * 解析器讀入整個文件,然後構建一個駐留記憶體的樹結構, * * 然後程式碼就可以使用 DOM 介面來操作這個樹結構。 * * 優點:整個文件樹在記憶體中,便於操作;支援刪除、修改、重新排列等多種功能; * * 缺點:將整個文件調入記憶體(包括無用的節點),浪費時間和空間; * * 使用場合:一旦解析了文件還需多次訪問這些資料;硬體資源充足(記憶體、CPU) */ // 表示整個HTML或 XML文件。從概念上講,它是文件樹的根,並提供對文件資料的基本訪問 private Document document; /** * 建立DOM樹 * * 要讀入一個XML文件,首先要一個DocumentBuilder物件 */ public void init() { // 獲取 DocumentBuilderFactory 的新例項 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 使用當前配置的引數建立一個新的 DocumentBuilder 例項 DocumentBuilder builder = null; try { builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { e.printStackTrace(); } // 獲取 DOM Document 物件的一個新例項來生成一個 DOM 樹 this.document = builder.newDocument(); } /** * xml文件的寫入操作 * * @param file */ public void createXml(File file) { // 建立DOM樹 this.init(); // 建立XML根節點employees Element root = this.document.createElement("employees"); // Adds the node newChild to the end of the list of children of this // node. // If the newChild is already in the tree, it is first removed. this.document.appendChild(root); // 1.建立根節點的子節點employee Element employee = this.document.createElement("employee"); // 向根節點新增屬性節點 Attr id = this.document.createAttribute("id"); id.setNodeValue("0001"); // 把屬性節點物件,追加到達employee節點; employee.setAttributeNode(id); // 宣告employee的子節點name Element name = this.document.createElement("name"); // 向XML檔案name節點追加資料 name.appendChild(this.document.createTextNode("wanglp")); // 把子節點的屬性追加到employee子節點中元素中 employee.appendChild(name); // 宣告employee的子節點sex Element sex = this.document.createElement("sex"); // 向XML檔案sex節點追加資料 sex.appendChild(this.document.createTextNode("m")); // 把子節點的屬性追加到employee子節點中元素中 employee.appendChild(sex); // 宣告employee的子節點age Element age = this.document.createElement("age"); // 向XML檔案age節點追加資料 age.appendChild(this.document.createTextNode("25")); // 把子節點的屬性追加到employee子節點中元素中 employee.appendChild(age); // employee節點定義完成,追加到root root.appendChild(employee); // 2.建立根節點的子節點employee employee = this.document.createElement("employee"); // 向根節點新增屬性節點 id = this.document.createAttribute("id"); id.setNodeValue("0002"); // 把屬性節點物件,追加到達employee節點; employee.setAttributeNode(id); // 宣告employee的子節點name name = this.document.createElement("name"); // 向XML檔案name節點追加資料 name.appendChild(this.document.createTextNode("huli")); // 把子節點的屬性追加到employee子節點中元素中 employee.appendChild(name); // 宣告employee的子節點sex sex = this.document.createElement("sex"); // 向XML檔案sex節點追加資料 sex.appendChild(this.document.createTextNode("f")); // 把子節點的屬性追加到employee子節點中元素中 employee.appendChild(sex); // 宣告employee的子節點age age = this.document.createElement("age"); // 向XML檔案age節點追加資料 age.appendChild(this.document.createTextNode("12")); // 把子節點的屬性追加到employee子節點中元素中 employee.appendChild(age); // employee節點定義完成,追加到root root.appendChild(employee); // 獲取 TransformerFactory 的新例項。 TransformerFactory tf = TransformerFactory.newInstance(); // 建立執行從 Source 到 Result 的複製的新 Transformer。能夠將源樹轉換為結果樹 Transformer transformer = null; try { transformer = tf.newTransformer(); } catch (TransformerConfigurationException e) { e.printStackTrace(); } // 設定轉換中實際的輸出屬性 // 指定首選的字元編碼 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); // indent="yes"|"no".指定了當輸出結果樹時,Transformer是否可以新增額外的空白 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); // 宣告檔案流 PrintWriter pw = null; try { pw = new PrintWriter(new FileOutputStream(file)); } catch (FileNotFoundException e) { e.printStackTrace(); System.out.println("檔案沒有找到!"); } // 充當轉換結果的持有者,可以為 XML、純文字、HTML 或某些其他格式的標記 StreamResult result = new StreamResult(pw); // DOMSource implements Source DOMSource source = new DOMSource(document); try { // 將 XML Source 轉換為 Result transformer.transform(source, result); } catch (TransformerException e) { e.printStackTrace(); System.out.println("生成XML檔案失敗!"); } System.out.println("生成XML檔案成功!"); } /** * xml文件的讀取操作 * * @param file */ public void parserXml(File file) { // 獲取 DocumentBuilderFactory 的新例項 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 使用當前配置的引數建立一個新的 DocumentBuilder 例項 DocumentBuilder builder; try { builder = factory.newDocumentBuilder(); // 將給定 URI的內容解析為一個 XML文件,並且返回一個新的 DOM Document 物件 document = builder.parse(file); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // 獲得文件根元素對物件; Element root = document.getDocumentElement(); // 獲得文件根元素下一級子元素所有元素; NodeList nodeList = root.getChildNodes(); System.out.print("<employees>"); System.out.println(root.getNodeName()); if (null != root) { for (int i = 0; i < nodeList.getLength(); i++) { Node child = nodeList.item(i); // 輸出child的屬性; System.out.print("<test>"); System.out.println(child); if (child.getNodeType() == Node.ELEMENT_NODE) { System.out.print("<id>"); System.out.println(child.getAttributes().getNamedItem("id").getNodeValue()); } for (Node node = child.getFirstChild(); node != null; node = node.getNextSibling()) { if (node.getNodeType() == Node.ELEMENT_NODE) { if ("name".equals(node.getNodeName())) { System.out.print("<name>"); System.out.println(node.getFirstChild().getNodeValue()); } } if (node.getNodeType() == Node.ELEMENT_NODE) { if ("sex".equals(node.getNodeName())) { System.out.print("<sex>"); System.out.println(node.getFirstChild().getNodeValue()); } } if (node.getNodeType() == Node.ELEMENT_NODE) { if ("age".equals(node.getNodeName())) { System.out.print("<age>"); System.out.println(node.getFirstChild().getNodeValue()); } } if (node.getNodeType() == Node.ELEMENT_NODE) { if ("email".equals(node.getNodeName())) { System.out.print("<email>"); System.out.println(node.getFirstChild().getNodeValue()); } } } } } System.out.println("解析完畢"); } /** * 測試 */ public static void main(String[] args) { // 為什麼有類似於這樣東西[#text:] // 原因是XML檔案元素之間的空白字元也是一個元素,<employees></employees>包含的空白 DomDemo dom = new DomDemo(); File file = new File("E://dom.xml"); dom.createXml(file); dom.parserXml(file); } }
2.SAX 解析XML文件
為解決DOM的問題,出現了SAX。SAX ,事件驅動。當解析器發現元素開始、元素結束、文字、文件的開始或結束等時,傳送事件,程式設計師編寫響應這些事件的程式碼,儲存資料。優點:不用事先調入整個文件,佔用資源少;SAX解析器程式碼比DOM解析器程式碼小,適於Applet,下載。缺點:不是持久的;事件過後,若沒儲存資料,那麼資料就丟了;無狀態性;從事件中只能得到文字,但不知該文字屬於哪個元素;使用場合:Applet;只需XML文件的少量內容,很少回頭訪問;機器記憶體少。
package cn.main.sax;
import java.io.File;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* SAX解析XML文件
*
* startDocument(),endDocument(),startElement(),endElement(),characters()
*
* @author wanglp 2012-2-21
*/
public class SAXParseDemo extends DefaultHandler {
private String tagValue; // 標籤值
// 開始解析XML檔案
public void startDocument() throws SAXException {
System.out.println("開始解析");
}
// 結束解析XML檔案
public void endDocument() throws SAXException {
System.out.println("結束解析");
}
// 解析元素
/**
* 開始解析一個元素
* @param qName 標籤名
* @param attributes 屬性
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
System.out.println(qName + "開始");
// 屬性
if (attributes != null && attributes.getLength() != 0) {
System.out.println("屬性:");
for (int i = 0; i < attributes.getLength(); i++) {
System.out.print(attributes.getQName(i) + "="); // 屬性名
System.out.print(attributes.getValue(i) + " "); // 屬性值
}
System.out.println();
}
}
/**
* 結束一個元素的解析 遇到結束標籤時呼叫此方法 通常在此方法對標籤取值並處理
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.println(qName + "標籤值:" + tagValue);
System.out.println(qName + "結束");
}
// 所有xml檔案中的字元都會放到ch[]中
public void characters(char ch[], int start, int length) throws SAXException {
tagValue = new String(ch, start, length).trim();
}
public static void main(String[] args) {
File file = new File("src/cn/main/example/demo.xml");
SAXParserFactory saxParFac = SAXParserFactory.newInstance();
try {
SAXParser saxParser = saxParFac.newSAXParser();
saxParser.parse(file, new SAXParseDemo());
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.JDOM生成和解析XML
要實現的功能簡單,如解析、建立等,但在底層,JDOM還是使用SAX(最常用)、DOM、Xanan文件。匯入jar包:jdom.jar
package cn.main.jdom;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import org.jdom.Attribute;
import org.jdom.Comment;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
/**
*
* jdom生成與解析XML文件
*
* @author wanglp 2012-2-23
*/
public class JdomDemo {
Document document = new Document();
/**
* 利用JDom進行xml文件的寫入操作
*/
public void createXml(File file) {
// 1.建立元素 及 設定為根元素
Element employees = new Element("employees");
document.setContent(employees);
// 2.建立註釋 及 設定到根元素上
Comment commet = new Comment("this is my comment");
employees.addContent(commet);
// 3.建立元素
Element element1 = new Element("employee");
// 3.1 設定元素的屬性名及屬性值
element1.setAttribute(new Attribute("id", "0001"));
// 3.2 建立元素的屬性名及屬性值
Attribute nameAttr = new Attribute("name", "wanglp");
// 3.3 設定元素名及文字
Element sexEle = new Element("sex");
sexEle.setText("m");
// 設定到上層元素上
element1.addContent(sexEle);
// 設定元素
Element ageEle = new Element("age");
ageEle.setText("22");
element1.addContent(ageEle);
// 設定為根元素的子元素
employees.addContent(element1);
// 將元素屬性設定到元素上
element1.setAttribute(nameAttr);
// 3.建立元素
Element element2 = new Element("employee");
// 3.1 設定元素的屬性名及屬性值
element2.setAttribute(new Attribute("id", "0002"));
// 3.2 建立元素的屬性名及屬性值
Attribute name2Attr = new Attribute("name", "fox");
// 3.3 設定元素名及文字
Element sex2Ele = new Element("sex");
sex2Ele.setText("f");
// 設定到上層元素上
element2.addContent(sex2Ele);
// 設定元素
Element age2Ele = new Element("age");
age2Ele.setText("21");
element2.addContent(age2Ele);
// 設定為根元素的子元素
employees.addContent(element2);
// 將元素屬性設定到元素上
element2.setAttribute(name2Attr);
Element element3 = new Element("employee");
element3.setText("title");
element3.addContent(new Element("name").addContent(new Element("hello")));
employees.addContent(element3);
// 設定xml文件輸出的格式
Format format = Format.getPrettyFormat();
XMLOutputter out = new XMLOutputter(format);
// 將得到的xml文件輸出到檔案流中
try {
out.output(document, new FileOutputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 利用JDom進行xml文件的讀取操作
*/
public void parserXml(File file) {
// 建立解析器
SAXBuilder builder = new SAXBuilder();
try {
// 將解析器與文件關聯
document = builder.build(file);
} catch (JDOMException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
// 讀取根元素
Element root = document.getRootElement();
// 輸出根元素的名字
System.out.println("<" + root.getName() + ">");
// 讀取元素集合
List<?> employeeList = root.getChildren("employee");
for (int i = 0; i < employeeList.size(); i++) {
Element ele = (Element) employeeList.get(i);
// 得到元素的名字
System.out.println("<" + ele.getName() + ">");
// 讀取元素的屬性集合
List<?> empAttrList = ele.getAttributes();
for (int j = 0; j < empAttrList.size(); j++) {
Attribute attrs = (Attribute) empAttrList.get(j);
// 將屬性的名字和值 並 輸出
String name = attrs.getName();
String value = (String) attrs.getValue();
System.out.println(name + "=" + value);
}
try {
Element sex = ele.getChild("sex");
System.out.println("<sex>" + sex.getText());
Element age = ele.getChild("age");
System.out.println("<age>" + age.getText());
} catch (NullPointerException e) {
System.out.println(ele.getTextTrim());
Element name = ele.getChild("name");
System.out.println("<name>" + name.getName());
}
System.out.println("</employee>");
}
System.out.println("</employees>");
}
/**
* 測試
*/
public static void main(String[] args) {
JdomDemo jdom = new JdomDemo();
File file = new File("E://jdom.xml");
jdom.createXml(file);
jdom.parserXml(file);
}
}
4.DOM4J生成和解析XML文件DOM4J 是一個非常非常優秀的Java XML API,具有效能優異、功能強大和極端易用使用的特點,同時它也是一個開放原始碼的軟體。如今你可以看到越來越多的 Java 軟體都在使用 DOM4J 來讀寫 XML,特別值得一提的是連 Sun 的 JAXM 也在用 DOM4J。
匯入jar包:dom4j-1.6.1.jar
package cn.main.dom4j;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
/**
*
* dom4j生成與解析XML文件
*
* @author wanglp 2012-2-23
*/
public class Dom4jDemo {
/**
* 利用dom4j進行xml文件的寫入操作
*/
public void createXml(File file) {
// XML 宣告 <?xml version="1.0" encoding="UTF-8"?> 自動新增到 XML文件中
// 使用DocumentHelper類建立文件例項(生成 XML文件節點的 dom4j API工廠類)
Document document = DocumentHelper.createDocument();
// 使用addElement()方法建立根元素 employees(用於向 XML 文件中增加元素)
Element root = document.addElement("employees");
// 在根元素中使用 addComment()方法添加註釋"An XML Note"
root.addComment("An XML Note");
// 在根元素中使用 addProcessingInstruction()方法增加一個處理指令
root.addProcessingInstruction("target", "text");
// 在根元素中使用 addElement()方法增加employee元素。
Element empElem = root.addElement("employee");
// 使用 addAttribute()方法向employee元素新增id和name屬性
empElem.addAttribute("id", "0001");
empElem.addAttribute("name", "wanglp");
// 向employee元素中新增sex元素
Element sexElem = empElem.addElement("sex");
// 使用setText()方法設定sex元素的文字
sexElem.setText("m");
// 在employee元素中增加age元素 並設定該元素的文字。
Element ageElem = empElem.addElement("age");
ageElem.setText("25");
// 在根元素中使用 addElement()方法增加employee元素。
Element emp2Elem = root.addElement("employee");
// 使用 addAttribute()方法向employee元素新增id和name屬性
emp2Elem.addAttribute("id", "0002");
emp2Elem.addAttribute("name", "fox");
// 向employee元素中新增sex元素
Element sex2Elem = emp2Elem.addElement("sex");
// 使用setText()方法設定sex元素的文字
sex2Elem.setText("f");
// 在employee元素中增加age元素 並設定該元素的文字。
Element age2Elem = emp2Elem.addElement("age");
age2Elem.setText("24");
// 可以使用 addDocType()方法新增文件型別說明。
// document.addDocType("employees", null, "file://E:/Dtds/dom4j.dtd");
// 這樣就向 XML 文件中增加文件型別說明:
// <!DOCTYPE employees SYSTEM "file://E:/Dtds/dom4j.dtd">
// 如果文件要使用文件型別定義(DTD)文件驗證則必須有 Doctype。
try {
XMLWriter output = new XMLWriter(new FileWriter(file));
output.write(document);
output.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
/**
* 利用dom4j進行xml文件的讀取操作
*/
public void parserXml(File file) {
Document document = null;
// 使用 SAXReader 解析 XML 文件 catalog.xml:
SAXReader saxReader = new SAXReader();
try {
document = saxReader.read(file);
} catch (DocumentException e) {
e.printStackTrace();
}
// 將字串轉為XML
// document = DocumentHelper.parseText(fileString);
// 獲取根節點
Element root = document.getRootElement();
// 列印節點名稱
System.out.println("<" + root.getName() + ">");
// 獲取根節點下的子節點遍歷
Iterator<?> iter = root.elementIterator("employee");
// 遍歷employee節點
while (iter.hasNext()) {
// 獲取當前子節點
Element empEle = (Element) iter.next();
System.out.println("<" + empEle.getName() + ">");
// 獲取當前子節點的屬性遍歷
Iterator<?> attrList = empEle.attributeIterator();
while (attrList.hasNext()) {
Attribute attr = (Attribute) attrList.next();
System.out.println(attr.getName() + "=" + attr.getValue());
}
// 遍歷employee節點下所有子節點
Iterator<?> eleIte = empEle.elementIterator();
while (eleIte.hasNext()) {
Element ele = (Element) eleIte.next();
System.out.println("<" + ele.getName() + ">" + ele.getTextTrim());
}
// 獲取employee節點下的子節點sex值
// String sex = empEle.elementTextTrim("sex");
// System.out.println("sex:" + sex);
}
System.out.println("</" + root.getName() + ">");
}
public static void main(String[] args) {
Dom4jDemo dom4j = new Dom4jDemo();
File file = new File("e:/dom4j.xml");
// dom4j.createXml(file);
dom4j.parserXml(file);
}
}