Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> XML解析器

XML解析器

編輯:關於Android編程

一、操作XML文檔概述

1、如何操作XML文檔

XML文檔也是數據的一種,對數據的操作也不外乎是“增刪改查”。也被大家稱之為“CRUD”

C:Create; R:Retrieve; U:Update; D:Delete

2、XML解析技術

XML解析方式分為兩種:DOM(Document Object Model)和SAX(Simple API for XML)。這兩種方式不是針對Java語言來解析XML的技術,而是跨語言的解析方式。例如DOM還在Javascript中存在!

DOM是W3C組織提供的解析XML文檔的標准接口,而SAX是社區討論的產物,是一種事實上的標准。

DOM和SAX只是定義了一些接口,以及某些接口的缺省實現,而這個缺省實現只是用空方法來實現接口。一個應用程序如果需要DOM或SAX來訪問XML文檔,還需要一個實現了DOM或SAX的解析器,也就是說這個解析器需要實現DOM或SAX中定義的接口。提供DOM或SAX中定義的功能。

二、解析原理

1、DOM解析原理

使用DOM要求解析器把整個XML文檔裝載到一個Document對象中。Document對象包含文檔元素,即根元素,根元素包含N多個子元素…

一個XML文檔解析後對應一個Document對象,這說明使用DOM解析XML文檔方便使用,因為元素與元素之間還保存著結構關系。

優先:使用DOM,XML文檔的結構在內存中依然清晰。元素與元素之間的關系保留了下來!

缺點:如果XML文檔過大,那麼把整個XML文檔裝載進內存,可能會出現內存溢出的現象!

2、設置Java最大內存

運行Java程序,指定初始內存大小,以及最大內存大小。

java -Xms20m -Xmx100m MyClass

這裡寫圖片描述

3、SAX解析原理

DOM會一行一行的讀取XML文檔,最終會把XML文檔所有數據存放到Document對象中。SAX也是一行一行的讀取XML文檔,但是當XML文檔讀取結束後,SAX不會保存任何數據,同時整個解析XML文檔的工作也就結束了。

但是,SAX在讀取一行XML文檔數據後,就會給感興趣的用戶一個通知!例如當SAX讀取到一個元素的開始時,會通知用戶當前解析到一個元素的開始標簽。而用戶可以在整個解析的過程中完成自己的業務邏輯,當SAX解析結束,不會保存任何XML文檔的數據。

優先:使用SAX,不會占用大量內存來保存XML文檔數據,效率也高。

缺點:當解析到一個元素時,上一個元素的信息已經丟棄,也就是說沒有保存元素與元素之間的結構關系,這也大大限制了SAX的使用范圍。如果只是想查詢XML文檔中的數據,那麼使用SAX是最佳選擇!

這裡寫圖片描述

三、解析器概述

1、什麼是XML解析器

DOM、SAX都是一組解析XML文檔的規范,其實就是接口,這說明需要有實現者能使用,而解析器就是對DOM、SAX的實現了。一般解析器都會實現DOM、SAX兩個規范!

Crimson(sun):JDK1.4之前,Java使用的解析器。性能效差,可以忘記它了!

Xerces(IBM):IBM開發的DOM、SAX解析器,現在已經由Apache基金會維護。是當前最為流行的解析器之一!在1.5之後,已經添加到JDK之中,也是JAXP的默認使用解析器,但不過在JDK中的包名與Xerces不太一樣。例如:org.apache.xerces包名改為了com.sun.org.apache.xerces.internal包名,也就是說JDK1.5中的Xerces是被包裝後的XML解析器,但二者區別很小。

Aelfred2(dom4j):DOM4J默認解析器,當DOM4J找不到解析器時會使用他自己的解析器。

四、JAXP概述

1、什麼是JAXP

JAXP是由Java提供的,用於隱藏底層解析器的實現。Java要求XML解析器去實現JAXP提供的接口,這樣可以讓用戶使用解析器時不依賴特定的XML解析器。

JAXP本身不是解析器(不是Xerces),也不是解析方式(DOM或SAX),它只是讓用戶在使用DOM或SAX解析器時不依賴特點的解析器。

當用戶使用JAXP提供的方式來解析XML文檔時,用戶無需編寫與特定解析器相關的代碼,而是由JAXP通過特定的方式去查找解析器,來解析XML文檔。

這裡寫圖片描述

2、JAXP對DOM的支持

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("src/students.xml");

在javax.xml.parsers包中,定義了DOM解析器工廠類DocumentBuilderFactory,用於產生DOM解析器。DocumentBuilderFactory是一個抽象類,它有一個靜態方法newInstance(),可以返回一個本類的實例對象。其實該方法返回的是DocumentBuilderFactory類的子類的實例(即工廠實例對象)。那麼這個子類又是哪個子類呢?其實這個子類是由XML解析器提供商提供的,不同的廠商提供的工廠類對抽象工廠的實現是不同的。然後由工廠實例創建解析器對象。

那麼newInstance()這個方法又是如果找到解析器提供商的工廠類的呢?此方法使用下面有序的查找過程來確定要加載的DocumentBuilderFactory實現類:

2.1、使用javax.xml.parsers.DocumentBuilderFactory系統屬性。如果設置了這個系統屬性的值,那麼newInstance()方法就以這個屬性的值來構造這個工廠的實例。通過下面的方法可以設置這個系統屬性值。

