XML解析技術
DOM技術:文檔對象模型,需要將整個XML加入內存才能解析,占用內存比較多
SAX技術:一邊加載,一邊解析,一邊釋放內存,比較節省內存,基於推模式
STAX技術:一種至於流的技術,和SAX技術很像,是基於拉模式的
推模式與拉模式
推模式就好像服務器主動給你發送消息一樣,當使用SAX技術時,xml解析器碰到一個標簽就會觸發一個事件,而且一旦開始解析就不會停下來,知道所有內容解析完畢,不能人為的控制解析過程。
拉模式類似客戶端發起HTTP請求后服務器才發送內容,使用STAX技術時,會碰到一個標簽解析一下然后停止解析,如果還需要解析的話必須人為的啟動解析,就好像解析器開啟懶人模式一樣。
基於以上技術實現的解析工具有
JAXP 同時支持DOM SAX STAX三種技術
DOM4J 支持DOM解析方式
XML PULL android移動設備內置的xml解析技術, 支持STAX解析方式
技術選擇
在javaee開發中通常使用DOM技術,編程簡單。當xml文檔過於大時,優先使用SAX/STAX技術。
JAXP解析代碼
JAXP(Java API for XML Processing)提供了DOM和SAX的接口,因此可用其對XML文件進行解析。以下面的XML文件為例,說明JAXP的使用方式。
XML文件
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book>
<name>adfafd</name>
<price>123</price>
</book>
<book>
<name>dfadfe</name>
<price>2321</price>
</book>
</books>
DOM解析API
全局查找
通過ID查找 getElementById() //無約束的DTD文檔不能使用該方法
通過標簽名查找 getElementsByTagName()
相對節點位置查找
getChildNodes(): 返回這個節點的所有子節點列表
getFirstChild(): 返回這個節點的第一個子節點
getParentNode(): 返回這個節點的父節點對象
getPreviousSibling(): 返回其前一個兄弟節點
getNextSibling(): 返回該節點下一個兄弟節點
DOM讀取XML文件
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
public class DOMTest {
@Test
public void demo1() throws ParserConfigurationException, SAXException,IOException {
// 構造工廠
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
// 通過工廠獲得解析器
DocumentBuilder builder = builderFactory.newDocumentBuilder();
// 使用解析器加載xml文檔
Document document = builder.parse("books.xml");
// 通過標簽名獲得元素
NodeList nodeList = document.getElementsByTagName("name");
for (int i = 0; i < nodeList.getLength(); i++) {
Element e = (Element) nodeList.item(i);
// 獲取標簽名稱
System.out.println(e.getNodeName());
// 獲取<name>標簽子節點
System.out.println(e.getFirstChild().getNodeValue());
// 獲取<name>標簽類型
System.out.println(e.getNodeType());
}
}
}
DOM的增加節點操作
public void DOM2Test() throws ParserConfigurationException, SAXException,
IOException, TransformerException,
TransformerConfigurationException {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
// 通過工廠獲得解析器
DocumentBuilder builder = builderFactory.newDocumentBuilder();
// 使用解析器加載xml文檔
Document document = builder.parse("books.xml");
// 創建元素節點
Element time = document.createElement("time");
// 設置節點包含的文本內容
time.setTextContent("2015-12-28");
// 獲取欲加入位置
NodeList nodeList = document.getElementsByTagName("name");
//將節點<time>加入到<name>下
nodeList.item(0).appendChild(time);
// 將新的DOM對象重新寫會原文件
TransformerFactory transformerFactory = TransformerFactory
.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(document);
StreamResult streamResult = new StreamResult(new File("books.xml"));
transformer.transform(domSource, streamResult);
}
DOM修改節點
public void DOM2Test() throws ParserConfigurationException, SAXException,
IOException, TransformerException,TransformerConfigurationException {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
// 通過工廠獲得解析器
DocumentBuilder builder = builderFactory.newDocumentBuilder();
// 使用解析器加載xml文檔
Document document = builder.parse("books.xml");
// 獲取欲修改位置
NodeList nodeList = document.getElementsByTagName("name");
int nodeListLength = nodeList.getLength();
for (int i = 0; i < nodeListLength; i++) {
Element e = (Element) nodeList.item(i);
if (e.getTextContent().equals("java編程思想")) {
String oldPrice = e.getNextSibling().getTextContent();
int newPrice = (int) (Integer.parseInt(oldPrice) * 1.5);
e.getNextSibling().setTextContent(String.valueOf(newPrice));
}
}
// 將新的DOM對象重新寫會原文件
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(document);
StreamResult streamResult = new StreamResult(new File("books.xml"));
transformer.transform(domSource, streamResult);
}
DOM的刪除操作
public void DOM2Test() throws ParserConfigurationException, SAXException,
IOException, TransformerException,TransformerConfigurationException {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
// 通過工廠獲得解析器
DocumentBuilder builder = builderFactory.newDocumentBuilder();
// 使用解析器加載xml文檔
Document document = builder.parse("books.xml");
// 獲取欲修改位置
NodeList nodeList = document.getElementsByTagName("name");
int nodeListLength = nodeList.getLength();
for (int i = 0; i < nodeListLength; i++) {
Element e = (Element) nodeList.item(i);
System.out.println(e.getTextContent());
if (e.getTextContent().equals("java編程思想")) {
e.getParentNode().removeChild(e);
//<name>節點被刪除后,<price>節點成為第一個節點,所以要減1
i--;
}
}
// 將新的DOM對象重新寫會原文件
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(document);
StreamResult streamResult = new StreamResult(new File("books.xml"));
transformer.transform(domSource, streamResult);
}
}
SAX
SAX是基於事件驅動的XML處理方法,當SAX解析器標簽的起始標記時會回調starElement()方法,比如遇到了
標簽就會回調該方法,endElement()函數是遇到結束標簽時調用該方法。
SAX測試代碼:
首先需要復寫DefaultHandler類
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class MyHandler extends DefaultHandler {
@Override
public void startDocument() throws SAXException {
System.out.println("start Document...");
}
@Override
public void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {
System.out.println(qName);
if (qName.equals("book"))
//標簽的屬性以鍵值對方式存儲
System.out.println("id屬性為:" + attributes.getValue("id"));
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String string = new String(ch, start, length);
System.out.println("start character..." + string);
}
@Override
public void endElement(String uri, String localName, String qName)throws SAXException {
System.out.println("end element..." + qName);
}
@Override
public void endDocument() throws SAXException {
System.out.println("end Document...");
}
}
構建解析器進行解析,解析器在進行解析時,會調用Handler中的方法進行處理
public void SAXTest() throws ParserConfigurationException, SAXException,IOException {
// 創建解析工廠
SAXParserFactory factory = SAXParserFactory.newInstance();
// 創建解析器
SAXParser saxParser = factory.newSAXParser();
// 初始化回調函數
MyHandler handler = new MyHandler();
//將解析的文檔和回調函數一並傳入
saxParser.parse("books.xml", handler);
}
STAX
STAX模式在Android中使用,因此下面的代碼只能在Android環境下運行
STAX測試代碼:
測試文檔為上面的books.xml
@Test
public void TestPull() throws XmlPullParserException, IOException {
// 創建工廠
XmlPullParserFactory pullParserFactory = XmlPullParserFactory.newInstance();
// 通過工廠獲得解析器
XmlPullParser pullParser = pullParserFactory.newPullParser();
// 將XML文檔傳入
pullParser.setInput(new FileInputStream("books.xml"), "utf-8");
// 獲取事件類型
while (pullParser.getEventType() != XmlPullParser.END_DOCUMENT) {
if (pullParser.getEventType() == XmlPullParser.START_TAG) {
if (pullParser.getName().equals("name"))
System.out.println(pullParser.nextText());
}
pullParser.next();
}
}
使用XML PULL生成XML文檔
public void SerializerTest() throws XmlPullParserException,
IllegalArgumentException, IllegalStateException,
FileNotFoundException, IOException {
XmlPullParserFactory pullParserFactory = XmlPullParserFactory.newInstance();
XmlSerializer serializer = pullParserFactory.newSerializer();
// 設置序列化文檔
serializer.setOutput(new FileOutputStream("books_blank.xml"), "utf-8");
serializer.startDocument("utf-8", true);
serializer.startTag(null, "admin");
serializer.startTag(null, "username");
serializer.text("root");
serializer.endTag(null, "username");
serializer.endTag(null, "admin");
serializer.endDocument();
}
總結
由於DOM是將整個XML裝載進內存,因此可以直接在內存直接操作XML文件。由於SAX和STAX技術對XML解析一部分后直接釋放內存,再將下一部分裝入內存,所以我們無法使用SAX或STAX來直接修改XML文檔,可以先用SAX解析后轉換成多個對象(用ArrayList存儲),然后這些對象進行修改,在用序列化方式,寫到文本文件中去。因此DOM多用於寫場景,而SAX/STAX多用於讀場景。
