DOM解析器
DOM(Document Object Model,文檔對象模型)是W3C制定的一套規范標準,即規定了解析文件的接口。各種語言可以按照DOM規范去實現這些接口,給出解析文件的解析器。
各種基于DOM規范解析器必須按照DOM規范在內存中建立數據,DOM規范的核心是樹模型。對于解析XML文件的解析器,解析器通過讀入XML文件在內存中建立一個樹,也就是說XML文件的標記、標記的文本內容、實體等都會和內存中樹的某個節點相對應。一個應用程序可以方便地操作內存中樹的節點來處理XML文檔,獲取自己所需要的數據。
2.DOM解析器
用 DOM 解析模型的優點是編程容易,開發人員只需要調用建樹的指令,然后利用navigation APIs訪問所需的樹節點來完成任務。可以很容易的添加和修改樹中的元素。然而由于使用 DOM 解析器的時候需要處理整個 XML 文檔,所以對性能和內存的要求比較高,尤其是遇到很大的 XML 文件的時候。由于它的遍歷能力,DOM 解析器常用于 XML 文檔需要頻繁的改變的服務中。W3C推薦使用DOM解析。
3.DOM和XML文件的互相轉化
DOM解析器是DocumentBuilder類的實例。
XML轉化為DOM對象
首先使用javax.xml.parsers包中的DocumentBuilderFactory類調用其類方法newInstance()實例化一個DocumentBuilderFactory對象:
[java] view plaincopyprint?然后factory對象調用newDocumentBuilder()方法返回一個DocumentBuilder對象(稱做DOM解析器),DocumentBuilder 類在javax.xml.parsers包中。例如:
[java] view plaincopyprint?
最后builder對象調用public Document parse(File f)方法解析參數f指定的文件,并將解析內容以對象的形式返回,該對象是實現了Document接口的一個實例,Document 接口在org.w3c.dom包中。例如:
[java] view plaincopyprint?
現在,應用程序只要分析內存中的樹狀結構數據Document,就可以獲得XML文件中的各種數據了。
DOM解析器經常使用下述3個方法解析XML文件:
public Document parse(File f) throws SAXException, IOException
public Document parse(InputStream in) throws SAXException, IOException
public Document parse(String uri) throws SAXException, IOException
其中:
方法parse(File f)可以解析參數f指定的XML文件,例如:
[java] view plaincopyprint?直接這樣指定assets下路徑是不幸的。File file = new File(" file:///android_asset/river.xml");原因是assets下的資源為原生的,只能用流的方式讀取,而且不能向assets目錄下寫。
方法parse(InputStream in)可以解析輸入流參數in指向的XML文件,例如:
[java] view plaincopyprint?
方法parse(String uri)可以解析參數uri指定的一個有效的資源,如果uri是一個鏈接地址,該鏈接地址必須是可以訪問的,例如:
[java] view plaincopyprint?除了通過parse方法得到Document對象外,還可以直接創建Document對象:
[java] view plaincopyprint?
DOM對象轉化為XML文件
解析器通過在內存中建立和XML結構相對應的樹狀結構數據,使得應用程序可以方便地獲得XML文件中的數據,同時提供了使用內存中的樹狀結構數據建立一個XML文件的API,即使用解析器得到的Document對象建立一個新的XML文件。但是需要注意的是,Android2.1中沒有相應的類包,從2.2開始才加入了。
解析器的parse方法將整個被解析的XML文件封裝成一個Document節點返回,我們可以對Document節點進行修改,然后使用 Transformer對象將一個Document節點變換為一個XML文件。
步驟如下:
首先使用javax.xml.transform包中的TransformerFactory類建立一個對象,
[java] view plaincopyprint?然后transFactory對象調用newTransformer()方法得到一個Transformer對象,Transformer類在javax.xml.transform包中。
然后將被變換的Document對象封裝到一個DOMSource對象中,DOMSource類在javax.xml.transform.dom包中。
再然后將變換得到XML文件對象封裝到一個StreamResult對象中,StreamResult類在javax.xml.transform.stream包中。
[java] view plaincopyprint?
最后,Transformer 對象transformer 調用transform方法實施變換:
[java] view plaincopyprint?
注意:以上用到了寫文件創建文件等,所以需要在AndroidManifest.xml中加入訪問SDCard的權限
<!--在SDCard中創建與刪除文件權限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!--往SDCard寫入數據權限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
1. 節點(Node)
解析器調用parse方法返回一個實現了Document接口的實例,該實例也稱做Document對象,應用程序可以從Document節點的子孫節點中獲取整個XML文件中數據的細節,它是由實現了Node接口的實例組成的樹狀結構數據,這些實例稱做Document對象中的節點。實際上Document接口也是Node接口的子接口,也就是說,parse方法將整個被解析的XML文件封裝成一個節點返回(XML文件和內存中的Document節點相對應),因此,我們也可以稱Document對象為Document節點。
Document對象中的節點形成樹狀結構,也就是說XML文件的標記、標記的文本內容、實體等都會和對象Document中的某個節點相對應。應用程序可以從Document節點的子孫節點中獲取整個XML文件中數據的細節。
按照DOM規范,Node接口有如下的子接口:
Attr, CDATASection, CharacterData, Comment, Document, DocumentFragment, DocumentType, Element, Entity, EntityReference, Notation, ProcessingInstruction, Text
任何實現上述某個接口的類的實例都稱做一個節點。
2.Node的常用方法
short getNodeType()
返回一個表示節點類型的常量(Node接口規定的常量值),例如,對于Element節點,getNodeType()方法返回的值為:Node.ELEMENT_NODE
NodeList getChildNodes()
返回一個由當前節點的所有子節點組成的NodeList對象。
Node getFirstChild()
返回當前節點的第一個子節點。
Node getLastChild()
返回當前節點的最后一個子節點。
NodeList getTextContent()
返回當前節點及所有子孫節點中的文本內容。
3.節點的子孫關系
為了解析規范的XML文件,DOM規范規定了各種類型節點之間可以形成的子孫關系,比如,Document節點有且僅有一個Element節點,也可以有一個DocumentType節點(規范的XML文件有且僅有一個根標記,也可以有一個與其關聯的DTD文件),Element節點可以有Element子節點和Text子節點(規范的XML文件中的標記可以有子標記和文本)。
?
Document節點
Document節點的兩個直接子節點的類型分別是DocumentType類型和Element類型,其中的DocumentType節點對應著XML文件所關聯的DTD文件,通過進一步獲取該節點子孫節點來分析DTDL文件中的數據;而其中的Element類型節點對應著XML文件的根節點,通過進一步獲取該Element類型節點子孫節點來分析XML文件中的數據。
Document 節點經常使用下列方法獲取和該節點相關的信息。
Element getDocumentElement()
返回當前節點的Element子節點。
DocumentType getDoctype()
返回當前節點的DocumentType子節點。
NodeList getElementsByTagName(String name)
返回一個NodeList對象,該對象由當前節點的Element類型子孫節點組成,這些子孫節點的名字由參數name指定。
NodeList getElementsByTagNameNS(String namespaceURI,String localName)
返回一個NodeList對象,該對象由當前節點的Element類型子孫節點組成, 這些子孫節點的名字由參數localName指定,名稱空間由參數namespaceURI 指定。
String getXmlEncoding()
返回XML文件使用的編碼,即XML聲明中encoding屬性的值。
boolean getXmlStandalone()
返回XML聲明中的standalone屬性的值。未指定時返回NULL
standalone 用來表示該文件是否呼叫其它外部的文件。 這里所指的外部文件其實就是查檢XML是不是有效的約束文件,或是DTD或是Schema
String getXmlVersion()
返回XML聲明中的version屬性的值
其中getXmlEncoding getXmlVersion getXmlStandalong在Android2.1中是沒有的。
要解析的XML文件:
?
其中getXmlEncoding方法在XML設置了encoding的情況下,取不到,不知道為什么。
?
Element節點
Element節點是Document節點的最重要的子孫節點,因為被解析的XML文件的標記對應著這樣類型的節點。表示Element節點的常量是Node.ELEMENT_NODE,一個節點用
short getNodeType()方法返回的值如果等于Node.ELEMENT_NODE,那么該節點就是Element節點。
Element節點經常使用下列方法獲取和該節點相關的信息。
String getTagName()
返回該節點的名稱,該名稱就是此節點對應的XML中的標記名稱。
String getAttribute(String name)
返回該節點中參數name指定的屬性值,該屬性值是此節點對應的XML標記中的屬性值。
NodeList getElementsByTagName(String name)
返回一個NodeList對象,該對象由當前節點的Element類型子孫節點組成,這些子孫節點的名字由參數name指定。
NodeList getElementsByTagNameNS(String namespaceURI,String localName)返回一個NodeList對象,該對象由當前節點的Element類型子孫節點組成,這些子孫節點的 名字由參數localName指定,名稱空間由參數namespaceURI 指定。
boolean hasAttribute(String name)
判斷當前節點是否有名字是參數name指定的屬性。
boolean hasAttributeNS(String namespaceURI, String localName)
判斷當前節點是否有名字是參數name指定、名稱空間是namespaceURI指定的屬性。
String getTextContent() Android 2.1沒有本API
getTagName和getNodeName的區別
getTagName()方法是Element接口中的方法,getNodeName()方法是Element接口從Node接口繼承的方法。對于Element節點,getTagName()和getNodeName()返回的都是Element節點對應的XML文件中標記的名稱。
?
Text節點
規范的XML文件的非空標記可以有子標記和文本內容。在DOM規范中,解析器使用Element節點封裝標記,用Text節點封裝標記的文本內容,即Element節點可以有Element子節點和Text節點。例如,對于下列標記:
該標記對應的Element節點共有7個子孫節點,其中2個Element子節點、3個Text子節點和2個Text孫節點。這些節點和XML中的標記及文本有如下的對應關系。
2個Element子節點分別對應“姓名”標記的2個子標記:“性別”和“年齡”。3個Text子節點分別對應著:“<姓名>”與“<性別>”之間的文本、“</性 別>”與“<年齡>”之間的空白類字符、“</年齡”與“</姓名>”之間的空白類字符。兩個Text孫節點分別對應標記“性別”和“年齡”的文本內容。
表示Text節點的常量是Node.TEXT_NODE,一個節點調用short getNodeType()方法返回的值如果等于Node.TEXT_NODE,那么該節點就是Text節點。
Text節點使用String getWholeText()方法獲取節點中的文本(包括其中的空白字符)。Android2.1中沒有本API
注意:對于Text節點,getNodeName()方法返回的是“#text”。
對于應用程序而言,Text節點是較重要的節點,因為Text節點封裝著XML標記中的文本數據。
Attr節點
在XML文件中,屬性并不是標記的子標記,因此,在DOM規范中,Att節點也不是Element節點的子節點。
如果想解析XML文件中標記的屬性,必須讓對應的Element節點調用NamedNodeMap getAttributes()方法。該方法返回的NamedNodeMap對象由節點組成,這些節點可以被轉換為Attr節點。Attr節點通過調用String getName()方法返回屬性的名字,調用String getValue()方法返回屬性的值。
處理空白
標記之間的縮進區域是為了使得XML文件看起來更美觀而形成的,但解析器并不知道這一點,所以解析器仍然認為它們是有用的文本數據(由空白類字符組成)
人們習慣上稱標記之間的縮進區是可忽略空白,這實際上不是很準確,因為XML文件的標記可以有文本和子標記(混合內容),在這種情況下,標記之間的區域中就可能包含非空白的字符內容。
如果我們不允許標記有混合內容,即標記要么只有子標記要么只有文本,在這種情形下,稱標記之間的縮進區域是可忽略空白就比較恰當,這些空白區確實使得XML文件看起來更加美觀,也是它們存在的惟一目的。
如果想讓DOM解析器忽略縮進空白,即這些縮進空白不在Document中形成Text節點,那么XML文件必須是有效的,而且所關聯的DTD文件必須規定XML文件的標記不能有混合內容,同時DocumentBuilderFactory對象在給出DOM解析器之前,必須調用setIgnoringElementContentWhitespace(boolean whitespace)進行設置,將參數whitespace的值設為true。
驗證規范性和有效性
通過DocumentBuilderFactory對象factory事先設置是否檢查XML文件的有效性,如:factory.setValidating(true);
?
DocumentType節點
DocumentType節點是Document節點的一個子節點。我們已經知道,解析器的parse方法將整個被解析的XML文件封裝成一個Document節點返回,Document節點的兩個子節點的類型分別是DocumentType類型和Element類型,其中的DocumentType節點對應著XML文件所關聯的DTD文件,通過進一步獲取該節點子孫節點來分析DTD文件中的數據。Document節點調用getDoctype() 返回當前節點的DocumentType子節點。
獲取DTD的基本信息
假如XML文件中的DOCTYPE聲明為:<!DOCTYPE 房子 PUBLIC "-//ISO88//beijing//ForXML/Ch" "b1.dtd",那么
DocumentType節點調用getName()方法返回的是:房子
調用getPublicId()方法返回的是:-//ISO88//beijing//ForXML/Ch
調用getSystemId()方法返回的是:b1.dtd
一個XML文件可以關聯一個外部DTD或一個內部DTD,也可二者皆有(見第3.9節)。如果XML關聯一個內部DTD,DocumentType節點調用getInternalSubset()方法可以返回內部DTD的內容。
獲取實體
DTD文件中可以定義實體,然后與該DTD文件關聯的XML文件可以通過實體引用使用該實體。實體又分為內部實體和外部實體,所謂內部實體就是實體的內容已經包含在DTD文件本身中;而外部實體是指實體的內容是DTD文件以外的其他文件。
解析器將實體封裝為Entity節點,DocumentType節點調用
NamedNodeMap getEntities()
方法可以得到全部的實體,該方法返回的NamedNodeMap對象由節點組成,這些節點可以被轉換為Entity節點。Entity節點通過調用getTextContent()方法返回實體,如果實體是一個外部文件,Entity節點通過調用getInputEncoding()方法可以返回解析該實體所使用的編碼;如果是內部實體,getInputEncoding()方法返回null。
CDATASection節點
在XML文件中,標記內容中的文本數據不可以含有左尖括號、右尖括號、與符號、單引號和雙引號這些特殊字符,如果想使用這些字符,辦法之一是通過實體引用,如果需要許多這樣的字符,文本數據中就會出現很多實體引用或字符引用,導致文本數據的閱讀變得困難。使用CDATA(Character Data)段可以解決這一問題,CDATA段用“<![CDATA[”作為段的開始,用“]]>”作為段的結束,段開始和段結束之間稱為CDATA段的內容,解析器不對CDATA段的內容做分析處理,因此,CDATA段中的內容可以包含任意的字符。
在DOM規范中,解析器使用CDATASection節點封裝CDATA段,CDATASection節點可以是Element的節點的子節點。
節點數目的計算辦法如下。
首先將標記中交替出現的普通文本和CDATA段按照它們在標記中出現的先后順序排列,如:
普通文本1 CDATA段1 普通文本2 CDATA段2 普通文本3
那么該標記對應的Element節點的子節點順序如下。
1 / Text節點:從“普通文本1”到“普通文本3”的區域,節點的文本內容是普通文本和CDATA段中的內容。
2 / CDATASection節點:從“CDATA段1”到“普通文本3”的區域,節點的文本內容是普通文本和CDATA段中的內容。
3 / Text節點:從“普通文本2”到“普通文本3”的區域,節點的文本內容是普通文本和CDATA段中的內容。
4 / CDATASection節點:從“CDATA段2”到“普通文本3”的區域,節點的文本內容是普通文本和CDATA段中的內容。
5 / Text節點:“普通文本3”,節點的文本內容是普通文本。
表示CDATASection節點的常量是Node.CDATA_SECTION_NODE,一個節點調用short getNodeType()方法返回的值如果等于Node.CDATA_SECTION_NODE,那么該節點就是CDATASection節點。CDATASection節點使用String getWholeText()方法獲取節點中的文本,即CDATA段中的文本(包括其中的空白字符)。
注意:對于CDATASection節點,getNodeName()方法返回的是“#cdata-section”。
總結
- 上一篇: Mathematica函数大全
- 下一篇: QueryRunner使用