DOM樹節點解析

DOM是解析XML文件的官方標準,它與平臺和語言無關。DOM解析將整個XML文件載入並組裝成一棵DOM節點樹,而後經過遍歷、查找節點以讀取XML文件中定義的數據。因爲DOM解析中把全部節點都載入到內存中,於是它比較耗資源,並且它須要把整棵節點樹構建完成後開始讀取數據,於是它相對性能也很差;不過因爲它在內存中保存了DOM節點樹,於是它能夠屢次讀取,而且它的節點樹定義比較容易理解,於是操做起來比較簡單。關於性能,有人對一些經常使用的解析方法作了比較:css

單位:s(秒)轉自:http://www.cnblogs.com/hedalixin/archive/2011/12/04/2275453.htmlhtml

 

100KB node

1MB spring

10MB編程

DOMapp

0.146s ide

0.469s性能

5.876sui

SAXthis

0.110s 

0.328s

3.547s

JDOM

0.172s

0.756s

45.447s

DOM4J

0.161s

0.422s

5.103s

StAX Stream

0.093s

0.334s

3.553s

StAX Event

0.131s

0.359s

3.641s

DOM樹中,全部節點都是一個Node,對不一樣節點有不一樣類型,W3C對不一樣類型的節點定義以下:

節點類型

描述

子元素

Document

表示整個文檔(DOM 樹的根節點)

Element (max. one)

ProcessingInstruction

Comment

DocumentType

DocumentFragment

表示輕量級的 Document 對象,其中容納了一部分文檔。

ProcessingInstruction

Comment

Text

CDATASection

EntityReference

DocumentType

向爲文檔定義的實體提供接口。

None

ProcessingInstruction

表示處理指令。

None

EntityReference

表示實體引用元素。

ProcessingInstruction

Comment

Text

CDATASection

EntityReference

Element

表示 element(元素)元素

Text

Comment

ProcessingInstruction

CDATASection

EntityReference

Attr

表示屬性。

Text

EntityReference

Text

表示元素或屬性中的文本內容。

None

CDATASection

表示文檔中的 CDATA 區段(文本不會被解析器解析)

None

Comment

表示註釋。

None

Entity

表示實體。

ProcessingInstruction

Comment

Text

CDATASection

EntityReference

Notation

表示在 DTD 中聲明的符號。

None

 

JavaDOM節點之間的繼承關係以下:

 

Node接口

DOM中,全部節點類型都繼承自Node類,它表明在DOM樹中的一個節點。Node接口定義了一些用於處理子節點的方法,可是並非全部的節點有子節點,好比Text節點並無子節點,於是向Text節點添加子節點(appendChild)會拋出DOMException

 

NodeNameNodeValueAttributes屬性

Node接口提供了getNodeName()getNodeValue()getAttributes()三個方法,以方便應用程序獲取這些信息,而不須要每次都轉換成不一樣子類以獲取這些信息。可是並非全部的節點類型都有NodeNameNodeValueAttributes信息,於是對那些不存在這些信息的節點能夠返回null。全部節點類型對這三個方法的返回值以下表:

節點類型

getNodeName()

getNodeValue()

getAttributes()

Document

「#document」

null

null

DocumentFragment

「#document-fragment」

null

null

DocumentType

DocumentType.name

null

null

EntityReference

實體引用名稱

null

null

Element

Element.tagName(qName)

null

NamedNodeMap

Attr

屬性名(Attr.name)

屬性值(Attr.value)

null

ProcessingInstruction

ProcessingInstruction.target

ProcessingInstruction.data

null

Comment

「#comment」

註釋文本(CharacterData.data)

null

Text

「#text」

節點內容(CharacterData.data)

null

CDATASection

「#cdata-section」

節點內容(CharacterData.data)

null

Entity

實體名稱

null

null

Notation

符號名稱

null

null

NodeValueNode接口還提供了setNodeValue(String nodeValue)方法,對那些getNodeValue()null的節點類型來講,調用該方法不會有任何影響,而對非nullnodeValue,若是它是隻讀的節點,調用該方法將會拋出DOMException。只有Element節點類型纔有屬性信息,於是只有Element節點的getAttributes()方法能返回NamedNodeMapNode接口還提供hasAttributes()方法以判斷當前節點是否存在屬性信息,也只有Element類型節點纔會返回true