System.setProperty(“javax.xml.parsers.DocumentBuilderFactory”, “工廠實現類名字”);

我們不建議大家用上面的方法來硬編碼這個系統屬性的值,如果這樣設置,假如將來需要更換解析器,就必需修改代碼。

2.2、如果你沒有設置上面的系統屬性,newInstance()方法就會采用下面的途徑來查找抽象工廠的實現類。第二個途徑在查找JRE下的lib子目錄下的jaxp.properties文件。如果這個文件存在,那麼就讀取這個文件。我們可以在%JAVA_HOME%\jre\lib\目錄下創建一個jaxp.properties文件。在這個文件中給出一個鍵值對。如下所示:

javax.xml.parsers.DocumentBuilderFactory=工廠實現類名字

這個key名字必須是javax.xml.parsers.DocumentBuilderFactory,而相對應的值也必須設置類路徑。

2.3、如果通過前兩種途徑下沒有找到工廠的實現類,那麼就需要使用服務API。這個服務API實際上是查找一個JAR文件的META-INF\ services\ javax.xml.parsers.DocumentBuilderFactory這個文件(該文件無擴展名)。如果找到了這個文件,就以這個文件的內容做為工廠實現類。這種方式被大多數解析器提供商所采用。在它們發布的解析器JAR包中往往會找到上述文件。然後在這個文件當中指定自己解析器的工廠類的名字。我們只需要把這個JAR文件的路徑寫到類路徑(classpath)中就可以了。但要注意的是,如果在你的classpath中有多個解析器的JAR包路徑,這時以前面的類路徑優先。

2.4、如果說在前三種途徑中都沒有找到工廠實現類,那麼就使用平台缺省工廠實現類。

在JAXP的早期的版本(5.0以前)中,除了JAXP API外,還包含了一個叫做Crimson的解析器。從JAXP1.2開始,Sun公司對Apache的Xerces解析器重新包裝了一下,並將org.apache.xerces包名改為了com.sun.org.apache.xerces.internal,然後在JAXP的開發包中一起提供,作為缺省的解析器。我們所使用的JDK1.5中包含的缺省解析器就是被重新包裝過後的Xerces解析器。

在獲取到某個特定解析器廠商的DocumentBuilderFactory後,那麼這個工廠對象創建出來的解析器對象當然就是自己廠商的解析器對象了。

3、JAXP對SAX的支持

SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse("src/students.xml", new DefaultHandler() {
    public void startDocument() throws SAXException {
        System.out.println("解析開始");
    }

    public void endDocument() throws SAXException {
        System.out.println("解析結束");
    }

    public void processingInstruction(String target, String data)
            throws SAXException {
        System.out.println("處理指令");
    }

    public void startElement(String uri, String localName, String qName,
            Attributes atts) throws SAXException {
        System.out.println("元素開始:" + qName);
    }

    public void characters(char[] ch, int start, int length)
            throws SAXException {
        System.out.println("文本內容:" + new String(ch, start, length).trim());
    }

    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        System.out.println("元素結束:" + qName);
    }
});

JAXP對SAX的支持與JAXP對DOM的支持是相同的,這裡就不在贅述!

JDOM和DOM4J

1、JDOM和DOM4J概述

JDOM和DOM4J都是針對Java解析XML設計的方式,它們與DOM相似。但DOM不是只針對Java,DOM是跨語言的,DOM在Javascript中也可以使用。而JDOM和DOM4J都是專業為Java而設計的,使用JDOM和DOM4J,對Java程序員而言會更加方便。

2、JDOM和DOM4J比較

JDOM與DOM4J相比,DOM4J完勝!!!所以,我們應該在今後的開發中,把DOM4J視為首選。

在2000年,JDOM開發過程中,因為團隊建議不同,分離出一支隊伍,開發了DOM4J。DOM4J要比JDOM更加全面。

3、DOM4J查找解析器的過程

DOM4J首先會去通過JAXP的查找方法去查找解析器,如果找到解析器,那麼就使用之;否則會使用自己的默認解析器Aelfred2。

DOM4J對DOM和SAX都提供了支持,可以把DOM解析後的Document對象轉換成DOM4J的Document對象,當然了可以把DOM4J的Document對象轉換成DOM的Document對象。

DOM4J使用SAX解析器把XML文檔加載到內存,生成DOM對象。當然也支持事件驅動的方式來解析XML文檔。
XML解析之JAXP(DOM)

JAXP獲取解析器

1、JAXP相關包

JAXP相關開發包:javax.xml DOM相關開發包:org.w3c.dom SAX相關開發包:org.xml.sax

2、JAXP與DOM、SAX解析器的關系

JAXP只是作用只是為了讓使用者不依賴某一特定DOM、SAX的解析器實現,當使用JAXP API時,使用者直接接觸的就是JAXP API,而不用接觸DOM、SAX的解析器實現API。

3、JAXP獲取DOM解析器

當我們需要解析XML文檔時,首先需要通過JAXP API解析XML文檔,獲取Document對象。然後用戶就需要使用DOM API來操作Document對象了。

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("src/students.xml");

4、JAXP保存Document

當我們希望把Document保存到文件中去時,可以使用Transformer對象的transform()方法來完成。想獲取Transformer對象,需要使用TransformerFactory對象。
與JAXP獲取DOM解析器一樣,隱藏了底層解析器的實現。也是通過抽象工廠來完成的,這裡就不在贅述了。

TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();
trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
trans.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "students.dtd");
trans.setOutputProperty(OutputKeys.INDENT, "yes");
Source source = new DOMSource(doc);
Result result = new StreamResult(xmlName);
transformer.transform(source, result);

Transformer類的transform()方法的兩個參數類型為:Source和Result,DOMSource是Source的實現類,StreamResult是Result的實現類。

5、JAXP創建Document

有時我們需要創建一個Document對象,而不是從XML文檔解析而來。這需要使用DocumentBuider對象的newDocument()方法。

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
doc.setXmlVersion("1.0");
doc.setXmlStandalone(true);

5、學習DOM之前,先寫兩個方法

Document getDocument(String xmlName):通過xmlName獲取Document對象; void saveDocument(Document doc, String xmlName):保存doc到xmlName文件中。

DOM API概述

1、Document對應XML文檔

無論使用什麼DOM解析器,最終用戶都需要獲取到Document對象,一個Document對象對應整個XML文檔。也可以這樣說,Document對象就是XML文檔在內存中的表示形式。

通常我們最為“關心”的就是文檔的根元素。所以我們必須要把Document獲取根元素的方法記住:Element getDocumentElement()。然後通過根元素再一步步獲取XML文檔中的數據。

2、DOM API中的類

在DOM中提供了很多接口,用來描述XML文檔中的組成部分。其中包括:文檔(Document)、元素(Element)、屬性(Attr)、文本(Text)、注釋(Comment)、CDATA段(CDATASection)等等。無論是哪種XML文檔組成部分,都是節點(Node)的子接口。

這裡寫圖片描述

3、Node方法介紹

Node基本方法:

String getNodeName()
獲取當前節點的名字。如果當前節點是Element,那麼返回元素名稱。如果當前節點是Text那麼返回#text。如果當前節點是Document那麼返回#document

String getNodeValue()
獲取當前節點的值。只有文本節點有值,其它節點的值都為null

String getTextContext()
獲取當前節點的文本字符串。如果當前節點為Text,那麼獲取節點內容。如果當前節點為Element,那麼獲取元素中所有Text子節點的內容。例如當前節點為:< name >zhangSan< /name >,那麼本方法返回zhangSan。如果當前節點為:< student >< name >zhangSan< /name >< age>23< /age>< sex>male< /sex>< /student>,那麼本方法返回zhangSan23male。

short getNodeType()
獲取當前節點的類型。Node中有很多short類型的常量,可以通過與這些常量的比較來判斷當前節點的類型。if(node.getNodeType() == Node.ELEMENT_NODE);

Node獲取子節點和父節點方法,只有Document和Element才能使用這些方法:

NodeList getChildNodes():獲取當前節點的所有子節點。 Node getFirstNode():獲取當前節點的第一個子節點; Node getLastNode():獲取當前節點的最後一個子節點; Node getParentNode():獲取當前節點的父節點。注意Document的父節點為null。

NodeList表示節點列表,它有兩個方法:

int getLength():獲取集合長度; Node item(int index):獲取指定下標的節點。

Node獲取弟兄節點的方法,只有Element才能使用這些方法:

Node getNextSibling():獲取當前節點的下一個兄弟節點; Node getPreviousSibling():獲取當前節點的上一個兄弟節點。

Node添加、替換、刪除子節點方法:

Node appendChild(Node newChild)
把參數節點newChild添加到當前節點的子節點列表的末尾處。返回值為被添加的子節點newChild對象,方便使用鏈式操作。如果newChild在添加之前已經在文檔中存在,那麼就是修改節點的位置了;

Node insertBefore(Node newChild, Node refNode)
把參數節點newChild添加到當前節點的子節點refNode之前。返回值為被添加的子節點newChild對象,方便使用鏈式操作。如果refNode為null,那麼本方法與appendNode()方法功能相同。如果newChild節點在添加之前已經在文檔中存在,那麼就是修改節點的位置了。

Node removeNode(Node oldChild)
從當前節點中移除子元素oldChild。返回值為被添加的子節點oldChild對象,方便使用鏈式操作。

Node replaceNode(Node newChild, Node oldChild)
將當前節點的子節點oldChild替換為newChild。

Node獲取屬性集合方法,只有Element可以使用:

NamedNodeMap getAttributes():返回當前節點的屬性集合。

NamedNodeMap表示屬性的集合,方法如下:

int getLength():獲取集合中屬性的個數; Node item(int index):獲取指定下標位置上的屬性節點; Node getNamedItem(String name):獲取指定名字的屬性節點; Node removeNamedItem(String name):移除指定名字的屬性節點,返回值為移除的屬性節點; Node setNamedItem(Node arg):添加一個屬性節點,返回值為添加的屬性節點。

Node的判斷方法:

boolean hasChildNodes():判斷當前節點是否有子節點; boolean hasAttribute():判斷當前節點是否有屬性。

4 Docment方法介紹
創建節點方法:

Attr createAttribute(String name):創建屬性節點; CDATASection createCDATASection(String data):創建CDATA段節點; Comment createComment(String data):創建注釋; Element createElement(String tagName):創建元素節點; Text createTextNode(String data):創建文本節點;

獲取子元素方法:

Element getElementById(String elementId):通過元素的ID屬性獲取元素節點,如果沒有DTD指定屬性類型為ID,那麼這個方法將返回null; NodeList getElementsByTagName(String tagName):獲取指定元素名稱的所有元素; Element getDocumentElement():獲取文檔元素,即獲取根元素。

