添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

DOM 是解析 XML 文件的官方标准,它与平台和语言无关。 DOM 解析将整个 XML 文件载入并组装成一棵 DOM 节点树,然后通过遍历、查找节点以读取 XML 文件中定义的数据。由于 DOM 解析中把所有节点都载入到内存中,因而它比较耗资源,而且它需要把整棵节点树构建完成后开始读取数据,因而它相对性能也不好;不过由于它在内存中保存了 DOM 节点树,因而它可以多次读取,并且它的节点树定义比较容易理解,因而操作起来比较简单。关于性能,有人对一些常用的解析方法做了比较:

单位: s (秒)转自: http://www.cnblogs.com/hedalixin/archive/2011/12/04/2275453.html

Node 接口

DOM 中,所有节点类型都继承自 Node 类,它代表在 DOM 树中的一个节点。 Node 接口定义了一些用于处理子节点的方法,但是并不是所有的节点有子节点,比如 Text 节点并没有子节点,因而向 Text 节点添加子节点( appendChild )会抛出 DOMException

NodeName NodeValue Attributes 属性

Node 接口提供了 getNodeName() getNodeValue() getAttributes() 三个方法,以方便应用程序获取这些信息,而不需要每次都转换成不同子类以获取这些信息。但是并不是所有的节点类型都有 NodeName NodeValue Attributes 信息,因而对那些不存在这些信息的节点可以返回 null 。所有节点类型对这三个方法的返回值如下表:

