DOM解析XML文件:
1.javax.xml.parsers 包中的DocumentBuilderFactory用於創建DOM模式的解析器對象 , DocumentBuilderFactory是一個抽象工廠類,它不能直接實例化,但該類提供了一個newInstance方法 ,這個方法會根據本地平臺默認安裝的解析器,自動創建一個工廠的對象並返回
2.調用 DocumentBuilderFactory.newInstance() 方法得到創建 DOM 解析器的工廠。
3.調用工廠對象的 newDocumentBuilder方法得到 DOM 解析器對象。
4.調用 DOM 解析器對象的 parse() 方法解析 XML 文檔,得到代表整個文檔的 Document 對象,進行可以利用DOM特性對整個XML文檔進行操作了。
案例1.遍歷xml文件中跟節點下面的所有子節點.
1.xml的約束文件java.dtd
- <!ELEMENT classes (java班,net班,php班,ios班)>
- <!ELEMENT java班 (teachers?,students?)>
- <!ELEMENT net班 (teachers?,students?)>
- <!ELEMENT php班 (teachers?,students?)>
- <!ELEMENT ios班 (teachers?,students?)>
- <!ELEMENT teachers (teacher*)>
- <!ELEMENT teacher EMPTY>
- <!ELEMENT students (student*)>
- <!ELEMENT student (name,sex,age)>
- <!ATTLIST java班 name CDATA #IMPLIED>
- <!ATTLIST net班 name CDATA #IMPLIED>
- <!ATTLIST php班 name CDATA #IMPLIED>
- <!ATTLIST ios班 name CDATA #IMPLIED>
- <!ATTLIST teacher name CDATA #IMPLIED>
- <!ATTLIST teacher sex CDATA #IMPLIED>
- <!ATTLIST teacher age CDATA #IMPLIED>
- <!ELEMENT name (#PCDATA)>
- <!ELEMENT sex (#PCDATA)>
- <!ELEMENT age (#PCDATA)>
- <!ATTLIST student id ID #IMPLIED>
2.xml文件內容如下java.xml
- <?xml version="1.0" encoding="UTF-8" ?>
- <!DOCTYPE classes SYSTEM "bin//parsers//java.dtd">
- <classes>
- <java班 name="CSDNJava01班">
- <teachers>
- <teacher name="軍哥" sex="男" age="28" />
- <teacher name="劉麗華" sex="女" age="28" />
- </teachers>
- <students>
- <student id="x121">
- <name>王亮</name>
- <sex>女</sex>
- <age>28</age>
- </student>
- </students>
- </java班>
- <!-- 註釋0 -->
- <net班 name="CSDNNet01班">xxx</net班>
- <php班 name="CSDNPhp01班"></php班>
- <ios班 name="CSDNIos01班"></ios班>
- </classes>
- <!-- 對java.xml文件進行CRUD的操作 -->
- <!-- 節點
- nodeName nodeValue nodeType
- element 標籤名 null 1
- Attr 屬性名 屬性值 2
- text #text 文本的值 3
- -->
//1.獲取XML的根節點對象
- @Test
- public void test() throws ParserConfigurationException, SAXException, IOException{
- //調用 DocumentBuilderFactory.newInstance() 方法得到創建 DOM 解析器的工廠
- DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
- //調用工廠對象的 newDocumentBuilder方法得到 DOM 解析器對象
- DocumentBuilder builder = builderFactory.newDocumentBuilder();
- //通過文件的方式獲取Document對象
- /*File file = new File("src//parsers//java.xml");
- System.out.println(file+"----");
- Document document = builder.parse(file);*/
- //解析指定的文件
- InputStream is= this.getClass().getClassLoader()
- .getResourceAsStream("parsers//java.xml");
- Document document = builder.parse(is);
- //document.getDocumentElement()獲取根節點的元素對象
- Element root = document.getDocumentElement();
- //遍歷根節點下面的所有子節點
- listNodes(root);
- }
//2.遍歷節點對象的方法
- /**
- * 遍歷根據節點對象下面的所有的節點對象
- * @param node
- */
- public void listNodes(Node node) {
- // 節點是什麼類型的節點
- if (node.getNodeType() == Node.ELEMENT_NODE) {// 判斷是否是元素節點
- Element element = (Element) node;
- //判斷此元素節點是否有屬性
- if(element.hasAttributes()){
- //獲取屬性節點的集合
- NamedNodeMap namenm = element.getAttributes();//Node
- //遍歷屬性節點的集合
- for(int k=0;k<namenm.getLength();k++){
- //獲取具體的某個屬性節點
- Attr attr = (Attr) namenm.item(k);
- System.out.println("name:::"+attr.getNodeName()+" value::"
- +attr.getNodeValue()+" type::"+attr.getNodeType());
- }
- }
- //獲取元素節點的所有孩子節點
- NodeList listnode = element.getChildNodes();
- //遍歷
- for (int j = 0; j < listnode.getLength(); j++) {
- //得到某個具體的節點對象
- Node nd = listnode.item(j);
- System.out.println("name::" + nd.getNodeName() + " value:::"
- + nd.getNodeValue() + " type:::" + nd.getNodeType());
- //重新調用遍歷節點的操作的方法
- listNodes(nd);
- }
- }
- }
4.查詢某個節點對象(簡單列舉一些案例)
- /**
- * 根據標籤的名稱查找所有該名稱的節點對象
- */
- public void findNode(Document document) {
- //根據標籤名稱獲取該名稱的所有節點對象
- NodeList nodelist = document.getElementsByTagName("teacher");
- //遍歷
- for (int i = 0; i < nodelist.getLength(); i++) {
- //得到具體的某個節點對象
- Node node = nodelist.item(i);
- System.out.println(node.getNodeName());
- }
- }
- /**
- * 根據屬性的值 查詢某個節點對象
- * 屬性值是唯一(假設)
- * @param document
- * @param value
- * @return
- */
- public Node findNodeByAttrValue(Document document, String value) {
- //根據標籤名稱獲取該名稱的節點對象集合
- NodeList nodelist = document.getElementsByTagName("teacher");
- //遍歷
- for (int i = 0; i < nodelist.getLength(); i++) {
- //獲取某個具體的元素節點對象
- Element node = (Element) nodelist.item(i);
- //根據屬性名稱獲取該節點的屬性節點對象
- Attr attr = node.getAttributeNode("name");
- //獲取屬性節點的值是否給指定的節點屬性值相同
- if (attr.getNodeValue().equals(value)) {
- //返回此節點
- return node;
- }
- }
- return null;
- }
- /**
- * 根據id獲取某個節點對象
- *
- * @param document
- * @param id
- * @return
- */
- public Node findNodeById(Document document, String id) {
- return document.getElementById(id);
- }
5.刪除指定的節點對象
- /**
- * 刪除某個節點對象
- *
- * @param document
- * @param id
- * @throws TransformerException
- */
- public void deleteNodeById(Document document, String id)
- throws TransformerException {
- //獲取刪除的節點對象
- Node node = document.getElementById(id);
- // 是通過父節點調用removeChild(node)把子節點給刪除掉
- Node node1 = node.getParentNode().removeChild(node);
- //創建TransformerFactory對象
- TransformerFactory transformerFactory = TransformerFactory
- .newInstance();
- //Transformer類用於把代表XML文件的Document對象轉換爲某種格式後進行輸出
- //Transformer對象通過TransformerFactory獲得
- Transformer transformer = transformerFactory.newTransformer();
- // 把Document對象又重新寫入到一個XML文件中。
- transformer.transform(new DOMSource(document), new StreamResult(
- new File("src//a.xml")));
- }
6.更新某個節點對象
- /**
- * 更新某個節點
- *
- * @param document
- * @param id
- * @throws TransformerException
- */
- public void updateNodeById(Document document, String id)
- throws TransformerException {
- //根據id獲取元素指定的元素節點對象
- Element node = document.getElementById(id);
- //獲取元素節點的id屬性節點對象
- Attr attr = node.getAttributeNode("id");
- //修改元素節點的屬性值
- attr.setValue("x122");
- //獲取該節點對象的所有孩子節點對象name、age、sex節點
- NodeList nodelist = node.getChildNodes();
- //遍歷
- for (int i = 0; i < nodelist.getLength(); i++) {
- //得到具體的節點對象
- Node n = nodelist.item(i);
- //判斷是否是元素節點對象
- if (n.getNodeType() == Node.ELEMENT_NODE) {
- //看是否是name節點
- if (n.getNodeName().equals("name")) {
- n.setTextContent("君君");//修改其值
- } else if (n.getNodeName().equals("age")) {//看看是否是age節點
- n.setTextContent("80");//修改其值
- } else if (n.getNodeName().equals("sex")) {//看看是否是sex節點
- n.setTextContent("男");//修改其值
- } else {
- System.out.println("不做處理");
- }
- }
- }
- //創建TransformerFactory對象
- TransformerFactory transformerFactory = TransformerFactory
- .newInstance();
- //Transformer類用於把代表XML文件的Document對象轉換爲某種格式後進行輸出
- //Transformer對象通過TransformerFactory獲得
- Transformer transformer = transformerFactory.newTransformer();
- //把Document對象又重新寫入到一個XML文件中。
- transformer.transform(new DOMSource(document), new StreamResult(
- new File("src//b.xml")));
- }
7.在某個節點的下方添加新的節點
- /**
- * 在指定的節點下方添加新得某個節點
- *
- * @param document
- * @param id
- * @throws TransformerException
- */
- public void addNodeById(Document document, String id)
- throws TransformerException {
- //獲取要添加位置節點的兄弟節點對象
- Element node = document.getElementById(id);
- //獲取其父節點對象
- Node parentNode = node.getParentNode();
- //創建元素節點
- Element nd = document.createElement("student");
- //設置元素節點的屬性值
- nd.setAttribute("id", "x123");
- //創建name元素節點
- Node name = document.createElement("name");
- //設置name節點的文本值
- name.appendChild(document.createTextNode("陳紅軍"));
- //創建age元素節點
- Node age = document.createElement("age");
- //設置age節點的文本值
- age.appendChild(document.createTextNode("20"));
- //創建sex元素節點
- Node sex = document.createElement("sex");
- //設置sex節點的文本值
- sex.appendChild(document.createTextNode("男"));
- //在nd節點中添加3個子節點
- nd.appendChild(name);
- nd.appendChild(age);
- nd.appendChild(sex);
- //在父節點中添加nd節點
- parentNode.appendChild(nd);
- //創建TransformerFactory對象
- TransformerFactory transformerFactory = TransformerFactory
- .newInstance();
- //Transformer類用於把代表XML文件的Document對象轉換爲某種格式後進行輸出
- //Transformer對象通過TransformerFactory獲得
- Transformer transformer = transformerFactory.newTransformer();
- //把Document對象又重新寫入到一個XML文件中。
- transformer.transform(new DOMSource(document), new StreamResult(
- new File("src//c.xml")));
- }
SAX解析XML文件:
SAX解析XML文件採用事件驅動的方式進行,也就是說,SAX是逐行掃描文件,遇到符合條件的設定條件後就會觸發特定的事件,回調你寫好的事件處理程序。使用SAX的優勢在於其解析速度較快,相對於DOM而言佔用內存較少。而且SAX在解析文件的過程中得到自己需要的信息後可以隨時終止解析,並不一定要等文件全部解析完畢。凡事有利必有弊,其劣勢在於SAX採用的是流式處理方式,當遇到某個標籤的時候,它並不會記錄下以前所遇到的標籤,也就是說,在處理某個標籤的時候,比如在startElement方法中,所能夠得到的信息就是標籤的名字和屬性,至於標籤內部的嵌套結構,上層標籤、下層標籤以及其兄弟節點的名稱等等與其結構相關的信息都是不得而知的。實際上就是把XML文件的結構信息丟掉了,如果需要得到這些信息的話,只能你自己在程序裏進行處理了。所以相對DOM而言,SAX處理XML文檔沒有DOM方便,SAX處理的過程相對DOM而言也比較複雜。
SAX採用事件處理的方式解析XML文件,利用 SAX 解析 XML 文檔,涉及兩個部分:解析器和事件處理器:
解析器可以使用JAXP的API創建,創建出SAX解析器後,就可以指定解析器去解析某個XML文檔。
解析器採用SAX方式在解析某個XML文檔時,它只要解析到XML文檔的一個組成部分,都會去調用事件處理器的一個方法,解析器在調用事件處理器的方法時,會把當前解析到的xml文件內容作爲方法的參數傳遞給事件處理器。
事件處理器由程序員編寫,程序員通過事件處理器中方法的參數,就可以很輕鬆地得到sax解析器解析到的數據,從而可以決定如何對數據進行處理。
備註說明:SAX API中主要有四種處理事件的接口,它們分別是ContentHandler,DTDHandler, EntityResolver 和 ErrorHandler
這裏使用最多的就是ContentHandler,仔細閱讀 API文檔,瞭解常用方法:startElement、endElement、characters等
1.startElement方法說明
- void startElement(String uri,
- String localName,
- String qName,
- Attributes atts)
- throws SAXException
- 方法說明:
- 解析器在 XML 文檔中的每個元素的開始調用此方法;對於每個 startElement 事件都將有相應的 endElement 事件(即使該元素爲空時)。所有元素的內容都將在相應的 endElement 事件之前順序地報告。
- 參數說明:
- uri - 名稱空間 URI,如果元素沒有名稱空間 URI,或者未執行名稱空間處理,則爲空字符串
- localName - 本地名稱(不帶前綴),如果未執行名稱空間處理,則爲空字符串
- qName - 限定名(帶有前綴),如果限定名不可用,則爲空字符串
- atts - 連接到元素上的屬性。如果沒有屬性,則它將是空 Attributes 對象。在 startElement 返回後,此對象的值是未定義的
- void endElement(String uri,
- String localName,
- String qName)
- throws SAXException接收元素結束的通知。
- SAX 解析器會在 XML 文檔中每個元素的末尾調用此方法;對於每個 endElement 事件都將有相應的 startElement 事件(即使該元素爲空時)。
- 參數:
- uri - 名稱空間 URI,如果元素沒有名稱空間 URI,或者未執行名稱空間處理,則爲空字符串
- localName - 本地名稱(不帶前綴),如果未執行名稱空間處理,則爲空字符串
- qName - 限定的 XML 名稱(帶前綴),如果限定名不可用,則爲空字符串
3.characters方法
- void characters(char[] ch,
- int start,
- int length)
- throws SAXException
- 接收字符數據的通知,可以通過new String(ch,start,length)構造器,創建解析出來的字符串文本.
- 參數:
- ch - 來自 XML 文檔的字符
- start - 數組中的開始位置
- length - 從數組中讀取的字符的個數
其它方法請參考api數據
下面我們就具體講解sax解析的操作.
一.我們通過XMLReaderFactory、XMLReader完成,步驟如下
- 1.通過XMLReaderFactory創建XMLReader對象
- XMLReader reader = XMLReaderFactory.createXMLReader();
- 2. 設置事件處理器對象
- reader.setContentHandler(new MyDefaultHandler());
- 3.讀取要解析的xml文件
- FileReader fileReader =new FileReader(new File("src\\sax\\startelement\\web.xml"));
- 4.指定解析的xml文件
- reader.parse(new InputSource(fileReader));
案例:通過案例對uri、localName、qName和attribute參數有更加深入的瞭解
1.首先創建要解析的web.xml文件,內容如下
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app version="2.5"
- xmlns:csdn="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
- http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
- <csdn:display-name></csdn:display-name>
- </web-app>
- <!--
- uri - 名稱空間 URI,如果元素沒有任何名稱空間 URI,或者沒有正在執行名稱空間處理,則爲空字符串。
- xml namespace-xmlns
- localName - 本地名稱(不帶前綴),如果沒有正在執行名稱空間處理,則爲空字符串。
- qName - 限定的名稱(帶有前綴),如果限定的名稱不可用,則爲空字符串。
- attributes - 附加到元素的屬性。如果沒有屬性,則它將是空的 Attributes 對象。
- -->
2.創建解析測試類及事件處理的內部類代碼如下
- package sax.startelement;
- import java.io.File;
- import java.io.FileReader;
- import org.junit.Test;
- import org.xml.sax.Attributes;
- import org.xml.sax.InputSource;
- import org.xml.sax.SAXException;
- import org.xml.sax.XMLReader;
- import org.xml.sax.helpers.DefaultHandler;
- import org.xml.sax.helpers.XMLReaderFactory;
- public class Demo3 {
- @Test
- public void test() throws Exception {
- // 通過XMLReaderFactory創建XMLReader對象
- XMLReader reader = XMLReaderFactory.createXMLReader();
- // 設置事件處理器對象
- reader.setContentHandler(new MyDefaultHandler());
- // 讀取要解析的xml文件
- FileReader fileReader = new FileReader(new File(
- "src\\sax\\startelement\\web.xml"));
- // 指定解析的xml文件
- reader.parse(new InputSource(fileReader));
- }
- // 自定義的解析類,通過此類中的startElement瞭解uri,localName,qName,Attributes的含義
- class MyDefaultHandler extends DefaultHandler {
- @Override
- public void startElement(String uri, String localName, String qName,
- Attributes attributes) throws SAXException {
- super.startElement(uri, localName, qName, attributes);
- System.out
- .println("--------------startElement開始執行--------------------------");
- System.out.println("uri:::" + uri);
- System.out.println("localName:::" + localName);
- System.out.println("qName:::" + qName);
- for (int i = 0; i < attributes.getLength(); i++) {
- String value = attributes.getValue(i);// 獲取屬性的value值
- System.out.println(attributes.getQName(i) + "-----" + value);
- }
- System.out
- .println("------------------startElement執行完畢---------------------------");
- }
- }
- }
3.程序運行的結果如下:
通過運行結果,希望你對uri,localName,qName有更加深入的瞭解.
二.我們通過SAXParserFactory、SAXParser、XMLReader完成,步驟如下
1.使用SAXParserFactory創建SAX解析工廠
SAXParserFactory spf = SAXParserFactory.newInstance();
2.通過SAX解析工廠得到解析器對象
SAXParser sp = spf.newSAXParser();
3.通過解析器對象得到一個XML的讀取器
XMLReader xmlReader = sp.getXMLReader();
4.設置讀取器的事件處理器
xmlReader.setContentHandler(new BookParserHandler());
5.解析xml文件
xmlReader.parse("book.xml");
說明:如果只是使用SAXParserFactory、SAXParser他們完成只需要如下3步驟
1.獲取sax解析器的工廠對象
SAXParserFactory factory = SAXParserFactory.newInstance();
2.通過工廠對象 SAXParser創建解析器對象
SAXParser saxParser = factory.newSAXParser();
3.通過解析saxParser的parse()方法設定解析的文件和自己定義的事件處理器對象
saxParser.parse(new File("src//sax//sida.xml"), new MyDefaultHandler());
案例:解析出"作者"元素標籤中的文本內容
1.需要解析的sida.xml文件
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE 四大名著[
- <!ELEMENT 四大名著 (西遊記,紅樓夢)>
- <!ATTLIST 西遊記 id ID #IMPLIED>
- ]>
- <四大名著>
- <西遊記 id="x001">
- <作者>吳承恩</作者>
- </西遊記>
- <紅樓夢 id="x002">
- <作者>曹雪芹</作者>
- </紅樓夢>
- </四大名著>
2.解析測試類和事件處理器類的實現代碼
- package sax;
- import java.io.File;
- import javax.xml.parsers.SAXParser;
- import javax.xml.parsers.SAXParserFactory;
- import org.junit.Test;
- import org.xml.sax.Attributes;
- import org.xml.sax.SAXException;
- import org.xml.sax.helpers.DefaultHandler;
- public class SaxTest {
- @Test
- public void test() throws Exception {
- // 1.獲取sax解析器的工廠對象
- SAXParserFactory factory = SAXParserFactory.newInstance();
- // 2.通過工廠對象 SAXParser創建解析器對象
- SAXParser saxParser = factory.newSAXParser();
- // 3.通過解析saxParser的parse()方法設定解析的文件和自己定義的事件處理器對象
- saxParser.parse(new File("src//sax//sida.xml"), new MyDefaultHandler());
- }
- // 自己定義的事件處理器
- class MyDefaultHandler extends DefaultHandler {
- // 解析標籤開始及結束的的標識符
- boolean isOk = false;
- @Override
- public void startElement(String uri, String localName, String qName,
- Attributes attributes) throws SAXException {
- super.startElement(uri, localName, qName, attributes);
- // 當解析作者元素開始的時候,設置isOK爲true
- if ("作者".equals(qName)) {
- isOk = true;
- }
- }
- @Override
- public void characters(char[] ch, int start, int length)
- throws SAXException {
- // TODO Auto-generated method stub
- super.characters(ch, start, length);
- // 當解析的標識符爲true時,打印元素的內容
- if (isOk) {
- System.out.println(new String(ch, start, length));
- }
- }
- @Override
- public void endElement(String uri, String localName, String qName)
- throws SAXException {
- super.endElement(uri, localName, qName);
- // 當解析作者元素的結束的時候,設置isOK爲false
- if ("作者".equals(qName)) {
- isOk = false;
- }
- }
- }
- }
3.程序運行結果如下:
1.sax與Dom解析的區別
dom是w3c指定的一套規範標準,核心是按樹形結構處理數據,dom解析器讀入xml文件並在內存中建立一個結構一模一樣的“樹”,這樹各節點和xml各標記對應,通過操縱此“樹”來處理xml中的文件。xml文件很大時,建立的“樹”也會大,所以會大量佔用內存。sax解析器佔內存少,效率高。sax解析器核心是事件處理機制。例如解析器發現一個標記的開始標記時,將所發現的數據會封裝爲一個標記開始事件,並把這個報告給事件處理器,事件處理器再調用方法(startElement)處理髮現的數據。事件處理器可以自己編寫也可以從父類繼承。
上圖中描述了SAX和DOM的不同。
SAX適於處理下面的問題:
1、對大型文件進行處理;
2、只需要文件夾的部分內容,或者只需從文件中得到特定信息。
3、想建立自己的對象模型的時候。
DOM適於處理下面的問題:
1、需要對文件進行修改;
2、需要隨機對文件進行存取
2.如果用(二的解析方式解析web.xml)會有什麼樣的輸出結果?