文檔聲明相關方法:

方法 說明 String getXmlVersion() 獲取文檔聲明的version屬性值 String getXmlEncoding() 獲取文檔聲明的encoding屬性值 String getXmlStandalone() 獲取文檔聲明的standalone屬性值 void setXmlVersion() 設置文檔聲明version屬性值 void setXmlStandalone() 設置文檔聲明standalone屬性值

5、Element方法介紹

獲取方法:

NodeList getElementsByTagName(String tagName):獲取當前元素的指定元素名稱的所有子元素; String getTagName():獲取當前元素的元素名。調用元素節點的getNodeName()也是返回名;

屬性相關方法:

方法 說明 String getAttribute(String name) 獲取當前元素指定屬性名的屬性值 Attr getAttributeNode(String name) 獲取當前元素指定屬性名的屬性節點 boolean hasAttribute(String name) 判斷當前元素是否有指定屬性 void removeAttribute(String name) 移除當前元素的指定屬性 void removeAttributeNode(Attr attr) 移除當前元素的指定屬性 void setAttribute(String name, String value) 為當前元素添加或修改屬性 Attr setAttributeNode(Attr attr) 為當前元素添加或修改屬性,返回值為添加的屬性

6、Attr方法介紹

方法 說明 String getName() 獲取當前屬性節點的屬性名 String getValue() 獲取當前屬性節點的屬性值 void setValue(String value) 設置當前屬性節點的屬性值 boolean isId() 判斷當前屬性節點是否為ID類型屬性

SAX

1、SAX解析原理

首先我們想一下,DOM解析器是不是需要把XML文檔遍歷一次,然後把每次讀取到的數據轉換成節點對象(到底哪一種節點對象,這要看解析時遇到了什麼東西)保存起來,最後生成一個Document對象返回。也就是說,當你調用了builder.parse(“a.xml”)後,這個方法就會把XML文檔中的數據轉換成節點對象保存起來,然後生成一個Document對象。這個解析XML文檔的過程在parse()方法調用結束後也就結束了。我們的工作是在解析之後,開始對Document對象進行操作。

但是SAX不同,當SAX解析器的parse()方法調用結束後,不會給我們一個Document對象,而是什麼都不給。SAX不會把XML數據保存到內存中,如果我們的解析工作是在SAX解析器的parse()方法調用結束後開始,那麼就已經晚了!!!這說明我們必須在SAX解析XML文檔的同時完成我們的工作。

SAX解析器在解析XML文檔的過程中,讀取到XML文檔的一個部分後,會調用ContentHandler(內部處理器)中的方法。例如當SAX解析到一個元素的開始標簽時,它會調用ContentHandler的startElement()方法;在解析到一個元素的結束標簽時會調用ContentHandler的endElement()方法。

ContentHandler是一個接口,我們的工作是編寫該接口的實現類,然後創建實現類的對象,在SAX解析器開始解析之前,把我們寫的內容處理類對象交給SAX解析器,這樣在解析過程中,我們的內容處理中的方法就會被調用了。

2、獲取SAX解析器

與DOM相同,你應該通過JAXP獲取SAX解析器,而不是直接使用特定廠商的SAX解析器。JAXP查找特定廠商的SAX解析器實現的方式與查找DOM解析器實現的方式完全相同,這裡就不在贅述了。

SAXParserFactory factory = SAXParserFactory.newInstance();
javax.xml.parsers.SAXParser parser = factory.newSAXParser();
parser.parse("src/students.xml", new MyContentHandler());

上面代碼中,MyContentHandler就是我們自己需要編寫的ContentHandler的實現類對象。

3、內容處理器

org.xml.sax.ContentHandler中的方法:

void setDocumentLocator(Locator locator)
與定位相關,例如獲取行數、實體、標識等信息,我們可以忽略他的存在;

void startDocument() throws SAXException:文檔開始解析之前被調用;

void endDocument() throws SAXException:文檔解析結束之後被調用;

void startPrefixMapping(String prefix,String uri)throws SAXException:與名稱空間相關,忽略;

void endPrefixMapping(String prefix) throws SAXException:與名稱空間相關,忽略;

void startElement(String uri,String local,String qName,Attributes atts)throws SAXException
開始解析一個元素時被調用,其中uri、local這兩個參數與名稱空間相關,可以忽略。qName表示當前元素的名稱,atts表示當前元素的屬性集合;

void endElement(String uri,String localName,String qName)throws SAXException:一個元素解析結束後會被調用;

void characters(char[] ch,int start,int length)throws SAXException
解析到文本數據時會被調用,ch表示當前XML文檔所有內容對應的字符數組,不只是當前文本元素的內容。start表示當前文本數據在整個XML文檔中的開始下載位置,length是當前文本數據的長度;

void ignorableWhitespace(char[] ch,int start,int length)throws SAXException:解析到空白文本數據時會被調用,可以忽略!

void processingInstruction(String target,String data)throws SAXException:解析到處理指令時會被調用,可以忽略!

void skippedEntity(String name)throws SAXException:解析到實體時會被調用,可以忽略!

org.xml.sax.helpers.DefualtHandler對ContentHandler做了空實現,所以我們可以自定義內容處理器時可以繼承DefaultHandler類。

SAX應用

1、測試SAX