NodeValue Node 接口还提供了 setNodeValue(String nodeValue) 方法,对那些 getNodeValue() null 的节点类型来说,调用该方法不会有任何影响,而对非 null nodeValue ,如果它是只读的节点,调用该方法将会抛出 DOMException 。只有 Element 节点类型才有属性信息,因而只有 Element 节点的 getAttributes() 方法能返回 NamedNodeMap Node 接口还提供 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 属性( set get

Node 接口还提供了 TextContent 属性,它以字符串的形式表示当前节点和它的所有子节点。对设置该属性(非空非 null 值),它会移除当前节点的所有子节点,并用一个 Text 节点替代。读取该属性的值不会包含任何标签字符,它也不会做任何解析,因而返回的文本会包含所有空格、换行等符号信息。对不同节点类型该属性的内容如下:

在节点树中遍历、查找方法

getParentNode() :返回当前节点的父节点。 Attr Document DocumentFragment Entity Notation 这些类型的节点没有父节点。其他类型都有可能有父节点。

getFirstChild() :返回当前节点的第一个子节点,如果没有,返回 null

getLastChild() :返回当前节点的最后一个子节点,如果没有,返回 null

getNextSibling() :返回当前节点的下一个兄弟节点,如果没有,返回 null

getPreviousSibling() :返回当前节点的上一个兄弟节点,如果没有,返回 null

getOwnerDocument() :返回和当前节点关联的 Document 节点,一般对 DOM 节点树, Document 是其根节点,因而所有子节点可以通过该方法直接获取根节点。对 Document DocumentType 节点,该方法返回 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 。如果 refChild null ,则该方法如 appendChild() ,即向子节点最后插入新子节点 newChild 。如果 newChild 节点为 DocumentFragment ,则插入的节点为 DocumentFragment 中的所有节点。如果 newChild 节点存在节点树中,该节点会先被移除。

命名空间支持

DOM Level2 开始提供对命名空间的支持。在 XML 中,只有 Element 节点和 Attr 节点存在命名空间的定义,而且属性节点( Attr )的命名空间并不默认继承自 Element 节点,而它需要自己显示的定义所在的命名空间,否则默认没有定义命名空间。

getNamespaceURI() :获取当前节点所在的命名空间,如果没有定义返回 null 。它是通过在当前作用域中查找到的值。出了 Element Attr ,其他节点类型没有命名空间定义。

getPrefix()/setPrefix(String prefix) :命名空间前缀属性,对非 Element Attr 的节点,他们永远返回 null ,对其设值不会有任何影响。

getLocalName() :返回当前节点的本地名称,即不包含命名空间的名字。

getBaseURI() :不认识

lookupPrefix(String namespaceURI) :通过 namespaceURI 查找命名空间前缀( prefix ),从当前节点开始查找,忽略默认命名空间 URI

lookupNamespaceURI(String prefix) :通过 prefix 查找命名空间 URI ,从当前节点开始查找。若 prefix null ,返回默认命名空间 URI

isDefaultNamespace(String namespaceURI) :判断是否是默认的命名空间 URI

isSupported(String feature, String version) :返回 DOM 实现是否支持给定的 Feature Version

getFeature(String feature, String version) :返回实现该 Feature Version 的对象,有点难理解的方法,参考其中一个简单实现( 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 节点是,如果 deep false ,不会拷贝所有字节点,但会拷贝所有属性节点以及定义的具有默认值的属性。而拷贝 Attr 节点,不管 deep false 还是 true ,都会拷贝 Attr 的属性值和其所有子节点。拷贝 EntityReference 节点时,不管 deep false 还是 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 方法后,比较。 parentNode ownedDocument userData 等属性不会影响该方法的比较,具体参考 API 文档。

Document 接口

Document DOM 树的根节点,由于其他节点类型都要基于 Document 而存在,因而 Document 接口还提供了创建其他节点的工厂方法。

Document 级别操作

Document 子节点只能包含一个 DocumentType Element 节点,因而 Document 提供了两个方法直接返回这两个节点,而对其他子节点( ProcessingInstruction Comment )则需要通过 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 提供了创建其他所有节点类型的工厂方法,并且支持带命名空间的 Element Attr 的创建:

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 树(替换 EntityReference Entity ,合并相邻的 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 中的节点而不改变另一个 Document DOM 树的结构。

public Node adoptNode(Node source);

向当前 Document 中加入存在于另一个 Document 中的节点,并将该节点从另一个 Document 中移除。

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

重命名一个 Element Attr 节点

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 中存在什么样的属性会在相应的 Schema DTD 中定义。虽然 Attr 继承自 Node 接口,但是它并不属于 Element 节点的子节点,因而它不属于 DOM 树中的节点,它只存在于 Element 节点中,所以它的 parentNode previousSibling nextSibling 的值都为 null 。然而 Attr 可以存在子节点,它的子节点可以是 Text 节点或 EntityReference 节点。

对在 Schema 中有默认值定义的 Attr ,移除和初始化时会新创建一个 Attr ,它的 specified 属性为 false ,值为 Schema 中定义的默认值。在调用 Document.normalizeDocument() 方法时,所有 specified 属性为 false Attr 值都会重新计算,如果它在 Schema 中没有默认值定义,则该 Attr 会被移除。

属性是具有 Name Value 的值对,其中创建一个 Attr 实例后, Name 不可以改变,要改变则需要创建新的 Attr 实例,而 Value 值可以改变。 specified 属性表明该属性的值是用户设置的( true )还是 Schema 中默认提供的。 id 属性表明该 Attr 是不是其所属 Element id 属性,一个 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 文件中定义的 Entity Notation 的集合,即它是对 XML 文件中以下语句的封装:

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

name beans

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

systemId http://www.springframework.org/dtd/spring-beans.dtd

entities notations 为该 DTD 文件中定义的 Entity Notation 的集合

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 额外定义了两个属性: target data

target xml-stylesheet

data href = "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 接口,它表示文本节点,一般作为 Element Attr 的子节点,而它本身没有子节点。 Text 定义了一个文本节点,如 Element 的文本 Content Attr 的值。若文本里面包含特殊字符(如 ’<’, ‘>’ 等)需要转义。在操作 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 接口定义了 systemId publicId notationName 等信息,而对其他信息则在其子节点中显示,如 Entity 可能指向另一个外部文件,或者直接定义 Entity 的值(对这个,貌似我始终返回 null ,按网上的说法,这个是 xerces bug ),如以下定义:

<!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 接口是对 DTD Notation 定义的抽象:

<!NOTATION gif SYSTEM "image/gif" >

一个 Notation 包含 name nodeName )、 systemId publicId 信息:

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 {