天天看點

XML解析---DOM解析和SAX解析

   XML是一種通用的資料交換格式,它的平台無關性、語言無關性、系統無關性、給資料內建與互動帶來了極大的友善。XML在不同的語言環境中解析方式都是一樣的,隻不過實作的文法不同而已。

   對于XML的解析,還有一些友善的XML解析工具,如JAXB、XStream這樣的解析工具,它們利用注解、反射等非常友善地幫助我們實作從XML解析為Java對象,還有如Dom4j,它是一個易用的、開源的庫,特别是對進行讀取和修改XML檔案内容提供了豐富的API接口,易操作、高效率,但是它對于XML内容和JAVA對象之間的映射轉換卻不太如JAXB和XStream。

一、DOM解析

  DOM的全稱是Document Object Model,也即文檔對象模型。在應用程式中,基于DOM的XML分析器将一個XML文檔轉換成一個對象模型的集合(通常稱DOM樹),應用程式正是通過對這個對象模型的操作,來實作對XML文檔資料的操作。通過DOM接口,應用程式可以在任何時候通路XML文檔中的任何一部分資料,是以,這種利用DOM接口的機制也被稱作随機通路機制。

  DOM接口提供了一種通過分層對象模型來通路XML文檔資訊的方式,這些分層對象模型依據XML的文檔結構形成了一棵節點樹。無論XML文檔中所描述的是什麼類型的資訊,即便是制表資料、項目清單或一個文檔,利用DOM所生成的模型都是節點樹的形式。也就是說,DOM強制使用樹模型來通路XML文檔中的資訊。由于XML本質上就是一種分層結構,是以這種描述方法是相當有效的。

  DOM樹所提供的随機通路方式給應用程式的開發帶來了很大的靈活性,它可以任意地控制整個XML文檔中的内容。然而,由于DOM分析器把整個XML文檔轉化成DOM樹放在了記憶體中,是以,當文檔比較大或者結構比較複雜時,對記憶體的需求就比較高。而且,對于結構複雜的樹的周遊也是一項耗時的操作。是以,DOM分析器對機器性能的要求比較高,實作效率不十分理想。不過,由于DOM分析器所采用的樹結構的思想與XML文檔的結構相吻合,同時鑒于随機通路所帶來的友善,是以,DOM分析器還是有很廣泛的使用價值的。

    優點:

      1、形成了樹結構,有助于更好的了解、掌握,且代碼容易編寫。

      2、解析過程中,樹結構儲存在記憶體中,友善修改。

    缺點:

      1、由于檔案是一次性讀取,是以對記憶體的耗費比較大。

      2、如果XML檔案比較大,容易影響解析性能且可能會造成記憶體溢出。

  DOM解析示例:

項目目錄結構

XML解析---DOM解析和SAX解析

  1.DOM解析

  TestXML.java檔案

import java.io.FileReader;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