public class SAXTest {
    @Test
    public void testSAX() throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        parser.parse("src/students.xml", new MyContentHandler());
    }
    private static class MyContentHandler extends DefaultHandler {
        @Override
        public void startDocument() throws SAXException {
            System.out.println("開始解析...");
        }
        @Override
        public void endDocument() throws SAXException {
            System.out.println("解析結束...");
        }
        @Override
        public void startElement(String uri, String localName, String qName,
                Attributes atts) throws SAXException {
            System.out.println(qName + "元素解析開始");
        }
        @Override
        public void endElement(String uri, String localName, String qName)
                throws SAXException {
            System.out.println(qName + "元素解析結束");
        }
        @Override
        public void characters(char[] ch, int start, int length)
                throws SAXException {
            String s = new String(ch, start, length);
            if(s.trim().isEmpty()) {
                return;
            }
            System.out.println("文本內容:" + s);
        }
        @Override
        public void ignorableWhitespace(char[] ch, int start, int length)
                throws SAXException {

        }
        @Override
        public void processingInstruction(String target, String data)
                throws SAXException {
            System.out.println("處理指令");
        }
    }
}

2、使用SAX打印XML文檔

public class SAXTest2 {
    @Test
    public void testSAX() throws 
ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        parser.parse("src/students.xml", new MyContentHandler());
    }
    private static class MyContentHandler extends DefaultHandler {
        @Override
        public void startDocument() throws SAXException {
            System.out.println("");
        }

        @Override
        public void startElement(String uri, String localName, String qName,
                Attributes atts) throws SAXException {
            StringBuilder sb = new StringBuilder();
            sb.append("<").append(qName);
            for(int i = 0; i < atts.getLength(); i++) {
                sb.append(" ");
                sb.append(atts.getQName(i));
                sb.append("=");
                sb.append("'");
                sb.append(atts.getValue(i));
                sb.append("'");
            }
            sb.append(">");
            System.out.print(sb);
        }

        @Override
        public void endElement(String uri, String localName, String qName)
                throws SAXException {
            System.out.print("");
        }

        @Override
        public void characters(char[] ch, int start, int length)
                throws SAXException {
            System.out.print(new String(ch, start, length));
        }
    }
}

DOM4J

1、DOM4J是什麼

DOM4J是針對Java開發人員專門提供的XML文檔解析規范,它不同與DOM,但與DOM相似。DOM4J針對Java開發人員而設計,所以對於Java開發人員來說,使用DOM4J要比使用DOM更加方便。

DOM4J對DOM和SAX提供了支持,使用DOM4J可以把org.dom4j.document轉換成org.w3c.Document,DOM4J也支持基於SAX的事件驅動處理模式。

使用者需要注意,DOM4J解析的結果是org.dom4j.Document,而不是org.w3c.Document。DOM4J與DOM一樣,只是一組規范(接口與抽象類組成),底層必須要有DOM4J解析器的實現來支持。

DOM4J使用JAXP來查找SAX解析器,然後把XML文檔解析為org.dom4j.Document對象。它還支持使用org.w3c.Document來轉換為org.dom4j.Docment對象。

2、DOM4J中的類結構

在DOM4J中,也有Node、Document、Element等接口,結構上與DOM中的接口比較相似。但還是有很多的區別:

在DOM4J中,所有XML組成部分都是一個Node,其中Branch表示可以包含子節點的節點,例如Document和Element都是可以有子節點的,它們都是Branch的子接口。

Attribute是屬性節點,CharacterData是文本節點,文本節點有三個子接口,分別是CDATA、Text、Comment。

這裡寫圖片描述

3、DOM4J獲取Document對象

使用DOM4J來加載XML文檔,需要先獲取SAXReader對象,然後通過SAXReader對象的read()方法來加載XML文檔:

SAXReader reader = new SAXReader();
//reader.setValidation(true);
Document doc = reader.read("src/students.xml");

4、DOM4J保存Document對象

保存Document對象需要使用XMLWriter對象的write()方法來完成,在創建XMLWriter時還可以為其指定XML文檔的格式(縮進字符串以及是否換行),這需要使用OutputFormat來指定。

doc.addDocType("students", "", "students.dtd");
OutputFormat format = new OutputFormat("\t", true);
format.setEncoding("UTF-8");
XMLWriter writer = new XMLWriter(new FileWriter(xmlName), format);
writer.write(doc);
writer.close();

5、DOM4J創建Document對象

DocumentHelper類有很多的createXXX()方法,用來創建各種Node對象。

Document doc = DocumentHelper.createDocument();

Document操作

1、遍歷students.xml

涉及的相關方法:

Element getRootElement():Document的方法,用來獲取根元素; List elements():Element的方法,用來獲取所有子元素; String attributeValue(String name):Element的方法,用來獲取指定名字的屬性值; Element element(String name):Element的方法,用來獲取第一個指定名字的子元素; Element elementText(String name):Element的方法,用來獲取第一個指定名字的子元素的文本內容。

分析步驟:

獲取Document對象; 獲取root元素; 獲取root所有子元素 遍歷每個student元素; 打印student元素number屬性; 打印student元素的name子元素內容; 打印student元素的age子元素內容; 打印student元素的sex子元素內容。

2、給學生元素添加< score>子元素

涉及的相關方法:

Element addElement(String name):Element的方法,為當前元素添加指定名字子元素。返回值為新建元素對象; setText(String text):Element的方法,為當前元素設置文本內容。

