XML解析技術


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多用於讀場景。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM