使用 .NET Framework 中的 XML(DOM)
2007-08-31 14:41 Jacky_Xu 阅读(869) 评论(0) 编辑 收藏 举报.NET Framework 中的 XML 目标是:
- 符合 W3C 标准。
- 扩展性。
- 可插接式结构。
- 性能。
- 与 ADO.NET 紧密集成。
符合标准
符合标准意味着类完全符合 XML、命名空间、XSLT、XPath、架构和文档对象模型 (DOM) 的当前 W3C 建议标准。符合标准确保了互操作性并使跨平台应用程序开发变得容易。
最显著的一点是,.NET Framework 中的 XML 类支持 W3C XML 架构定义语言 (XSD) 1.0 建议。.NET Framework 中有一些 XML 类提供验证,还有一个对象模型可用于在内存中生成 XSD 架构。可针对架构、DTD、XDR 和 XSD 进行验证的快速只进分析器称为 XmlValidatingReader。XmlValidatingReader 是符合标准的 XML 分析器。XmlSchemaCollection 类可用于在使用 XmlValidatingReader 时缓存常用的 XSD 或 XDR 架构。
.NET Framework 中有一个 XML 类集提供架构对象模型 (SOM),使用该模型可以以编程方式生成和编译 XSD 架构。XmlSchema 类表示 XSD 架构。使用 XmlReader 和 XmlWriter 类可以加载和保持这些架构。
XmlDocument 类实现文档对象模型级别 1 和级别 2 建议,它是根据 .NET Framework 的通用设计指南定制的。例如,方法名称使用大写字母。
XslTransform 类符合用于使用 XSLT 转换文档的 XSL 转换 (XSLT) 1.0 版建议和 XML 路径语言 (XPath) 1.0 建议。
扩展性
.NET Framework 中的 XML 通过使用抽象基类和虚方法而变得可扩展。这种扩展性(或子类创建)由 XmlReader、XmlWriter 和 XPathNavigator 抽象类阐释,这些抽象类是使得能够在不同的存储区或数据源上开发新实现的类和公开 XML 的类。例如,XPathNavigator 是集成了 XPath 查询引擎的 API,该引擎可在现有存储区(如文件系统、注册表和关系数据库)上实现。它不仅将数据显示为 XML,还使用 XPath API 查询方法(如 Select)的默认实现,提供对不同数据源的 XPath 查询支持。
扩展性的另一个示例是 XmlReader,它提供一个用于对存储区进行快速只进分析的 API,公开在它移动通过流时所发现的 XML Infoset。.NET Framework 中的 XML 具有以下 XmlReader 实现:用于读取流的 XmlTextReader 类、用于读取节点树的 XmlNodeReader 类和用于在 XmlTextReader 上分层验证支持的 XmlValidatingReader。
XmlWriter 使用基于推送的 API 生成 XML 流。.NET Framework 中的 XML 具有一个称为 XmlTextWriter 的 XmlWriter 实现。
可以进一步从这些类的实现派生,创建可以实现特定处理的其他读取器。例如,通过从 XmlTextReader 派生,创建一个称为 MyXmlTextReader 的自定义读取器,可以创建具有特定于应用程序处理需求的逻辑的读取器版本。
XmlResolver 抽象类提供 XML 文档中引用的外部资源的挂钩。这使您可以用特定于应用程序(如数据库)的方式来缓存资源,或通过从该类派生来解析具有不同协议的资源。XmlResolver 的两个实现 XmlUrlResolver 和 XmlSecureResolver 将解析使用 http://
、https://
和 file://
协议的资源。
可插接式结构
.NET Framework 中的 XML 具有可插接式结构。在此基于流的结构中,可插接式意味着可以很容易替代 .NET Framework 中基于这些抽象类的组件。可插接式结构还意味着数据可以在组件之间以流的形式传送,以及插入到此流中的新组件可以改变处理。例如,来自 XML Web services 的流可用 XmlTextReader 分析。可以使用 XmlTextReader 创建 XmlDocument,然后可以使用 XmlDocument 创建 XmlNodeReader。
另一个示例是从 XmlReader 加载 DOM(XmlDocument 类)并使用 XmlWriter 保存输出。通过扩展现有类来创建自己的这些类的实现,可以影响 XmlDocument 类的行为。例如,如果创建了一个称为 MyXmlFileReader 的 XmlReader 实现,并且它可以将文件系统公开为 XML,则可以从此读取器加载 XmlDocument。或者可以从 XmlTextReader 继承,创建一个新的自定义读取器将以属性为中心的文档转换为以元素为中心的文档,它可以用于加载 XmlDocument。这为基于现有类的新类提供了可插接式结构。
将组件插接在一起的另一个示例是在转换过程中使用不同的数据存储区(如 XPathDocument 和 XmlDocument)。这些数据存储区可用 XslTransform 类转换,输出然后可以流入另一个存储区或以流的形式从 XML Web services 返回。下图说明了这一点。
使用 XslTransform 类对数据进行流处理
通过使用 IXPathNavigable 接口,实现 XPathNavigator 的任何存储区都可以插入到 XslTransform 类中以允许对该存储区进行 XSLT 转换。XmlDocument、XPathDocument 和 XmlDataDocument 类具有此能力。然后,来自 XslTransform 的流式输出可以发送到可插接式结构样式的 XmlReader 或 XmlWriter。
性能
.NET Framework 中的 XML 类表示低级别 XML 处理组件,这些组件不仅用作 .NET Framework 的一部分,还用于将 XML 集成到应用程序中。这些类需要具有极高的性能。
.NET Framework 中的 XML 类旨在支持基于流处理的模型,它具有下列特性:
- 使用 XmlReader 进行的只进、拉模型分析所用的缓存最小。
- 使用 XmlValidatingReader 进行只进验证。
- XPathNavigator 的创新游标样式导航,它将节点创建最小化为单个虚节点,但仍提供对文档的随机访问。这不需要在内存中生成完整的节点树,如 DOM。
- 来自 XslTransform 类的增量流式输出。
XPathDocument 是 XPath 查询的优化只读存储区,建议在需要进行 XSLT 处理的任何时候都使用它。通过使用此存储区和 XslTransform 类,可以实现高性能的 XSLT 转换。
与 ADO.NET 集成
通过 XML 类和 ADO.NET 之间的紧密集成,关系数据和 XML 在 .NET Framework 中结合在一起。
DataSet 组件表示断开连接的数据库,它能够使用 XmlReader 和 XmlWriter 类读取和写入 XML、将其内部关系架构结构保持为 XML 架构 (XSD) 以及从 XML 文档推测架构结构。
通过将 DataSet 与 XmlDocument 同步,使其中一个所维护的数据在适当的时候在另一个中得到更新,XmlDataDocument 确实超出了 XML 和关系数据领域的边界。由于 XmlDocument 能够存储半结构化数据,因此在获得 XML 存储区的所有功能的同时,DataSet 基于它的架构提供 XML 上的关系视图。
有关 .NET Framework 如何通过 DataSet 对象和 XmlDataDocument 对象启用对数据的关系和分层表示形式的实时同步访问的更多信息,请参见关系数据和 ADO.NET 的 XML 集成
文档对象模型 (DOM) 类是 XML 文档的内存中表示形式。DOM 使您能够以编程方式读取、操作和修改 XML 文档。XmlReader 类也读取 XML,但它提供非缓存的只进、只读访问。这意味着使用 XmlReader 无法编辑属性值或元素内容,或无法插入和移除节点。编辑是 DOM 的主要功能。XML 数据在内存中表示是常见的结构化方法,尽管实际的 XML 数据在文件中时或从另一个对象传入时以线性方式存储。以下是 XML 数据。
输入
<?xml version="1.0"?>
<books>
<book>
<author>Carson</author>
<price format="dollar">31.95</price>
<pubdate>05/01/2001</pubdate>
</book>
<pubinfo>
<publisher>MSPress</publisher> <state>WA</state>
</pubinfo> </books>
下图显示将此 XML 数据读入 DOM 结构中时内存的构造。
XML 文档结构
在 XML 文档结构中,此图中的每个圆圈表示一个节点(称为 XmlNode 对象)。XmlNode 对象是 DOM 树中的基本对象。XmlDocument 类(它扩展 XmlNode)支持用于对整个文档执行操作(例如,将文档加载到内存中或将 XML 保存到文件)的方法。此外,XmlDocument 提供了查看和操作整个 XML 文档中的节点的方法。XmlNode 和 XmlDocument 都具有性能和可用性增强,并具有进行下列操作的方法和属性:
- 访问和修改特定于 DOM 的节点,如元素节点、实体引用节点等。
- 除检索节点包含的信息(如元素节点中的文本)外,还检索全部节点。
注意 如果应用程序不需要 DOM 提供的结构或编辑功能,则 XmlReader 和 XmlWriter 类提供对 XML 的非缓存的只进流访问。有关更多信息,请参见用 XmlReader 读取 XML 和用 XmlWriter 编写 XML。
Node 对象具有一组方法和属性以及基本和定义完善的特性。其中的某些特性包括:
- 节点有单个父节点,父节点是节点的上一级节点。唯一没有父级的节点是文档根,因为它是顶级节点,包含了文档本身和文档片段。
- 大多数节点可以有多个子节点,子节点是节点的下一级节点。以下是可以有子节点的节点类型列表。
- Document
- DocumentFragment
- EntityReference
- Element
- Attribute
XmlDeclaration、Notation、Entity、CDATASection、Text、Comment、ProcessingInstruction 和 DocumentType 节点没有子级。
- 处于同一级别、在关系图中由 book 和 pubinfo 节点表示的节点是同辈。
DOM 的一个特性是它处理属性的方式。属性是不属于父子关系和同辈关系的节点。属性被视为元素节点的属性,由名称和值对组成。例如,如果存在由与元素 price
关联的 format="dollar
" 组成的 XML 数据,则单词 format
是名称,format
属性的值是 dollar
。为检索 price 节点的 format="dollar"
属性,可以在游标位于 price
元素节点时调用 GetAttribute。有关更多信息,请参见访问 DOM 中的属性。
将 XML 读入内存时会创建节点。然而,并非所有节点都是同一类型。XML 中的元素具有不同于处理指令的规则和语法。因此,在读取各种数据时,将向每个节点分配一种节点类型。此节点类型确定节点的特性和功能。
有关在内存中生成的节点类型的更多信息,请参见 XML 节点类型。有关在节点树中创建的对象的更多信息,请参见将对象层次结构映射到 XML 数据。
Microsoft 扩展了 W3C DOM 级别 1 和级别 2 中可用的 API,使 XML 文档的使用更容易。在完全支持 W3C 标准的同时,附加的类、方法和属性增加了使用 W3C XML DOM 可以完成的功能以外的功能。新类使您能够访问关系数据,为您提供与 ADO.NET 数据同步、同时将数据作为 XML 公开的方法。有关更多信息,请参见使 DataSet 与 XmlDataDocument 同步。
DOM 对于将 XML 数据读入内存以更改其结构、添加或移除节点,或者与元素包含的文本一样修改节点所保存的数据最有用。不过,在其他方案中,还有其他比 DOM 更快的类。对于对 XML 的快速非缓存只进流访问,请使用 XmlReader 和 XmlWriter。如果需要用游标模型和 XPath 进行随机访问,请使用 XPathNavigator 类。
当将 XML 文档作为节点树读入内存时,这些节点的节点类型在创建这些节点时确定。XML DOM 具有多种节点类型,这些类型由 W3C 确定并在 1.1.1 节“DOM 结构模型”中列出。下表列出了节点类型、分配给该节点类型的对象以及每种节点类型的简短说明。
DOM 节点类型 | 对象 | 说明 |
---|---|---|
Document | XmlDocument 类 | 树中所有节点的容器。它也称作文档根,文档根并非总是与根元素相同。 |
DocumentFragment | XmlDocumentFragment 类 | 包含一个或多个不带任何树结构的节点的临时袋。 |
DocumentType | XmlDocumentType 类 | 表示 <!DOCTYPE...> 节点。 |
EntityReference | XmlEntityReference 类 | 表示非扩展的实体引用文本。 |
Element | XmlElement 类 | 表示元素节点。 |
Attr | XmlAttribute 类 | 为元素的属性。 |
ProcessingInstruction | XmlProcessingInstruction 类 | 为处理指令节点。 |
Comment | XmlComment 类 | 注释节点。 |
Text | XmlText 类 | 属于某个元素或属性的文本。 |
CDATASection | XmlCDataSection 类 | 表示 CDATA。 |
Entity | XmlEntity 类 | 表示 XML 文档(来自内部文档类型定义 (DTD) 子集或来自外部 DTD 和参数实体)中的 <!ENTITY ...> 声明。 |
Notation | XmlNotation 类 | 表示 DTD 中声明的表示法。 |
尽管属性 (attr) 在 W3C DOM 级别 1 的 1.2 节“Fundamental Interfaces”(基本接口)中作为节点列出,但不能将其视为任何元素节点的子级。
下表显示了 W3C 未定义的附加节点类型,但它们可作为 XmlNodeType 枚举在 .NET 对象模型中使用。因此,这些节点类型不存在匹配的 DOM 节点类型列。
节点类型 | 说明 |
---|---|
XmlDeclaration | 表示声明节点 <?xml version="1.0"...> 。 |
XmlSignificantWhitespace | 表示有效空白(混合内容中的空白)。 |
XmlWhitespace | 表示元素内容中的空白。 |
EndElement | 当 XmlReader 到达元素的末尾时返回。
示例 XML:</item> 有关更多信息,请参见 XmlNodeType 枚举。 |
EndEntity | 由于调用 ResolveEntity 而在 XmlReader 到达实体替换的末尾时返回。有关更多信息,请参见 XmlNodeType 枚举。 |
若要查看读入 XML 并对代码类型使用 case 构造以打印有关节点及其内容的信息的代码示例,请参见 XmlSignificantWhitespace.NodeType 属性。
有关节点类型及其等效对象名的对象层次结构的更多信息,请参见 XML 文档对象模型 (DOM) 层次结构。有关在节点树中创建的对象的更多信息,请参见将对象层次结构映射到 XML 数据。
当 XML 文档在内存中时,概念上的表示形式是树。编程时可使用对象层次结构访问树节点。下面的示例显示 XML 内容如何成为节点。
当将 XML 读入 DOM 中时,各片段被翻译为节点,这些节点保留有关自身的附加元数据,如它们的节点类型和值。节点类型是节点的对象,它确定可执行的操作以及可设置或检索的属性。
如果具有下面的简单 XML:
输入
<book> <title>The Handmaid's Tale</title> </book>
输入在内存中表示为具有分配的节点类型属性的下列节点树:
Book 和 title 节点树表示形式
book
元素成为 XmlElement 对象,下一个元素 title
也成为 XmlElement,而元素内容成为 XmlText 对象。查看 XmlElement 的方法和属性可以得知,这些方法和属性不同于 XmlText 对象的方法和属性。因此知道 XML 标记成为的节点类型至关重要,因为其节点类型确定可以执行的操作。
下面的示例读入 XML 数据并根据节点类型写出不同的文本。将下面的 XML 数据文件 items.xml 用作输入:
输入
<?xml version="1.0"?> <!-- This is a sample XML document --> <!DOCTYPE Items [<!ENTITY number "123">]> <Items> <Item>Test with an entity: &number;</Item> <Item>test with a child element <more/> stuff</Item> <Item>test with a CDATA section <![CDATA[<456>]]> def</Item> <Item>Test with a char entity: A</Item> <!-- Fourteen chars in this element.--> <Item>1234567890ABCD</Item> </Items>
下面的代码示例读取 items.xml 文件并显示每个节点类型的信息。
[Visual Basic] Imports System Imports System.IO Imports System.Xml Public Class Sample Private Const filename As String = "items.xml" Public Shared Sub Main() Dim txtreader As XmlTextReader = Nothing Dim reader As XmlValidatingReader = Nothing Try ' Load the reader with the data file and 'ignore all whitespace nodes. txtreader = New XmlTextReader(filename) txtreader.WhitespaceHandling = WhitespaceHandling.None ' Implement the validating reader over the text reader. reader = New XmlValidatingReader(txtreader) reader.ValidationType = ValidationType.None ' Parse the file and display each of the nodes. While reader.Read() Select Case reader.NodeType Case XmlNodeType.Element Console.Write("<{0}>", reader.Name) Case XmlNodeType.Text Console.Write(reader.Value) Case XmlNodeType.CDATA Console.Write("<![CDATA[{0}]]>", reader.Value) Case XmlNodeType.ProcessingInstruction Console.Write("<?{0} {1}?>", reader.Name, reader.Value) Case XmlNodeType.Comment Console.Write("<!--{0}-->", reader.Value) Case XmlNodeType.XmlDeclaration Console.Write("<?xml version='1.0'?>") Case XmlNodeType.Document Case XmlNodeType.DocumentType Console.Write("<!DOCTYPE {0} [{1}]", reader.Name, reader.Value) Case XmlNodeType.EntityReference Console.Write(reader.Name) Case XmlNodeType.EndElement Console.Write("</{0}>", reader.Name) End Select End While Finally If Not (reader Is Nothing) Then reader.Close() End If End Try End Sub 'Main ' End class End Class 'Sample [C#] using System; using System.IO; using System.Xml; public class Sample { private const String filename = "items.xml"; public static void Main() { XmlTextReader txtreader = null; XmlValidatingReader reader = null; try { // Load the reader with the data file and ignore // all whitespace nodes. txtreader = new XmlTextReader(filename); txtreader.WhitespaceHandling = WhitespaceHandling.None; // Implement the validating reader over the text reader. reader = new XmlValidatingReader(txtreader); reader.ValidationType = ValidationType.None; // Parse the file and display each of the nodes. while (reader.Read()) { switch (reader.NodeType) { case XmlNodeType.Element: Console.Write("<{0}>", reader.Name); break; case XmlNodeType.Text: Console.Write(reader.Value); break; case XmlNodeType.CDATA: Console.Write("<![CDATA[{0}]]>", reader.Value); break; case XmlNodeType.ProcessingInstruction: Console.Write("<?{0} {1}?>", reader.Name, reader.Value); break; case XmlNodeType.Comment: Console.Write("<!--{0}-->", reader.Value); break; case XmlNodeType.XmlDeclaration: Console.Write("<?xml version='1.0'?>"); break; case XmlNodeType.Document: break; case XmlNodeType.DocumentType: Console.Write("<!DOCTYPE {0} [{1}]", reader.Name, reader.Value); break; case XmlNodeType.EntityReference: Console.Write(reader.Name); break; case XmlNodeType.EndElement: Console.Write("</{0}>", reader.Name); break; } } } finally { if (reader!=null) reader.Close(); } } } // End class
此示例的输出显示数据到节点类型的映射。
输出
<?xml version='1.0'?><!--This is a sample XML document --><!DOCTYPE Items [<!ENTITY number "123">]<Items><Item>Test with an entity: 123</Item><Item>test with a child element <more> stuff</Item><Item>test with a CDATA section <![CDATA[<456>]]> def</Item><Item>Test with a char entity: A</Item><--Fourteen chars in this element.--><Item>1234567890ABCD</Item></Items>
通过逐行获取输入并使用从代码生成的输出,可以使用下表分析哪个节点测试生成哪些输出行,从而了解哪些 XML 数据成为哪种节点类型。
输入 | 输出 | 节点类型测试 |
---|---|---|
<?xml version="1.0"?> | <?xml version='1.0'?> | XmlNodeType.XmlDeclaration |
<!-- 这是一个示例 XML 文档 --> | <!-- 这是一个示例 XML 文档 --> | XmlNodeType.Comment |
<!DOCTYPE Items [<!ENTITY number "123">]> | <!DOCTYPE Items [<!ENTITY number "123">] | XmlNodeType.DocumentType |
<Items> | <Items> | XmlNodeType.Element |
<Item> | <Item> | XmlNodeType.Element |
Test with an entity: &number;</Item> | Test with an entity: 123 | XmlNodeType.Text |
</Item> | </Item> | XmlNodeType.EndElement |
<Item> | <Item> | XmNodeType.Element |
test with a child element | test with a child element | XmlNodeType.Text |
<more> | <more> | XmlNodeType.Element |
stuff | stuff | XmlNodeType.Text |
</Item> | </Item> | XmlNodeType.EndElement |
<Item> | <Item> | XmlNodeType.Element |
test with a CDATA section | test with a CDATA section | XmlTest.Text |
<![CDATA[<456>]]> | <![CDATA[<456>]]> | XmlTest.CDATA |
def | def | XmlNodeType.Text |
</Item> | </Item> | XmlNodeType.EndElement |
<Item> | <Item> | XmlNodeType.Element |
Test with a char entity: A | Test with a char entity: A | XmlNodeType.Text |
</Item> | </Item> | XmlNodeType.EndElement |
<!-- Fourteen chars in this element.--> | <--Fourteen chars in this element.--> | XmlNodeType.Comment |
<Item> | <Item> | XmlNodeType.Element |
1234567890ABCD | 1234567890ABCD | XmlNodeType.Text |
</Item> | </Item> | XmlNodeType.EndElement |
</Items> | </Items> | XmlNodeType.EndElement |
您必须知道分配的节点类型,因为节点类型控制哪种操作有效,以及可以设置和检索哪种属性。
当将数据加载到 DOM 中时,空白的节点创建受 PreserveWhitespace 标志的控制。有关更多信息,请参见加载 DOM 时的空白和有效空白处理。
若要向 DOM 添加新节点,请参见将节点插入 XML 文档中。若要从 DOM 中移除节点,请参见移除 XML 文档中的节点、内容和值。若要修改 DOM 中的节点的内容,请参见修改 XML 文档中的节点、内容和值。
有两种创建 XML 文档的方法。一种方法是创建不带参数的 XmlDocument。另一种方法是创建一个 XmlDocument 并将 XmlNameTable 作为参数传递给它。下面的示例显示如何创建一个新的不带参数的空 XmlDocument。
[Visual Basic] Dim doc As New XmlDocument() [C#] XmlDocument doc = new XmlDocument();
创建文档后,可通过 Load 方法从字符串、流、URL、文本读取器或 XmlReader 派生类中加载数据到该文档中。还存在另一种加载方法,即 LoadXML 方法,此方法从字符串中读取 XML。有关各种 Load 方法的更多信息,请参见将 XML 文档读入 DOM。
有一个名为 XmlNameTable 的类。此类是原子化字符串对象的表。该表为 XML 分析器提供了一种高效的方法,即对 XML 文档中所有重复的元素和属性名使用相同的字符串对象。创建文档时(如上所示),将自动创建 XmlNameTable,并在加载此文档时用属性和元素名加载 XmlNameTable。如果已经有一个包含名称表的文档,且这些名称在另一个文档中会很有用,则可使用将 XmlNameTable 作为参数的 Load 方法创建一个新文档。使用此方法创建文档后,该文档使用现有 XmlNameTable,后者包含所有已从其他文档加载到此文档中的属性和元素。它可用于有效地比较元素和属性名。有关 XmlNameTable 的更多信息,请参见使用 XmlNameTable 的对象比较。有关参考,请参见 XmlNameTable 成员。
XML 信息从不同的格式读入内存。读取源包括字符串、流、URL、文本读取器或 XmlReader 的派生类。
Load 方法将文档置入内存中并包含可用于从每个不同的格式中获取数据的重载方法。还存在 LoadXML 方法,该方法从字符串中读取 XML。
不同的 Load 方法将影响在加载 DOM 时会创建哪些节点。下表列出了一些 Load 方法的区别以及讲述这些区别的主题。
Subject(主题) | 主题 |
---|---|
空白节点的创建。 | 用来加载 DOM 的对象对 DOM 中生成的空白和有效空白节点有影响。有关更多信息,请参见加载 DOM 时的空白和有效空白处理。 |
从特定节点开始加载 XML 或加载整个 XML 文档。 | 使用采用 XmlReader 为参数的 Load 方法,可以从一个特定节点加载数据到 DOM 中。有关更多信息,请参见从读取器中加载数据。 |
在加载前验证 XML。 | XmlValidatingReader 是唯一派生的 XmlReader,它可验证 XML。有关更多信息,请参见使用 XmlValidatingReader 进行 XML 验证。 |
下面的示例显示用 LoadXML 方法加载 XML 然后将数据保存到一个名为 data.xml 的文本文件。
[Visual Basic] Option Explicit Option Strict Imports System Imports System.IO Imports System.Xml Public Class Sample Public Shared Sub Main() ' Create the XmlDocument. Dim doc As New XmlDocument() doc.LoadXml(("<book genre='novel' ISBN='1-861001-57-5'>" & _ "<title>Pride And Prejudice</title>" & _ "</book>")) ' Save the document to a file. doc.Save("data.xml") End Sub 'Main End Class 'Sample [C#] using System; using System.IO; using System.Xml; public class Sample { public static void Main() { // Create the XmlDocument. XmlDocument doc = new XmlDocument(); doc.LoadXml("<book genre='novel' ISBN='1-861001-57-5'>" + "<title>Pride And Prejudice</title>" + "</book>"); // Save the document to a file. doc.Save("data.xml"); } }有时,现有 XML 会包含
<?xml:stylesheet
...?>
形式的样式表指令。Microsoft Internet Explorer 接受此指令作为<?xml-stylesheet
...?>
语法的替换形式。当 XML 数据包含<?xml:stylesheet
...?>
指令时(如下面的数据所示),试图将此数据加载到 DOM 中将引发异常。<?xml version="1.0" ?> <?xml:stylesheet type="text/xsl" href="test2.xsl"?> <root> <test>Node 1</test> <test>Node 2</test> </root>发生这种情况是由于
<?xml:stylesheet
...?>
被视为 DOM 的无效 ProcessingInstruction。根据 XML 规范中的命名空间,任何 ProcessingInstruction 都只能是 NCNames(无冒号名称),与 QNames(限定名)相反。根据 XML 规范中的命名空间的第 6 节,使 Load 和 LoadXml 方法符合此规范所产生的影响是,在文档中:
- 所有元素类型和属性名都应包含零个或一个冒号。
- 任何实体名称、ProcessingInstruction 目标或表示法名称都不能包含冒号。
<?xml:stylesheet
...?>
包含有一个冒号,违反了上面的第二条规则。根据“将样式表与 XML 文档关联 1.0 版”建议(位于 www.w3.org/TR/xml-stylesheet),将 XSL 样式表与 XML 文档关联的处理指令是
<?xml-stylesheet
...?>
(用短划线代替冒号)。如果使用 Load 方法和 XmlReader 参数加载文档,则此行为与从其他格式加载数据时发生的行为有所不同。如果读取器处于初始状态(检查方法是查看 ReadState 属性是否等于 ReadState.Initial),则 Load 使用读取器中的全部内容并从读取器中的所有数据生成 DOM。
如果读取器已定位在文档中某个位置的节点上,然后将读取器传递给 Load 方法,则 Load 将试图读取当前节点及其所有同辈。直到关闭当前路径的结束标记进入内存中,此操作才完成。Load 成功与否取决于加载尝试发生时读取器所在的节点,因为 Load 验证读取器中的 XML 的格式是否正确。如果 XML 的格式不正确,Load 将引发异常。例如,下面的节点集包含两个根级别元素,且 XML 的格式不正确,因此 Load 引发异常。
- Comment 节点,后跟 Element 节点,后跟 Element 节点,后跟 EndElement 节点。
下面的节点集创建不完整的 DOM,原因是没有根级别元素。
- Comment 节点,后跟 ProcessingInstruction 节点,后跟 Comment 节点,后跟 EndElement 节点。
这不引发异常,并且加载数据。可以向这些节点的顶部添加根元素并创建保存时不会发生错误的格式正确的 XML。
如果读取器定位在对文档的根级别无效的叶节点上(如空白或属性节点),则读取器继续读取,直到定位在一个可用于根的节点上。文档在此时开始加载。
默认情况下,Load 不使用 DTD 或架构验证检查 XML 是否有效。它只检查 XML 的格式是否正确。为使验证发生,需传入 XmlValidatingReader,指定相应的 ValidationType 并提供 ValidationEventHandler。有关 XmlValidatingReader 的更多信息,请参见使用 XmlValidatingReader 进行 XML 验证。有关事件处理的更多信息,请参见使用 XmlNodeChangedEventArgs 的 XML 文档中的事件处理。
如果要在加载 XML 时对照 DTD 或架构对其进行验证,需要将 XmlValidatingReader 传递给 Load 方法。事先必须已将 XmlValidatingReader 的 ValidationType 属性设置为 ValidationType 枚举中某个值(ValidationType.None 除外),并且需要提供 validationEventHandler。在执行此操作的过程中,Load 会通知您在加载过程中发现的验证错误。除非 ValidationEventHandler 停止加载过程,否则,即使发现错误,文档仍会加载到内存中。有关 XmlValidatingReader 的更多信息,请参见使用 XmlValidatingReader 进行 XML 验证。
可以通过插入新节点来修改文档。节点可以是新创建的,存在于同一文档中,也可以从另一个文档导入。有关创建新节点的更多信息,请参见在 DOM 中创建新节点。有关复制现有节点的更多信息,请参见复制现有节点、将现有节点从一个文档复制到另一个文档或复制文档片段。
XmlDocument 具有用于所有节点类型的 Create 方法。为该方法提供名称(需要时)以及那些具有内容的节点(如文本节点)的内容或其他参数,这样便可创建节点。下面的方法需要填充名称和几个其他参数以创建相应的节点。
- CreateComment
- CreateCDataSection
- CreateDocumentFragment
- CreateDocumentType
- CreateElement
- CreateProcessingInstruction
- CreateTextNode
- CreateXmlDeclaration
- CreateWhitespace
- CreateSignificantWhitespace
其他节点类型不仅仅只要求向参数提供数据。
有关属性的信息,请参见为 DOM 中的元素创建新属性。有关元素和属性名验证的信息,请参见创建新节点时的 XML 元素和属性名验证。有关如何创建实体引用,请参见创建新实体引用。有关命名空间如何影响实体引用扩展的信息,请参见命名空间对包含元素和属性的新节点的实体引用扩展的影响。
创建新节点后,有几个方法可用于将其插入到树中。下表列出了这些方法,并描述了新节点在 DOM 中的位置。
方法 节点位置 InsertBefore 插入到引用节点之前。例如,在位置 5 插入新节点: [Visual Basic] Dim refChild As XmlNode = node.ChildNodes(4) 'The reference is zero-based.node.InsertBefore(newChild, refChild); [C#] XmlNode refChild = node.ChildNodes[4]; //The reference is zero-based. node.InsertBefore(newChild, refChild);有关更多信息,请参见 XmlNode.InsertBefore 方法。
InsertAfter 插入到引用节点之后。例如: [Visual Basic] node.InsertAfter(newChild, refChild) [C#] node.InsertAfter(newChild, refChild);有关更多信息,请参见 XmlNode.InsertAfter 方法。
AppendChild 将节点添加到给定节点的子节点列表的末尾。如果所添加的节点是 XmlDocumentFragment,则会将文档片段的全部内容移至该节点的子列表中。有关更多信息,请参见 XmlNode.AppendChild 方法。 PrependChild 将节点添加到给定节点的子节点列表的开头。如果所添加的节点是 XmlDocumentFragment,则会将文档片段的全部内容移至该节点的子列表中。有关更多信息,请参见 XmlNode.PrependChild 方法。 Append 将 XmlAttribute 节点追加到与元素关联的属性集合的末尾。有关更多信息,请参见 XmlAttributeCollection.Append 方法。
创建新属性不同于创建其他节点类型,因为属性不是节点,而是元素节点的属性并包含在与元素关联的 XmlAttributeCollection 中。有多种方法可创建属性并将其附加到元素:
- 获取元素节点并使用 SetAttribute 将属性添加到该元素的属性集合。
- 使用 CreateAttribute 方法创建 XmlAttribute 节点,获取元素节点,然后使用 SetAttributeNode 将节点添加到该元素的属性集合。
下面的示例显示如何使用 SetAttribute 方法将属性添加到元素。
[Visual Basic] Imports System Imports System.IO Imports System.Xml public class Sample public shared sub Main() Dim doc as XmlDocument = new XmlDocument() doc.LoadXml("<book xmlns:bk='urn:samples' bk:ISBN='1-861001-57-5'>" & _ "<title>Pride And Prejudice</title>" & _ "</book>") Dim root as XmlElement = doc.DocumentElement 'Add a new attribute. root.SetAttribute("genre", "urn:samples", "novel") Console.WriteLine("Display the modified XML...") Console.WriteLine(doc.InnerXml) end sub end class [C#] using System; using System.IO; using System.Xml; public class Sample { public static void Main() { XmlDocument doc = new XmlDocument(); doc.LoadXml("<book xmlns:bk='urn:samples' bk:ISBN='1-861001-57-5'>" + "<title>Pride And Prejudice</title>" + "</book>"); XmlElement root = doc.DocumentElement; // Add a new attribute. root.SetAttribute("genre", "urn:samples", "novel"); Console.WriteLine("Display the modified XML..."); Console.WriteLine(doc.InnerXml); }
下面的示例显示一个用 CreateAttribute 方法创建的新属性。然后显示使用 SetAttributeNode 方法添加到 book 元素的属性集合的属性。
已知下列 XML:
[Visual Basic, C#]
<book genre='novel' ISBN='1-861001-57-5'>
<title>Pride And Prejudice</title>
</book>
创建一个新属性并为其提供值:
[Visual Basic] Dim attr As XmlAttribute = doc.CreateAttribute("publisher") attr.Value = "WorldWide Publishing" [C#] XmlAttribute attr = doc.CreateAttribute("publisher"); attr.Value = "WorldWide Publishing";
将其附加到此元素:
[Visual Basic] doc.DocumentElement.SetAttributeNode(attr) [C#] doc.DocumentElement.SetAttributeNode(attr);
输出
<book genre="novel" ISBN="1-861001-57-5" publisher="WorldWide Publishing"> <title>Pride And Prejudice</title> </book>
完整代码示例位于 XmlDocument.CreateAttribute 方法。
还可以创建一个 XmlAttribute 节点并使用 InsertBefore 或 InsertAfter 方法将其放在集合中的适当位置。如果该属性集合中已存在一个同名属性,则从该集合中移除现有的 XmlAttribute 节点并插入新的 XmlAttribute 节点。这与 SetAttribute 方法执行的方式相同。这些方法(作为参数)将现有节点作为参考点以执行 InsertBefore 和 InsertAfter。如果不提供指示插入新节点位置的引用节点,则 InsertAfter 方法的默认设置是在集合的开头插入新节点。如果未提供任何引用节点,则 InsertBefore 的默认位置是集合的末尾。
如果创建了属性的 XmlNamedNodeMap,则可以使用 SetNamedItem 方法按名称添加属性。有关更多信息,请参见 NamedNodeMap 和 NodeList 中的节点集合。
默认属性
如果创建一个声明为具有默认属性的元素,则 DOM 创建一个带默认值的新默认属性并将其附加到该元素。此时还创建默认属性的子节点。
属性子节点
属性节点的值成为它的子节点。只存在两种类型的有效子节点:XmlText 节点和 XmlEntityReference 节点。从 FirstChild 和 LastChild 等方法将这些节点按子节点来处理这一点上说,它们是子节点。当试图移除属性或属性子节点时,属性这种具有子节点的特性很重要。有关更多信息,请参见移除 DOM 中元素节点的属性。
DOM 中有许多方法和属性可用来选择节点,如 SelectSingleNode、ChildNodes[int i]、Attributes[int i]。选择节点后,可以使用适用于特定节点类型的插入方法之一将该节点插入到树中。将节点插入到树中的唯一限制是在插入节点后文档仍必须是格式正确的。将现有节点插入到 DOM 树中时,该节点从其原始位置移除并添加到它的目标位置。
ImportNode 方法是将节点或整个节点子树从一个 XmlDocument 复制到另一个 XmlDocument 的机制。调用返回的节点是源文档节点的副本,其中包括属性值、节点名、节点类型以及所有与命名空间相关的属性(如前缀、本地名称和命名空间 URI)。源文档不更改。导入该节点后,仍需使用插入节点的方法之一将该节点添加到树种。
节点附加到它的新文档后,它归此新文档所有。原因是每个节点在创建后都具有所属文档,即使节点是在单独的文档片段中创建的。这是 DOM 的要求并由 XmlDocument 类的工厂创建设计强制,例如,CreateElement 是创建新节点的唯一方法。
根据导入节点的节点类型和 deep 参数的值,将复制适当的附加信息。此方法尝试镜像当 XML 片段或 HTML 源从一个文档复制到另一个文档时的预期行为,以说明一个事实:对于 XML,两个文档可能具有不同的 DTD。
下表描述了可导入的每种节点类型的特定行为。
节点类型 | deep 参数为 true | deep 参数为 false |
---|---|---|
XmlAttribute | XmlAttribute.Specified 属性在 XmlAttribute 上设置为 true。递归导入源 XmlAttribute 的子代,并重组结果节点以构成相应的子树。 | deep 参数不适用于 XmlAttribute 节点,因为这些节点在导入时总是携带其子节点。 |
XmlCDataSection | 复制该节点,包括复制其数据。 | 复制该节点,包括复制其数据。 |
XmlComment | 复制该节点,包括复制其数据。 | 复制该节点,包括复制其数据。 |
XmlDocumentFragment | 递归导入源代码的子代,并重组结果节点以构成相应的子树。 | 创建空的 XmlDocumentFragment。 |
XmlDocumentType | 复制该节点,包括复制其数据。 | 复制该节点,包括复制其数据。 |
XmlElement | 递归导入源元素的子代,并重组结果节点以构成相应的子树。
注意 不复制默认属性。如果导入到的文档定义该元素名称的默认属性,则分配这些默认属性。 |
导入源元素的指定属性节点,并将生成的 XmlAttribute 节点附加到新元素。不复制子代节点。
注意 不复制默认属性。如果导入到的文档定义该元素名称的默认属性,则分配这些默认属性。 |
XmlEntityReference | 因为源文档和目标文档可能以不同的方式定义实体,所以该方法仅复制 XmlEntityReference 节点。不包括替换文本。如果目标文档定义了实体,则给它赋值。 | 因为源文档和目标文档可能以不同的方式定义实体,所以该方法仅复制 XmlEntityReference 节点。不包括替换文本。如果目标文档定义了实体,则给它赋值。 |
XmlProcessingInstruction | 从导入的节点复制目标和数据值。 | 从导入的节点复制目标和数据值。 |
XmlText | 复制该节点,包括复制其数据。 | 复制该节点,包括复制其数据。 |
XmlSignificantWhitespace | 复制该节点,包括复制其数据。 | 复制该节点,包括复制其数据。 |
XmlWhitespace | 复制该节点,包括复制其数据。 | 复制该节点,包括复制其数据。 |
XmlDeclaration | 从导入的节点复制目标和数据值。 | 从导入的节点复制目标和数据值。 |
所有其他节点类型 | 不能导入这些节点类型。 | 不能导入这些节点类型。 |
*注意 尽管可以导入 DocumentType 节点,但一个文档只能有一个 DocumentType。因此,导入文档类型后,在将其插入到树中之前必须确保文档中没有任何文档类型。有关移除节点的信息,请参见移除 XML 文档中的节点、内容和值。
可以创建一个 XmlDocumentFrament 节点,然后在它下面添加节点。当使用 InsertNode 方法插入 XmlDocumentFragment 时,不复制 XmlDocumentFrament 节点,但在 DOM 中插入它的子节点。
DOM 在内存中之后,可以移除树中的节点,或移除特定节点类型中的内容和值。有关如何从文档中移除叶节点或整个节点子树的信息,请参见从 DOM 中移除节点。有关如何移除元素属性的信息,请参见移除 DOM 中元素节点的属性。有关如何移除节点内容但将节点保留在树中的信息,请参见移除 DOM 中的节点内容。
若要从 DOM 中移除节点,请使用 RemoveChild 方法移除特定节点。移除节点时,此方法移除属于所移除的节点的子树(即如果它不是叶节点)。
若要从 DOM 中移除多个节点,请使用 RemoveAll 方法移除当前节点的所有子级和属性(如果适用)。
如果使用 XmlNamedNodeMap,则可以使用 RemoveNamedItem 方法移除节点。
若要移除属性,请参见移除 DOM 中元素节点的属性。
有多种方法可以移除属性。一种方法是从属性集合中移除它们。为此,请执行下列步骤:
- 使用
XmlAttributeCollection attrs = elem.Attributes;
获取元素的属性集合。 - 使用以下三种方法之一移除属性集合中的属性:
- 使用 XmlAttributeCollection.Remove 方法移除特定属性。
- 使用 XmlAttributeCollection.RemoveAll 方法移除集合中的所有属性,使元素不具有任何属性。
- 使用 XmlAttributeCollection.RemoveAt 方法移除属性集合中的属性(通过使用其索引号)。
下列方法移除元素节点中的属性。
- 使用 XmlElement.RemoveAllAttributes 移除属性集合。
- 使用 XmlElement.RemoveAttribute 方法按名称移除集合中的单个属性。
- 使用 XmlElement.RemoveAttributeAt 按索引号移除集合中的单个属性。
另一个替换方法是获取元素,获取属性集合中的属性并直接移除属性节点。若要获取属性集合中的属性,可使用名称 XmlAttribute attr = attrs["attr_name"];
、索引 XmlAttribute attr = attrs[0];
或用命名空间 XmlAttribute attr = attrs["attr_localName", "attr_namespace"]
完全限定该名称。
无论用于移除属性的方法是什么,当移除在 DTD 中定义为默认属性的属性时有特殊限制。除非移除了默认属性所属的元素,否则不能移除默认属性。已声明了默认属性的元素总是存在默认属性。移除 XmlAttributeCollection 或 XmlElement 中的默认属性可导致将替换属性插入到元素的 XmlAttributeCollection 中,并初始化为已声明的默认值。如果将某个元素定义为 <book att1="1" att2="2" att3="3"></book>
,则将得到一个具有三个已声明的默认属性的 book
元素。DOM 实现保证只要此 book
元素存在,它就具有这三个默认属性:att1
、att2
和 att3
。
当用 XmlAttribute 调用时,RemoveAll 将该属性值设置为 String.Empty,因为属性没有值便无法存在。
对于从 XmlCharacterData 继承的节点类型(即 XmlComment、XmlText、XmlCDataSection、XmlWhitespace 和 XmlSignificantWhitespace 节点类型),可以使用 DeleteData 方法移除字符,此方法从节点中移除某个范围的字符。如果要完全移除内容,则移除包含此内容的节点。如果要保留节点,但节点内容不正确,则修改内容。有关修改节点内容的信息,请参见修改 XML 文档中的节点、内容和值。
有多种方法可以修改文档中的节点和内容。您可以:
- 使用 XmlNode.Value 方法更改节点值。
- 通过用新节点替换节点来修改全部节点集。这可使用 XmlNode.InnerXml 属性完成。
- 使用 XmlNode.ReplaceChild 方法用新节点替换现有节点。
- 使用 XmlCharacterData.AppendData 方法、XmlCharacterData.InsertData 方法或 XmlCharacterData.ReplaceData 方法将附加字符添加到从 XmlCharacter 类继承的节点。
- 对从 XmlCharacterData 继承的节点类型使用 DeleteData 方法移除某个范围的字符来修改内容。
- 使用 SetAttribute 方法更新属性值。如果不存在属性,SetAttribute 创建一个新属性;如果存在属性,则更新属性值。
更改节点值的一个简单方法是使用 node.Value = "new value";
。下表列出了此单个代码行作用于的节点类型,以及对于该节点类型将更改的确切数据。
节点类型 | 更改的数据 |
---|---|
Attribute | 属性的值。 |
CDATASection | CDATASection 的内容。 |
Comment | 注释的内容。 |
ProcessingInstruction | 内容(不包括目标)。 |
Text | 文本的内容。 |
XmlDeclaration | 声明的内容(不包括 <?xml 和 ?> 标记)。 |
Whitespace | 空白的值。可以将该值设置为四个可识别的 XML 空白字符之一:空格、制表符、回车或换行。 |
SignificantWhitespace | 有效空白的值。可以将该值设置为四个可识别的 XML 空白字符之一:空格、制表符、回车或换行。 |
该表中未列出的任何节点类型都不是设置了值的有效节点类型。设置任何其他节点类型的值都将引发 InvalidOperationException。
InnerXml 属性更改当前节点的子节点标记。设置此属性将用给定字符串的分析内容替换子节点。分析在当前命名空间上下文中执行。此外,InnerXml 移除多余的命名空间声明。因此,大量的剪切和粘贴操作并不会使文档的大小因多余的命名空间声明而增加。有关显示命名空间对 InnerXml 操作的影响的代码示例,请参见 XmlDocument.InnerXml 属性。
当使用 ReplaceChild 和 RemoveChild 方法时,这两个方法返回已替换或移除的节点。然后,此节点可以重新插入到 DOM 中的其他位置。ReplaceChild 方法对插入到文档中的节点执行两个验证检查。第一个检查确保该节点成为某个节点的子级,这个节点可具有其类型的子节点。第二个检查确保插入的节点不是它成为其子级的节点的上级。违犯这两个条件中的任何一个都将引发 InvalidOperationException 异常。
向可编辑的节点中添加或从中移除只读子级是有效的。然而,试图修改只读节点本身将引发 InvalidOperationException 异常。修改 XmlEntityReference 节点的子级便属于这种情况。该子级是只读的,因此无法修改。任何修改它们的尝试都将引发 InvalidOperationException。
当加载并保存 XmlDocument 时,所保存的文档在下列方面可能与原始文档存在差异:
- 如果在调用 Save 方法前将 PreserveWhitespace 属性设置为 true,则在输出中将保留文档中的空白;否则,如果此属性为 false,XmlDocument 会自动缩进输出。
- 各个属性之间的所有空白都缩减为一个空白字符。
- 更改元素间的空白。保留有效空白,但不保留无效空白。但是,当保存文档时,文档在默认情况下将使用 XmlTextWriter Indenting 模式整齐地打印输出,以使其更易于阅读。
- 属性值两边所用的引号字符在默认情况下更改为双引号。可以使用 XmlTextWriter 的 QuoteChar 属性将引号字符设置为双引号或单引号。
- 默认情况下,保留像
&abc;
这样的常规实体。但如果构造一个具有 ExpandEntities 的默认 EntityHandling 设置的 XmlValidatingReader,然后调用 Load,则常规实体将被扩展,从而丢失已保存文档中的常规实体。 - 默认情况下,扩展像
{
这样的数字字符实体。 - 不保留输入文档中的字节顺序标记。除非显式创建指定不同编码的 XML 声明,否则 UCS-2 保存为 UTF-8。
- 如果要将 XmlDocument 写出到文件或流中,则写出的输出与文档内容相同。也就是说,仅当文档中包含 XmlDeclaration 时才写出 XmlDeclaration,并且写出文档时所使用的编码与声明节点中给定的编码相同。
写出 XmlDeclaration
除了 Save 和 WriteContentTo 的 XmlDocument 方法外,OuterXml、InnerXml 和 WriteTo 的 XmlDocument 和 XmlDeclaration 方法也创建 XML 声明。
对于 OuterXml、InnerXml 的 XmlDocument 方法以及 Save(Stream stm)、Save(String filename)、WriteTo 和 WriteConentTo 方法,在 XML 声明中写出的编码是从 XmlDeclaration 节点中获取的。如果不存在 XmlDeclaration 节点,则不写出 XmlDeclaration。如果 XmlDeclaration 节点中没有编码,则在 XML 声明中不写出编码。
Save(TextWriter writer) 和 Save(XmlWriter writer) 方法总写出 XmlDeclaration。这些方法从其写入的编写器中获取编码。也就是说,编写器的编码值重写文档和 XmlDeclaration 对象的编码。例如,下列代码不在输出文件 out.xml 中的 XML 声明中写出编码。
[Visual Basic] Dim tw As New XmlTextWriter("out.xml", Nothing) doc.load("text.xml") doc.Save(tw) [C#] XmlTextWriter tw = new XmlTextWriter("out.xml", null); doc.load("text.xml"); doc.Save(tw);
对于 Save(XmlTextWriter writer) 方法,通过 XmlWriter 类中的 WriteStartDocument 方法写出 XML 声明。因此,改写 WriteStartDocument 方法将更改文档开头的写出方式。
对于 OuterXml、WriteTo 和 InnerXml 的 XmlDeclaration 方法,如果未设置 Encoding 属性,则不写出任何编码。否则,在 XML 声明中写出的编码与 Encoding 属性中的编码相同。
用 OuterXml 属性写出文档内容
OuterXml 属性是 Microsoft 对 W3C 文档对象模型标准的扩展。OuterXml 属性用于获取整个 XML 文档的标记,或只获取单个节点及其子节点的标记。OuterXml 返回表示给定节点及其所有子节点的标记。
下面的代码示例显示了如何将整个文档保存为一个字符串。
[Visual Basic]
Dim mydoc As New XmlDocument()
' Perform application needs here, like mydoc.Load("myfile");
' Now save the entire document to a string variable called "xml".
Dim xml As String = mydoc.OuterXml
[C#]
XmlDocument mydoc = new XmlDocument(); // Perform application needs here, like mydoc.Load("myfile"); // Now save the entire document to a string variable called "xml". string xml = mydoc.OuterXml;
下面的代码示例显示了如何只保存文档元素。
[Visual Basic]
' For the content of the Document Element only.
Dim xml As String = mydoc.DocumentElement.OuterXml
[C#]
// For the content of the Document Element only. string xml = mydoc.DocumentElement.OuterXml;
与此相反,如果您需要子节点的内容,可以使用 InnerText 属性。
OuterXml 属性有一个方面影响它从文档中生成的输出,例如,当您在 XML 中将一个元素定义为 EntityReference 节点时。在 DTD 中,您已用一个默认属性定义了该元素。OuterXml 属性不写出该元素的默认属性。例如,假定已声明了下面这一常规实体,替换文本在一个称为 013.ent 的文件中,如下所示:
013.ent
<e/>
例如,下面这一称为 X_Entity.XML 的 XML 中包含一个 DTD,其元素定义为具有默认属性:
X_Entity.XML
<!DOCTYPE doc [
<!ELEMENT doc (e)><!ELEMENT e (#PCDATA)><!ATTLIST e a1 CDATA "a1 default" a2 NMTOKENS "a2 default"><!ENTITY x SYSTEM "013.ent">]><doc>&x;</doc>
在分析此 XML 时,代码行 <!ENTITY x SYSTEM "013.ent
"> 定义了在何处查找实体 x
的替换文本。在分析该文档并遇到 &x;
EntityReference 时,将找到 013.ent 文件并将 &x;
替换为其定义。其定义的重要部分变成了如下所示的 xml 数据:
<doc>
<e a1="a1 default" a2="a2 default" />
</doc>
然而,在使用 OuterXml 属性时,实际的输出是:
输出
<!DOCTYPE doc [
<!ELEMENT doc (e)><!ELEMENT e (#PCDATA)><!ATTLIST e a1 CDATA "a1 default" a2 NMTOKENS "a2 default"><!ENTITY x SYSTEM "013.ent">]><doc><e /></doc>
------------------------- AttributeCount 2
下面的代码用来测试默认属性并显示不包含 DTD 中定义的默认属性的输出。
[Visual Basic] Imports System Imports System.Data Imports System.Xml Imports System.Xml.XPath Imports System.Runtime.Remoting Namespace TestSimple Public Class MyTestApp Public Shared Sub Main() Dim treader As New XmlTextReader("X_Entity.xml") Dim vrdr As New XmlValidatingReader(treader) Dim xDoc As New XmlDocument() xDoc.Load(vrdr) Console.WriteLine(("AttributeCount " + CType(xDoc.DocumentElement.ChildNodes(0), XmlElement).Attributes.Count)) Console.WriteLine("-------------------------") Console.WriteLine(xDoc.OuterXml) End Sub 'Main End Class 'MyTestApp End Namespace 'TestSimple [C#] using System; using System.Data; using System.Xml; using System.Xml.XPath; using System.Runtime.Remoting; namespace TestSimple { public class MyTestApp { public static void Main() { XmlTextReader treader = new XmlTextReader("X_Entity.xml"); XmlValidatingReader vrdr = new XmlValidatingReader(treader); XmlDocument xDoc = new XmlDocument(); xDoc.Load (vrdr); Console.WriteLine(xDoc.OuterXml); Console.WriteLine("-------------------------"); Console.WriteLine("AttributeCount " + ((XmlElement)(xDoc.DocumentElement.ChildNodes[0])).Attributes.Count); } } }DOM 包含的方法允许使用 XPath 导航查询 DOM 中的信息。可以使用 XPath 查找单个特定节点,或查找与某个条件匹配的所有节点。如果不使用 XPath,则检索 DOM 中的一个或多个节点将需要大量导航代码。而使用 XPath 只需要一行代码。例如,下面的示例显示如何从一个名为 doc 的 XmlDocument 中检索单个 book 节点。
[Visual Basic] Dim bookNode as XmlNode bookNode = nav.SelectSingleNode("//book") [C#] XmlNode bookNode = nav.SelectSingleNode("//book");为找到一个特定节点,可使用 SelectSingleNode 方法。如果有多个节点与搜索条件匹配,则 SelectSingleNode 按文档顺序从树的顶部向下导航,返回第一个与搜索条件匹配的节点。若要将多个节点检索到一个节点集中,可使用 SelectNodes 方法。
SelectNodes 返回一个节点集,该节点集被放到 XmlNodeList 对象中。已对 XmlNodelist 进行了枚举以提供
foreach
语句功能。这提供了依次通过 XmlNodeList 中的节点的能力。下面的示例显示一个 XPath 查询,该查询返回包含书作者 Smith 的所有节点。
[Visual Basic] Dim root As XmlNode = doc.DocumentElement nodeList = root.SelectNodes("descendant::book[author/last-name='Smith']") [C#] XmlNode root = doc.DocumentElement; nodeList = root.SelectNodes("descendant::book[author/last-name='Smith']");生成节点列表后,可以循环通过该列表,如下面的示例所示。
[Visual Basic] Dim book as XmlNode For Each book in nodeList ' Do something. Next [C#] foreach (XmlNode book in nodeList) { // Do something. }根变量设置为文档元素。设置 XPath 查询的起始点将设置上下文节点,该节点是 XPath 查询开始的起始点。上面的示例在 DocumentElement(存储在变量
root
中)的上下文节点处开始 XPath 查询。如果不希望在文档元素处开始,而是希望从文档元素的第一个子级开始,可以编写 Select 语句的代码,如下面的示例所示。[Visual Basic] doc.DocumentElement.FirstChild.Select(. . . ) [C#] this doc.DocumentElement.FirstChild.Select(. . .);所有 XmlNodeList 对象都与基础文档同步。因此,如果循环访问节点列表并修改某个节点的值,则该节点在包含它的文档中也被更新。
注意 修改基础文档时,最好重新运行此 Select 语句。如果修改的节点可能导致该节点被添加到节点列表(当先前没有添加它时),或者现在会导致它从节点列表中被移除,则无法保证节点列表现在是精确的。下面的示例修改节点列表中的书价,同时在基础文档中修改该节点的价格。
[Visual Basic] ' Create the XmlDocument. Dim doc as XmlDocument = new XmlDocument() doc.Load("booksort.xml") Dim book as XmlNode Dim nodeList as XmlNodeList Dim root as XmlNode = doc.DocumentElement nodeList=root.SelectNodes("descendant::book[author/last-name='Austen']") ' Change the price on the books. for each book in nodeList book.LastChild.InnerText="15.95" next Console.WriteLine("Display the modified XML document....") doc.Save(Console.Out) [C#] XmlDocument doc = new XmlDocument(); doc.Load("booksort.xml"); XmlNodeList nodeList; XmlNode root = doc.DocumentElement; nodeList=root.SelectNodes("descendant::book[author/last-name='Austen']"); // Change the price on the books. foreach (XmlNode book in nodeList) { book.LastChild.InnerText="15.95"; } Console.WriteLine("Display the modified XML document...."); doc.Save(Console.Out);SelectNodes 和 SingleSelectNodes 方法都具有一个采用命名空间管理器参数的重载方法。XPath 查询使用命名空间管理器中的命名空间集来匹配所查找的节点的命名空间。有关命名空间管理器的更多信息,请参见 XML 文档中的命名空间。
XmlDocuments 在创建后会创建一个专门用于该文档的名称表。将 XML 加载到文档中或创建新元素或属性后,属性和元素名被放到 XmlNameTable 中。还可以使用另一个文档中的现有 NameTable 创建 XmlDocument。当使用带 XmlNameTable 参数的构造函数创建 XmlDocuments 后,该文档具有对已存储在 XmlNameTable 中的节点名称、命名空间和前缀的访问权。无论如何用名称加载名称表,一旦名称存储在表中之后,便可以使用对象比较而不是字符串比较来快速比较名称。可使用 NameTable.Add 方法将字符串添加到名称表中。下面的代码示例显示所创建的名称表以及添加到该表中的字符串 MyString。然后,使用该表创建 XmlDocument,并将 Myfile.xml 中的元素和属性名添加到现有的名称表中。
[Visual Basic] Dim nt As New NameTable() nt.Add("MyString") Dim doc As New XmlDocument(nt) doc.Load("Myfile.xml") [C#] NameTable nt = new NameTable(); nt.Add("MyString"); XmlDocument doc = new XmlDocument(nt); doc.Load("Myfile.xml");下面的代码示例显示文档的创建,添加到该文档中的两个新元素(该文档还将这两个元素添加到名称表中)以及针对名称进行的对象比较。
[Visual Basic] Dim doc1 As XmlDocument = imp.CreateDocument() Dim node1 As XmlElement = doc.CreateElement("node1") Dim doc2 As XmlDocument = imp.CreateDocument() Dim node2 As XmlElement = doc.CreateElement("node2") if (CType(node1.Name, object) = CType(node2.Name, object)) ... [C#] XmlDocument doc1 = imp.CreateDocument(); node1 = doc1.CreateElement ("node1"); XmlDocument doc2 = imp.CreateDocument(); node2 = doc2.CreateElement ("node1"); if (((object)node1.Name) == ((object)node2.Name)) { ...当对同一类型的文档(如电子商务站点的订购文档,该文档符合 XSD 架构或 DTD)进行重复处理并重复相同的字符串时,以上在两个文档之间传递名称表的方案是典型的。使用同一名称表可提高性能,因为同一元素名出现在多个文档中。