分析步驟:

獲取Document對象; 獲取root對象; 獲取root所有子元素; 遍歷所有學生子元素; 創建元素,為添加文本內容; 把元素添加到學生元素中。 保存Document對象。

3、為張三添加friend屬性,指定為李四學號

涉及方法:

addAttribute(String name, String value):Element的方法,為當前元素添加屬性。

分析步驟:

獲取Document對象; 獲取root對象; 獲取root所有子元素; 創建兩個Element引用:zhangSanEle、liSiEle,賦值為null; 遍歷所有學生子元素; 如果zhangSanEle和liSiEle都不是null,break; 判斷當前學生元素的name子元素文本內容是zhangSan,那麼把當前學生元素賦給zhangSanEle; 判斷當前學生元素的name子元素文本內容是liSi,那麼把當前學生元素賦給liSiEle。 判斷zhangSanEle和liSiEle都不為null時: 獲取liSiEle的number屬性。 為zhangSanEle添加friend屬性,屬性值為liSi的number屬性值。 保存Document對象。

4、刪除number為ID_1003的學生元素

涉及方法:

boolean remove(Element e):Element和Document的方法,移除指定子元素; Element getParent():獲取父元素,根元素的父元素為null。

分析步驟:

獲取Document對象; 獲取root對象; 獲取root所有子元素; 遍歷所有學生子元素; 判斷當前學生元素的number屬性是否為ID_1003; 獲取當前元素的父元素; 父元素中刪除當前元素; 保存Document對象.

5、通過List生成Document並保存

涉及方法:

DocumentHelper.createDocument():創建Document對象; DocumentHelper.createElement(String name):創建指定名稱的Element元素。

分析步驟:

創建Document對象; 為Document添加根元素; 循環遍歷學生集合List; 把當前學生對象轉換成Element元素; 把Element元素添加到根元素中; 保存Document對象。

把學生轉換成Element步驟分析:

創建Element對象; 為Element添加number屬性,值為學生的number; 為Element添加name子元素,文本內容為學生的name; 為Element添加age子元素,文本內容為學生的age; 為Element添加sex子元素,文本內容為學生的sex。

6、新建趙六學生元素,插入到李四之前

涉及方法:

int indexOf(Node node):Branch的方法,查找指定節點,在當前Branch的子節點集合中的下標位置。

分析步驟:

創建趙六學生對象; 通過學生對象創建趙六學生元素; 通過名稱查找李四元素; 查看李四元素在其父元素中的位置; 獲取學生子元素List; 將趙六元素插入到List中。

通過名字查找元素:

獲取Document; 獲取根元素; 獲取所有學生元素; 遍歷學生元素; 獲取學生元素name子元素的文本內容,與指定名稱比較; 返回當前學生元素。

7、其它方法介紹

Node方法:

String asXML():把當前節點轉換成字符串,如果當前Node是Document,那麼就會把整個XML文檔返回; String getName():獲取當前節點名字;Document的名字就是綁定的XML文檔的路徑;Element的名字就是元素名稱;Attribute的名字就是屬性名; Document getDocument():返回當前節點所在的Document對象; short getNodeType():獲取當前節點的類型; String getNodeTypeName():獲取當前節點的類型名稱,例如當前節點是Document的話,那麼該方法返回Document; String getStringValue():獲取當前節點的子孫節點中所有文本內容連接成的字符串; String getText():獲取當前節點的文本內容。如果當前節點是Text等文本節點,那麼本方法返回文本內容;例如當前節點是Element,那麼當前節點的內容不是子元素,而是純文本內容,那麼返回文本內容,否則返回空字符串; void setDocument(Document doc):給當前節點設置文檔元素; void setParent(Element parent):給當前節點設置父元素; void setText(String text):給當前節點設置文本內容;

Branch方法:

Branch