NamedNodeMap接口實現了name=>Node的一個Map操做,對NamedNodeMap實例操做會影響其所在的Element節點中屬性的信息:

public interface NamedNodeMap {

    public Node getNamedItem(String name);

    public Node setNamedItem(Node arg);

    public Node removeNamedItem(String name);

    public Node item(int index);

    public int getLength();

    public Node getNamedItemNS(String namespaceURI, String localName);

    public Node setNamedItemNS(Node arg);

    public Node removeNamedItemNS(String namespaceURI, String localName);

}

 

TextContent屬性(setget

Node接口還提供了TextContent屬性,它以字符串的形式表示當前節點和它的全部子節點。對設置該屬性(非空非null值),它會移除當前節點的全部子節點,並用一個Text節點替代。讀取該屬性的值不會包含任何標籤字符,它也不會作任何解析,於是返回的文本會包含全部空格、換行等符號信息。對不一樣節點類型該屬性的內容以下:

 

節點類型

TextContent

ElementAttrEntityEntityReferenceDocumentFragment

將全部子節點的TextContent屬性鏈接在一塊兒組成的字符串(不包含CommentProcessingInstruction節點),若是當前節點沒有子節點,該值爲空

TextCDATASectionCommentProcessingInstruction

NodeValue

DocumentDocumentTypeNotation

null

 

NodeType屬性

DOM爲每種節點類型定義了一個short值:

NodeType

Named Constant

Node Value

Element

ELEMENT_NODE

1

Attr

ATTRIBUTE_NODE

2

Text

TEXT_NODE

3

CDATASection

CDATA_SECTION_NODE

4

EntityReference

ENTITY_REFERENCE_NODE

5

Entity

ENTITY_NODE

6

ProcessingInstruction

PROCESSING_INSTRUCTION_NODE

7

Comment

COMMENT_NODE

8

Document

DOCUMENT_NODE

9

DocumentType

DOCUMENT_TYPE_NODE

10

DocumentFragment

DOCUMENT_FRAGMENT_NODE

11

Notation

NOTATION_NODE

12

 

在節點樹中遍歷、查找方法

getParentNode():返回當前節點的父節點。AttrDocumentDocumentFragmentEntityNotation這些類型的節點沒有父節點。其餘類型都有可能有父節點。

getFirstChild():返回當前節點的第一個子節點,若是沒有,返回null

getLastChild():返回當前節點的最後一個子節點,若是沒有,返回null

getNextSibling():返回當前節點的下一個兄弟節點,若是沒有,返回null

getPreviousSibling():返回當前節點的上一個兄弟節點,若是沒有,返回null

getOwnerDocument():返回和當前節點關聯的Document節點,通常對DOM節點樹,Document是其根節點,於是全部子節點能夠經過該方法直接獲取根節點。對DocumentDocumentType節點,該方法返回null

hasChildNodes():判斷當前節點是否存在子節點。

 

修改子節點方法

appendChild(Node newChild):向該節點添加子節點(全部已存在的子節點以後),若是該新的節點已經在節點樹中了,該節點會先被移除。若是新節點是DocumentFragment類型,則新節點內部全部的節點都會添加到子節點列表中。因爲若是新添加的節點已存在在節點樹中,該節點會先被移除,於是新節點不能夠是當前節點的祖先節點或該節點自己;對不一樣類型的節點,其子節點的類型也是固定的,於是不能夠添加了當前節點不支持的子節點;另外,對Document節點,它只能存在一個Element節點和一個DocumentType節點。

removeChild(Node oldChild):移除當前節點中的oldChild子節點,並返回該節點。

replaceChild(Node newChild, Node oldChild):將oldChild子節點替換成newChild子節點,若是newChild節點類型是DocumentFragment,則全部DocumentFragment內部的節點都會插入到oldChild節點所在的位置,最後返回oldChild子節點。若是oldChild節點已存在節點樹中,則該節點會先被移除。

insertBefore(Node newChild, Node refChild):向已存在的refChild子節點以前插入新子節點newChild。若是refChildnull,則該方法如appendChild(),即向子節點最後插入新子節點newChild。若是newChild節點爲DocumentFragment,則插入的節點爲DocumentFragment中的全部節點。若是newChild節點存在節點樹中,該節點會先被移除。

 

命名空間支持

DOMLevel2開始提供對命名空間的支持。在XML中,只有Element節點和Attr節點存在命名空間的定義,並且屬性節點(Attr)的命名空間並不默認繼承自Element節點,而它須要本身顯示的定義所在的命名空間,不然默認沒有定義命名空間。

getNamespaceURI():獲取當前節點所在的命名空間,若是沒有定義返回null。它是經過在當前做用域中查找到的值。出了ElementAttr,其餘節點類型沒有命名空間定義。

getPrefix()/setPrefix(String prefix):命名空間前綴屬性,對非ElementAttr的節點,他們永遠返回null,對其設值不會有任何影響。

getLocalName():返回當前節點的本地名稱,即不包含命名空間的名字。

getBaseURI():不認識

lookupPrefix(String namespaceURI):經過namespaceURI查找命名空間前綴(prefix),從當前節點開始查找,忽略默認命名空間URI

lookupNamespaceURI(String prefix):經過prefix查找命名空間URI,從當前節點開始查找。若prefixnull,返回默認命名空間URI

isDefaultNamespace(String namespaceURI):判斷是不是默認的命名空間URI

 

其餘

isSupported(String feature, String version):返回DOM實現是否支持給定的FeatureVersion

getFeature(String feature, String version):返回實現該FeatureVersion的對象,有點難理解的方法,參考其中一個簡單實現(NodeImpl):

public Object getFeature(String feature, String version) {

    return isSupported(feature, version) ? this : null;

}

setUserData(String key, Object data, UserDAtaHandler handler)/getUserData(String key):向該Node中添加用戶自定義的數據,應用程序能夠在接下來的邏輯中從該Node使用getUserData(String key)方法從新獲取該數據。其中UserDataHandler實例會在該Node每次被複制(Node.cloneNode())、導入(Document.importNode())、重命名(Document.renameNode())、從其餘Document中引入(Document.adoptNode())、刪除(刪除在Java中的實現不可靠)時被調用:

public interface UserDataHandler {

    public static final short NODE_CLONED = 1;

    public static final short NODE_IMPORTED = 2;

    public static final short NODE_DELETED = 3;

    public static final short NODE_RENAMED = 4;

    public static final short NODE_ADOPTED = 5;

public void handle(short operation, String key, Object data, Node src, Node dst);

}

cloneNode(boolean deep):拷貝當前Node,可是不會拷貝UserData屬性和ParentNode屬性。拷貝Element節點是,若是deepfalse,不會拷貝全部字節點,但會拷貝全部屬性節點以及定義的具備默認值的屬性。而拷貝Attr節點,無論deepfalse仍是true,都會拷貝Attr的屬性值和其全部子節點。拷貝EntityReference節點時,無論deepfalse仍是true,都會拷貝相應的Entity。對全部其餘節點類型的拷貝都是指返回自身引用。

normalize():在編程構建DOM樹時,能夠構建出一棵不標準的DOM樹,好比存在兩個相鄰的Text節點,空節點之類的,normalize方法能夠合併兩個相鄰的Text節點、移除空節點等。

compareDocumentPosition(Node other):比較兩個節點的相對位置,返回的short值只是一些簡單的信息,能夠有以下值:

public static final short DOCUMENT_POSITION_DISCONNECTED = 0x01;

public static final short DOCUMENT_POSITION_PRECEDING = 0x02;

public static final short DOCUMENT_POSITION_FOLLOWING = 0x04;

public static final short DOCUMENT_POSITION_CONTAINS = 0x08;

public static final short DOCUMENT_POSITION_CONTAINED_BY = 0x10;

public static final short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;

isSameNode(Node other):判斷兩個節點是否相同,比較引用,包括代理引用。

isEqualNode(Node other):判斷兩個節點是否相同,比較內容。normalize操做會影響該方法的結果,於是通常先調用normalize方法後,比較。parentNodeownedDocumentuserData等屬性不會影響該方法的比較,具體參考API文檔。

Document接口

DocumentDOM樹的根節點,因爲其餘節點類型都要基於Document而存在,於是Document接口還提供了建立其餘節點的工廠方法。

Document級別操做

Document子節點只能包含一個DocumentTypeElement節點,於是Document提供了兩個方法直接返回這兩個節點,而對其餘子節點(ProcessingInstructionComment)則須要經過Node接口提供的操做讀取:

public DocumentType getDoctype();

public Element getDocumentElement();

public DOMImplementation getImplementation();

DOMImplementation接口,它提供了一些和DOM節點無關的操做:

public interface DOMImplementation {

    public boolean hasFeature(String feature, String version);

public DocumentType createDocumentType(String qualifiedName,

String publicId, String systemId);

    public Document createDocument(String namespaceURI, String qualifiedName,

                                   DocumentType doctype);

    public Object getFeature(String feature, String version);

}

每一個XML文件均可以指定編碼類型、standalone屬性、XML版本、是否執行嚴格的語法檢查、Document的位置:

public String getInputEncoding();

public String getXmlEncoding();

public boolean getXmlStandalone();

public void setXmlStandalone(boolean xmlStandalone);

public String getXmlVersion();

public void setXmlVersion(String xmlVersion);

public boolean getStrictErrorChecking();

public void setStrictErrorChecking(boolean strictErrorChecking);

public String getDocumentURI();

public void setDocumentURI(String documentURI);

 

工廠方法

Document提供了建立其餘全部節點類型的工廠方法,而且支持帶命名空間的ElementAttr的建立:

public Element createElement(String tagName);

public Element createElementNS(String namespaceURI, String qualifiedName);

public Attr createAttribute(String name);

public Attr createAttributeNS(String namespaceURI, String qualifiedName);

public DocumentFragment createDocumentFragment();

public Text createTextNode(String data);

public Comment createComment(String data);

public CDATASection createCDATASection(String data);

public ProcessingInstruction createProcessingInstruction(String target, String data);

public EntityReference createEntityReference(String name);

 

查找Element節點

1.       經過ID屬性查找:public Element getElementById(String elementId);

2.       使用標籤名查找:public NodeList getElementsByTagName(String tagname);

3.       使用命名空間URI和本地標籤名:

public NodeList getElementsByTagNameNS(String namespaceURI, String localName);

其中NodeList接口提供了遍歷Node的操做:

public interface NodeList {

    public Node item(int index);

    public int getLength();

}

 

配置與規格化

Node.normalize()Document也定義本身的normalizeDocument(),它根據當前的配置規格化DOM樹(替換EntityReferenceEntity,合併相鄰的Text節點等),若是配置驗證信息,在操做同時還會驗證DOM樹的合法性。Document能夠經過DOMConfiguration接口實例配置:

public DOMConfiguration getDomConfig();

public interface DOMConfiguration {

    public void setParameter(String name, Object value);

    public Object getParameter(String name);

    public boolean canSetParameter(String name, Object value);

    public DOMStringList getParameterNames();

}

如設置validate屬性:

DOMConfiguration docConfig = myDocument.getDomConfig();

docConfig.setParameter("validate", Boolean.TRUE);

其中DOMStringList提供了遍歷String List的操做,相似NodeList

public interface DOMStringList {

    public String item(int index);

    public int getLength();

    public boolean contains(String str);

}

 

其餘

public Node importNode(Node importedNode, boolean deep);

向當前Document中導入存在於另外一個Document中的節點而不改變另外一個DocumentDOM樹的結構。

public Node adoptNode(Node source);

向當前Document中加入存在於另外一個Document中的節點,並將該節點從另外一個Document中移除。

public Node renameNode(Node n, String namespaceURI, String qualifiedName);

重命名一個ElementAttr節點

 

Element接口

Element節點表示XML文件中的一個標籤,於是它最經常使用。因爲在全部節點類型中,只有Element節點能夠包含書,於是全部和屬性具體相關的操做都定義在Element接口中:

public String getAttribute(String name);

public void setAttribute(String name, String value);

public void removeAttribute(String name);

public Attr getAttributeNode(String name);

public Attr setAttributeNode(Attr newAttr)

public Attr removeAttributeNode(Attr oldAttr);

public String getAttributeNS(String namespaceURI, String localName);

public void setAttributeNS(String namespaceURI, String qualifiedName, String value);

public void removeAttributeNS(String namespaceURI, String localName);

public Attr getAttributeNodeNS(String namespaceURI, String localName);

public Attr setAttributeNodeNS(Attr newAttr);

public boolean hasAttribute(String name);

public boolean hasAttributeNS(String namespaceURI, String localName);

public void setIdAttribute(String name, boolean isId);

public void setIdAttributeNS(String namespaceURI, String localName, boolean isId);

public void setIdAttributeNode(Attr idAttr, boolean isId);

 

Element還提供了兩個使用標籤名查找子Element方法以及返回當前Element的標籤名方法,該標籤名爲包含命名空間前綴的全名:

public NodeList getElementsByTagName(String name);

public NodeList getElementsByTagNameNS(String namespaceURI, String localName);

public String getTagName();

 

Attr接口

Attr節點表示Element節點中的一個屬性,通常一個Element中存在什麼樣的屬性會在相應的SchemaDTD中定義。雖然Attr繼承自Node接口,可是它並不屬於Element節點的子節點,於是它不屬於DOM樹中的節點,它只存在於Element節點中,因此它的parentNodepreviousSiblingnextSibling的值都爲null。然而Attr能夠存在子節點,它的子節點能夠是Text節點或EntityReference節點。

對在Schema中有默認值定義的Attr,移除和初始化時會新建立一個Attr,它的specified屬性爲false,值爲Schema中定義的默認值。在調用Document.normalizeDocument()方法時,全部specified屬性爲falseAttr值都會從新計算,若是它在Schema中沒有默認值定義,則該Attr會被移除。

屬性是具備NameValue的值對,其中建立一個Attr實例後,Name不能夠改變,要改變則須要建立新的Attr實例,而Value值能夠改變。specified屬性代表該屬性的值是用戶設置的(true)仍是Schema中默認提供的。id屬性代表該Attr是否是其所屬Elementid屬性,一個id屬性的值能夠在一個Document中惟一的標識一個Element。因爲全部Attr都是基於Element的,於是能夠獲取其所屬的Element

public interface Attr extends Node {

    public String getName();

    public boolean getSpecified();

    public String getValue();

    public void setValue(String value);

    public Element getOwnerElement();

    public TypeInfo getSchemaTypeInfo();

    public boolean isId();

}

DocumentType接口

每一個Document都有doctype屬性,它包含了DTD文件信息(位置、文件名等),同時它還提供了讀取DTD文件中定義的EntityNotation的集合,即它是對XML文件中如下語句的封裝:

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">  

其中:

namebeans

publicId-//SPRING//DTD BEAN//EN

systemIdhttp://www.springframework.org/dtd/spring-beans.dtd

entitiesnotations爲該DTD文件中定義的EntityNotation的集合

public interface DocumentType extends Node {

    public String getName();

    public NamedNodeMap getEntities();

    public NamedNodeMap getNotations();

    public String getPublicId();

    public String getSystemId();

    public String getInternalSubset();

}

ProcessingInstruction接口

XML文件中還能夠定義一些指令以供一些解析器使用該信息對該文件作正確的處理,在DOM中使用ProcessingInstruction接口對該定義進行抽象:

<?xml-stylesheet href="show.css" type="text/css" ?>

ProcessingInstruction額外定義了兩個屬性:targetdata

targetxml-stylesheet

datahref="show.css" type="text/css"

public interface ProcessingInstruction extends Node {

    public String getTarget();

    public String getData();

    public void setData(String data);

}

CharacterData接口

CharacterData接口繼承自Node接口,它是全部字符相關節點的父接口,定義了全部字符相關的操做:定義屬性、添加、插入、刪除、替換、取子串等。

public interface CharacterData extends Node {

    public String getData();

    public void setData(String data);

    public int getLength();

    public String substringData(int offset, int count);

    public void appendData(String arg);

    public void insertData(int offset, String arg);

    public void deleteData(int offset, int count);

    public void replaceData(int offset, int count, String arg);

}

Text接口

Text接口繼承自CharacterData接口,它表示文本節點,通常做爲ElementAttr的子節點,而它自己沒有子節點。Text定義了一個文本節點,如Element的文本ContentAttr的值。若文本里麪包含特殊字符(如’<’, ‘>’等)須要轉義。在操做DOM樹時,用戶能夠插入多個Text節點,在Node.normalize()處理時會合並兩個各相鄰的Text節點。

Text節點提供了除對字符數據操做的其餘額外操做:

splitText()Text節點分割成兩個相鄰的Text節點,即新分割出的Text節點爲以前Text節點的兄弟節點

isElementContentWhitespace():判斷當前Text節點是否存在Element Content Whitespace,沒讀懂。

getWholeText():當存在多個相鄰的Text節點時,該屬性會返回全部相鄰Text節點的值。

replaceWholeText():替換全部相鄰Text節點爲新設置的節點(多是當前節點自己)。若是其中有一個節點沒法移除(如包含EntityReference的節點),則會拋出DOMException

public interface Text extends CharacterData {

    public Text splitText(int offset);

    public boolean isElementContentWhitespace();

    public String getWholeText();

    public Text replaceWholeText(String content);

}

CDATASection接口

CDATASection接口繼承自Text接口,它相似於Text節點,所不一樣的是全部的CDATASection節點都包含在<![CDATA[「Content need not to be escaped」]]>中,而且若是其內容包含特殊字符不須要轉義。不一樣於Text節點,在Node.normalize()階段,相鄰的兩個CDATASection節點不會被合併。

public interface CDATASection extends Text {

}

Comment接口

Comment接口繼承自CharacterData接口,它是對XML文件中註釋語句的抽象,它只包含註釋的字符串信息,沒有額外定義的行爲:

public interface Comment extends CharacterData {

}

Entity接口

Entity接口是對一個實體定義的抽象,即它是對DTD文件中如下定義的抽象:

<!ENTITY JENN SYSTEM "http://images.about.com/sites/guidepics/html.gif" NDATA gif>

Entity接口定義了systemIdpublicIdnotationName等信息,而對其餘信息則在其子節點中顯示,如Entity可能指向另外一個外部文件,或者直接定義Entity的值(對這個,貌似我始終返回null,按網上的說法,這個是xercesbug),如如下定義:

<!ENTITY name "cnblog">

<!ENTITY copyright SYSTEM "copyright.desc">

另外Entity還定義了一些編碼和XML版本的信息:

public interface Entity extends Node {

    public String getPublicId();

    public String getSystemId();

    public String getNotationName();

    public String getInputEncoding();

    public String getXmlEncoding();

    public String getXmlVersion();

}

EntityReference接口

EntityReference節點表示對一個Entity的引用。

public interface EntityReference extends Node {

}

Notation接口

Notation接口是對DTDNotation定義的抽象:

<!NOTATION gif SYSTEM "image/gif">

一個Notation包含namenodeName)、systemIdpublicId信息:

public interface Notation extends Node {

    public String getPublicId();

    public String getSystemId();

}

DocumentFragment接口

DocumentFragment是對DOM樹片斷的抽象,從而能夠將部分DOM樹節點做爲一個集合來處理,如插入到一個Document中的某個節點中,實際插入的是DocumentFragment中全部的子節點。把DOM樹的部分節點做爲一個總體來看待部分能夠經過Document來實現,然而在部分實現中,Document是一個重量級的對象,而DocumentFragment則能夠保證它是一個輕量級的對象,由於它沒有Document存在的那麼限制,這也是DocumentFragment存在的緣由。DocumentFragment的定義以下:
public interface DocumentFragment extends Node {

}

相關文章
相關標籤/搜索