//采用DOM進行解析
//DOM的讀取時是将XML整個檔案裝載成DOM樹,即使文檔中有不需要的資訊時也會被裝載進來,在大多數的時候DOM是非常友善好用的。
public class TestXMl {
	public static void main(String[] args) {
		// 文檔建構器的工廠
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		// 得到XML文檔建構器
		try {
			DocumentBuilder db = dbf.newDocumentBuilder();
			// 1.得到XML文檔對象
			// 當test.xml放在本項目的根目錄時(和src bin同一目錄下時)
			// db.parse("test.xml");
			// 當test.xml放在bin目錄下時
			String path = FileReader.class.getResource("/").getFile();
			/// System.out.println(path);
			// 導入的是org.w3c.dom.Document;
			Document doc = db.parse(path + "test.xml");
			// 得到文檔根節點
			Element root = doc.getDocumentElement();
			String rootName = root.getNodeName();
			System.out.println(rootName);
			NodeList list = doc.getElementsByTagName("People");
			// 2.擷取XML中的節點
			System.out.println("目前有幾個People:" + list.getLength());

			// 3.得到節點裡面的有用資訊
			for (int i = 0; i < list.getLength(); i++) {
				Element peopleElement = (Element) list.item(i);
				String id = peopleElement.getAttribute("id"); // 擷取id的屬性值
				System.out.println("id:" + id);
				// 周遊目前元素的子節點
				NodeList childNodeList = peopleElement.getChildNodes();
				// test.xml中一個People元素下有Name和Age兩個标簽,但是它把回車也算作一個節點,是以列印出來是五個
				// 第一個是沒有回車的,第二個有
				System.out.println("子節點個數:" + childNodeList.getLength());

				// 判斷這些子節點中的元素是不是有效的元素,即不為回車
				for (int j = 0; j < childNodeList.getLength(); j++) {
					// 判斷是不是元素,有标簽的,若是元素才把它轉換為元素
					if (childNodeList.item(j) instanceof Element) {
						Element cElement = (Element) childNodeList.item(j);
						String tagName = cElement.getTagName();
						String content = cElement.getTextContent();
						System.out.println(tagName + ":" + content);
					}
				}
			}

		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}
           

2.往XML檔案中寫入的是Document對象

  Write.java檔案

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
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.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

//由對象到檔案,把document對象儲存到檔案中
//XML是磁盤檔案,DOM是記憶體對象。
//往XML檔案中寫入的是Document對象,是以要編輯XML檔案,實際上編輯的是Document文檔對象。
public class WriteXML {
    public static void main(String[] args) {
    	//建立一個document,通過builder建立
    	DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    	try {
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document doc = db.newDocument();
			
			//通過document建立節點Element
			Element stuEle = doc.createElement("Student");
			stuEle.setAttribute("stuNo","S1001");
			//給stuEle添加子節點
			Element nameEle = doc.createElement("Name");
			//方式一:建立一個文本節點
			/*Text txtName = doc.createTextNode("zhangsan");
			nameEle.appendChild(txtName);*/
			//方式二:直接添加文本
			nameEle.setTextContent("zhangsan");
			Element ageEle = doc.createElement("Age");
			ageEle.setTextContent("20");
			stuEle.appendChild(nameEle);
			stuEle.appendChild(ageEle);
			
	    	//将Element對象添加到document中
			doc.appendChild(stuEle);
			
	    	//将document對象儲存到硬碟檔案xml中
	    	//通過transformer進行儲存
			TransformerFactory tf = TransformerFactory.newInstance();
			Transformer former = tf.newTransformer();
			former.transform(new DOMSource(doc), new StreamResult("stu.xml"));
			System.out.println("生成xml成功...");
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TransformerConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TransformerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
    	
    }
}
           

  test.xml

<?xml version="1.0" encoding="UTF-8"?>
<PeopleList>
   <People id="1">
      <Name en="zhangsan">張三</Name>
      <Age>20</Age>
	  <Address>蘇州</Address>
   </People>
   <People id="2">
      <Name en="lisi">李四</Name>
      <Age>39</Age>
	  <Address>常州</Address>
    </People>
	 <People id="3">
      <Name en="wangwu">王五</Name>
      <Age>14</Age>
	  <Address>楊州</Address>
    </People>
	 <People id="4">
      <Name en="zhaowu">趙武</Name>
      <Age>31</Age>
	  <Address>貴州</Address>
    </People>
	 <People id="5">
      <Name en="sunliu">孫劉</Name>
      <Age>26</Age>
	  <Address>廣州</Address>
    </People>
</PeopleList>
           

二、SAX解析

  SAX的全稱是Simple APIs for XML,也即XML簡單應用程式接口。與DOM不同,SAX提供的通路模式是一種順序模式,這是一種快速讀寫XML資料的方式。當使用SAX分析器對XML文檔進行分析時,會觸發一系列事件,并激活相應的事件處理函數,應用程式通過這些事件處理函數實作對XML文檔的通路,因而SAX接口也被稱作事件驅動接口。

    優點:

      1、采用事件驅動模式,對記憶體耗費比較小。

      2、适用于隻處理XML檔案中的資料時。

    缺點:

      1、編碼比較麻煩。

      2、很難同時通路XML檔案中的多處不同資料。

SAX解析示例

      XMLHandler.java

import java.util.ArrayList;
import java.util.List;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class XMLHandler extends DefaultHandler {
	
	private String value; //讀取到的文本
	private People people; //臨時存放的一個人
	private List<People> peopleList;

	
	public List<People> getPeopleList() {
		return peopleList;
	}

	

	//當讀到文檔開始處觸發事件,該方法隻執行一次
	@Override
	public void startDocument() throws SAXException {
		// TODO Auto-generated method stub
		super.startDocument();
		System.out.println("開始執行startDocument,開始讀取xml文檔");
		peopleList = new ArrayList<People>();
	}
    
	//當讀到文檔結束處觸發事件,該方法隻執行一次
	@Override
	public void endDocument() throws SAXException {
		// TODO Auto-generated method stub
		super.endDocument();
		System.out.println("開始執行endDocument,結束讀取xml文檔");
	}
    
    //當讀到元素開始處觸發事件
	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		// TODO Auto-generated method stub
		super.startElement(uri, localName, qName, attributes);
		System.out.println("開始執行startElement,開始讀取xml文檔的标簽,該标簽為:"+qName);
		if("People".equals(qName)) {
			people = new People();
			System.out.println("目前people有屬性"+attributes.getLength());
			for(int i = 0;i<attributes.getLength();i++) {
				//擷取目前屬性名
				if("id".equals(attributes.getQName(i))) {
					people.setId(attributes.getValue(i));
				}
			}
		}
	}
    
	//當讀到元素結束處觸發事件
	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		// TODO Auto-generated method stub
		super.endElement(uri, localName, qName);
		System.out.println("開始執行endElement,結束讀取xml文檔的标簽,該标簽為:"+qName);
		if("People".equals(qName)) {
			peopleList.add(people);
			people = null;
		}else if("Name".equals(qName)) {  //讀到Name的結束标簽
			people.setName(value);
		}else if("Age".equals(qName)) {
			people.setAge(value);
		}
	}
    
	//讀到内容時觸發事件
	@Override
	public void characters(char[] ch, int start, int length) throws SAXException {
		// TODO Auto-generated method stub
		super.characters(ch, start, length);
		value = new String(ch,start,length);
		System.out.println("目前讀到的文本是:"+value);
	}
  
}
           

   ReadXMLBySAX.java

import java.io.IOException;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;

//SAX解析
//流讀取的機制(SAX),Document的讀取在内部也是使用流的機制去讀的。
//流讀取的機制是隻負責讀取,不進行存儲,但是會給出一定的關鍵的事件,提示你進行相應的儲存操作。
public class ReadXMLBySAX {
    public static void main(String[] args) {
    	//得到saxParser
    	SAXParserFactory  factory = SAXParserFactory.newInstance();
    	try {
			SAXParser parser = factory.newSAXParser();
			XMLHandler handler = new XMLHandler();
			//通過saxParse接卸器的parse方法,解析xml,parse需要一個處理器,handler
			parser.parse("test.xml", handler);
			List<People> list = handler.getPeopleList();
			System.out.println("一共讀取倒了"+list.size()+"個人");
			for(People people : list) {
				System.out.println(people);
			}
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
    }
}
           

   People.java

public class People {
	private String id;
	private String name;
	private String age;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAge() {
		return age;
	}

	public void setAge(String age) {
		this.age = age;
	}

	public People(String id, String name, String age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}

	public People() {
		super();
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "Student[id="+id+",name="+name+",age="+age+"]";
	}
	
	

}