Document方法:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCkVsZW1lbnQgZ2V0Um9vdEVsZW1lbnQoKaO6u/HIobj51KrL2KO7IHZvaWQgc2V0Um9vdEVsZW1lbnQoKaO6yejWw7j51KrL2KO7IFN0cmluZyBnZXRYbWxFbmNvZGluZygpo7q78cihWE1MzsS1tbXEseDC66O7IHZvaWQgc2V0WG1sRW5jb2RpbmcoKaO6yejWw1hNTM7EtbW1xLHgwuujuw0KPHA+RWxlbWVudLe9t6ijujwvcD4NCnZvaWQgYWRkKEF0dHJpYnV0ZSBhdHRyKaO6zO2808r00NS92rXjo7sgdm9pZCBhZGQoQ0RBVEEgY2RhdGEpo7rM7bzTQ0RBVEG2zr3ateOjuyB2b2lkIGFkZChUZXh0IFRleHQpo7rM7bzTVGV4dL3ateOjuyBFbGVtZW50IGFkZEF0dHJpYnV0ZShTdHJpbmcgbmFtZSwgU3RyaW5nIHZhbHVlKaO6zO2808r00NSjrLe1u9jWtc6qtbHHsNSqy9ixvsnto7sgRWxlbWVudCBhZGRDREFUQShTdHJpbmcgY2RhdGEpo7rM7bzTQ0RBVEG2zr3ateOjuyBFbGVtZW50IGFkZENvbW1lbnQoU3RyaW5nIGNvbW1lbnQpo7rM7bzTyvTQ1L3ateOjuyBFbGVtZW50IGFkZFRleHQoU3RyaW5nIHRleHQpo7rM7bzTVGV4dL3ateOjuyB2b2lkIGFwcGVuZEF0dHJpYnV0ZXMoRWxlbWVudCBlKaO6sNGyzsr91KrL2GW1xMv509DK9NDUzO2807W9tbHHsNSqy9jW0KO7IEF0dHJpYnV0ZSBhdHRyaWJ1dGUoaW50IGluZGV4KaO6u/HIoda4tqjPwrHqzrvWw8nPtcTK9NDUttTP86O7IEF0dHJpYnV0ZSBhdHRyaWJ1dGUoU3RyaW5nIG5hbWUpo7rNqLn91ri2qMr00NTD+7PGu/HIocr00NS21M/zo7sgaW50IGF0dHJpYnV0ZUNvdW50KCmjurvxyKHK9NDUuPbK/aO7IEl0ZXJhdG9yIGF0dHJpYnV0ZUl0ZXJhdG9yKCmjurvxyKG1scew1KrL2Mr00NS8r7rPtcS1/LT6xvejuyBMaXN0IGF0dHJpYnV0ZXMoKaO6u/HIobWxx7DUqsvYtcTK9NDUvK+6z6O7IFN0cmluZyBhdHRyaWJ1dGVWYWx1ZShTdHJpbmcgbmFtZSmjurvxyKG1scew1KrL2Na4tqjD+7PGtcTK9NDU1rWjuyBFbGVtZW50IGNyZWF0ZUNvcHkoKaO6Y2xvbmW1scew1KrL2LbUz/OjrLWrsru74WNvcHm4uNSqy9iho9Kyvs3Kx8u10MLUqsvYw7vT0Li41KrL2KOstavT0NfT1KrL2KO7IEVsZW1lbnQgZWxlbWVudChTdHJpbmcgbmFtZSmjurvxyKG1scew1KrL2LXa0ru49sP7s8bOqm5hbWW1xNfT1KrL2KO7IEl0ZXJhdG9yIGVsZW1lbnRJdGVyYXRvcigpo7q78cihtbHHsNSqy9i1xNfT1KrL2Lyvus+1xLX8tPrG96O7IEl0ZXJhdG9yIGVsZW1lbnRJdGVyYXRvcihTdHJpbmcgbmFtZSmjurvxyKG1scew1KrL2NbQ1ri2qMP7s8a1xNfT1KrL2Lyvus+1xLX8tPrG96O7IExpc3QgZWxlbWVudHMoKaO6u/HIobWxx7DUqsvY19PUqsvYvK+6z6O7IExpc3QgZWxlbWVudHMoU3RyaW5nIG5hbWUpo7q78cihtbHHsNSqy9jWuLaow/uzxrXE19PUqsvYvK+6z6O7IFN0cmluZyBlbGVtZW50VGV4dChTdHJpbmcgbmFtZSmjurvxyKG1scew1KrL2Na4tqjD+7PGtcS12tK7uPbUqsvYzsS8/sTayN2juyBTdHJpbmcgZWxlbWVudFRleHRUcmltZShTdHJpbmcgbmFtZSmjus2syc+jrNa7ysfIpbP9wcvO3tPDv9Ww16O7IGJvb2xlYW4gaXNUZXh0T25seSgpo7q1scew1KrL2MrHt/HOqrS/zsSxvsTayN3UqsvYo7sgYm9vbGVhbiByZW1vdmUoQXR0cmlidXRlIGF0dHIpo7rSxrP9yvTQ1KO7IGJvb2xlYW4gcmVtb3ZlKENEQVRBIGNkYXRhKaO60saz/UNEQVRBo7sgYm9vbGVhbiByZW1vdmUoVGV4dCB0ZXh0KaO60saz/VRleHShow0KPHA+RG9jdW1lbnRIZWxwZXK+ssyst723qL3pydyjujwvcD4NCnN0YXRpYyBEb2N1bWVudCBjcmVhdGVEb2N1bWVudCgpo7q0tL2oRGNvdW1lbnS21M/zo7sgc3RhdGljIEVsZW1lbnQgY3JlYXRlRWxlbWVudChTdHJpbmcgbmFtZSmjurS0vajWuLaow/uzxrXE1KrL2LbUz/OjuyBzdGF0aWMgQXR0cmlidXRlIGNyZWF0ZUF0dHJidXRlKEVsZW1lbnQgb3duZXIsIFN0cmluZyBuYW1lLCBTdHJpbmcgdmFsdWUpo7q0tL2oyvTQ1LbUz/OjuyBzdGF0aWMgVGV4dCBjcmVhdGVUZXh0KFN0cmluZyB0ZXh0KaO6tLS9qMr00NS21M/zo7sgc3RhdGljIERvY3VtZW50IHBhcnNlVGV4dChTdHJpbmcgdGV4dCmjus2ouf24+LaotcTX1rf7tK7J+rPJRG9jdW1lbnS21M/zo7sNCjxoMiBpZD0="schema">Schema

我們學習Schema的第一目標是:參照Schema的要求可以編寫XML文檔;
第二目標是:可以自己來定義Schema文檔。

1、Schema是什麼

XML文檔的約束,用來替代DTD。
DTD文檔不是XML語法,而Schema本身也是XML文檔,這對解析器來說不用再去處理非XML的文檔了;

DTD只能表述平台線束,而Schema本身也是XML,所以可以描述結構化的約束信息。

DTD不只約束元素或屬性的類型,但Schema可以。例如讓age屬性的取值在0~100之間。

Schema文檔的擴展名為xsd,即XML Schema Definition。

2、為students.xml編寫DTD

< !ELEMENT students (student+)>
< !ELEMENT student (name,age,sex)>
< !ELEMENT name (#PCDATA)>
< !ELEMENT age (#PCDATA)>
< !ELEMENT sex (#PCDATA)>
< !ATTLIST student number CDATA #REQUIRED>

3、為students.xml編寫schema



    
    
        
            
        
    
    
        
            
            
                
                    
                        
                        
                    
                
            
            
                
                    
                        
                        
                    
                
            
        
        
    

參照Schema編寫XML文檔
我們參照上面的Schema文檔編寫一個studens.xml文件



    
        張三
        23
        
    
    
        李四
        32
        
    
    
        王五
        50
        
    
 

名稱空間相關內容
XSD文檔中是創建元素和屬性的地方;
XML文檔中是使用元素和屬性的地方。
所以在XML文檔中需要說明使用了哪些XSD文檔。

1、什麼是名稱空間

名稱空間是用來處理XML元素或屬性的名字沖突問題。你可以理解為Java中的包!包的作用就是用來處理類的名字沖突問題。

注意:XML與Java有很大區別,雖然都是處理名字沖突問題,但語法上是有很大區別的。例如在Java中可以使用import來導入類,但你一定要保存你導入的類已經在類路徑(classpath)中存在。使用package為當前Java文件中所有類聲明名。但XML的名稱空間要比Java復雜很多。

我們在下面講解XML名稱空間時,會使用Java包的概念來理解XML的名稱空間的概念,所以現在大家就要注意這麼幾個特性:

import:導包,聲明名稱空間; package:定義包,指定目標名稱空間; classpath:添加到類路徑,關聯XSD文件

2、聲明名稱空間(導包)

無論是在XML中,還是在XSD中,都需要聲明名稱空間。這與Java中使用import來導包是一個道理。當然,前提是有包(創建類是使用了package)才可以導,沒包就不能導了。如果被定義的元素在聲明時沒有指定目標名稱空間,那麼就是在無名稱空間中,那麼我們在使用這些在無名稱空間中的元素時,就不用再去聲明名稱空間了。

聲明名稱空間使用xmlns,例如:xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”。這表示聲明了一個名稱空間,相當與Java中的import。但是,Java中的import的含義是在下面使用的類,如果沒有給出包名,那麼就是import導入的這個類。而xmlns表示,下面使用xsi為前綴的元素或屬性,都是來自http://www.w3.org/2001/XMLSchema-instance名稱空間。也就是說給名稱空間起了一個簡稱,這就相當於我們稱呼“北京傳智播客教育科技有限公司”為“傳智”一樣。“傳智”就是簡稱。

例如在XSD文件中,xmlns:xsd=”http://www.w3.org/2001/XMLSchema”就是聲明名稱空間,而這個名稱空間是W3C的名稱空間,無需關聯文件就可以直接聲明!在XSD文件中所有使用xsd為前面的元素和屬性都是來自http://www.w3.org/2001/XMLSchema名稱空間。

名稱空間命名:一般名稱空間都是以公司的URL來命名,即網址!當然也可以給名稱空間命名為aa、bb之類的名字,但這可能會導致名稱空間的重名問題。

前綴命名:前綴的命名沒有什麼要求,但一般對http://www.w3.org/2001/XMLSchema名稱空間的前綴都是使用xs或xsd。http://www.w3.org/2001/XMLSchema-instance的前綴使用xsi。

在XML文檔中聲明xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”名稱空間的目的是使用xsi中的一個屬性:xsi:noNamespaceSchemaLocation,它是用W3C提供的庫屬性,用來關聯XSD文件用的。當然,它只能關聯那些沒有“目標名稱空間”的XSD文件。下面會講解目標名稱空間!

3、默認名稱空間

所謂默認名稱空間就是在聲明名稱空間時,不指定前綴,也可以理解為前綴為空字符串的意思。這樣定義元素時,如果沒有指定前綴的元素都是在使用默認名稱空間中的元素。

xmlns=”http://www.itcast.cn”
當在文檔中使用時,那麼元素就是http://www.itcast.cn名稱空間中聲明的元素。

注意:沒有指定前綴的屬性不表示在默認名稱空間中,而是表示沒有名稱空間。也就是說,默認名稱空間不會涉及到屬性,只對元素有效!

XPath(擴展)

1、什麼是XPath

XPath即為XML路徑語言(XML Path Language),它是一種用來確定XML文檔中某部分位置的語言。XPath基於XML的樹狀結構,提供在數據結構樹中找尋節點的能力。起初 XPath 的提出的初衷是將其作為一個通用的、介於XPointer與XSL間的語法模型。但是 XPath 很快的被開發者采用來當作小型查詢語言。

2、DOM4J對XPath的支持

在DOM4J中,Node接口中的三個方法最為常用: 

List selectNodes(String xpathExpression):在當前節點中查找滿足XPath表達式的所有子節點; Node selectSingleNode(String xpathExpression):在當前節點中查找滿足XPath表達式的第一個子節點; String valueOf(String xpathExpression):在當前節點中查找滿足XPath表達式的第一個子節點的文本內容;
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved