用 XmlReader 读取 XML
2007-08-31 14:42 Jacky_Xu 阅读(489) 评论(0) 编辑 收藏 举报XmlReader 类是一个提供非缓存的、只进只读访问的抽象基类。XmlReader 类检查 XML 格式是否正确,并且在遇到错误时引发 XmlExceptions。它可以读取流或文档,并且实现在由位于 www.w3.org/TR/REC-xml-names 的 W3C 所提供的建议中概述的命名空间要求。
作为抽象基类,它使得您能够自定义自己的类型的读取器或扩展 XmlTextReader、XmlValidatingReader 和 XmlNodeReader 类的当前实现。但是,.NET Framework 1.1 版中针对 XmlReader 实现的安全约束限制了继承 XmlTextReader 和 XmlValidatingReader 的能力。在 .NET Framework 1.0 版中,任何组件都可以继承 XmlTextReader 或 XmlValidatingReader。通过在 .NET Framework 1.1 版中实现对 XmlTextReader 和 XmlValidatingReader 构造函数的继承要求,可信的组件现在只是那些能够继承 XmlTextReader 或 XmlValidatingReader 的组件。
XmlReader 类还定义了使您能够从 XML 提取数据或跳过不需要的记录的方法。XmlReader 与 SAX 模型不同,后者是具有将事件推送到应用程序的分析器的推送模型。有关与 SAX 比较的更多信息,请参见将 XmlReader 与 SAX 读取器比较。
XmlReader 类具有进行以下操作的方法:
- 当 XML 内容在整体上完全可用时(如 XML 文本文件)读取该内容。
- 找出 XML 元素堆栈的深度。
- 确定元素是具有内容还是为空。
- 读取并定位属性。
- 跳过元素和它们的内容。
XmlReader 类的某些属性返回信息,例如:
- 当前节点的类型和名称。
- 当前节点的内容。
XmlReader 类的实现扩展了基类,而它们的设计也多种多样,能够支持不同方案的需要。下表说明了 XmlReader 类的实现。
类 | 说明 |
---|---|
XmlTextReader 类 | 读取字符流。它是一个只进读取器,具有返回有关内容和节点类型的数据的方法。没有文档类型定义 (DTD) 或架构支持。 |
XmlNodeReader 类 | 提供 XML 文档对象模型 (DOM) API(如 XmlNode 树)的分析器。获取一个 XmlNode,它将返回在 DOM 树中查找到的任何节点,包括实体引用节点。没有 DTD 或架构验证支持,但它可以解析定义在 DTD 中的实体。 |
XmlValidatingReader 类 | 提供具有 DTD、XML 架构定义语言 (XSD) 架构或 XML-数据精简 (XDR) 架构支持的完全符合的验证或非验证 XML 分析器。获取 XmlTextReader 并在顶部设置验证服务层。 |
自定义的 XML 读取器的创建 | 使得能够实现 XmlReader 的开发人员定义的派生。 |
注意 ?? XmlTextReader 和 XmlValidatingReader 可以读取的文件的大小有限制。它们无法读取大于 2 千兆字节的文件。如果可能,可将源文件分割为多个较小的文件。
XsltReader 是不具有公共构造函数的另一种实现。它是作为调用 XslTransform 类的 Transform 方法的结果创建的。XsltReader 提供以下功能:
- 强制 XML 必须采用正确格式的规则。
- 不针对 DTD 或架构进行验证。
- 不扩展默认特性,这是因为 DTD 信息(DOCTYPE 节点)不在 XPath 数据模型中公开。请参见 XmlReader.IsDefault 属性。如果您需要在应用转换前在源文档中扩展默认特性,则需要通过 XmlValidatingReader 加载数据存储区(例如 XmlDocument)。
XmlTextReader、XmlValidatingReader 和 XmlNodeReader,这些类中的每一个均被设计为支持不同的方案。下表描述用于各种方案的读取器以及提供给 ValidationType 属性的值。
方案 | 要使用的读取器 | ValidationType 属性 |
---|---|---|
需要更高的性能并且不需要任何 DTD 和架构支持。 | XmlTextReader | 不可用。 |
要求 XML 格式正确,包括外部实体和符合所提供的 DTD 的 DocType。 | XmlTextReader | 不可用。 |
需要 XML 对于 DTD 是有效且格式正确的。 | XmlValidatingReader | Auto 或 DTD。 |
需要 XML 格式正确且要针对某个架构对它进行验证。 | XmlValidatingReader | 当没有 DTD 可用时为 Auto,否则为 XDR 架构。 |
需要 XML 在从 XmlNode 对 XML 数据进行流式处理时格式正确。 | XmlNodeReader | 不可用。 |
下表描述如何设置各种读取器的 XmlResolver 和 XmlTextReader.Normalization 属性以便实现各种方案。
Normalization 属性在设置为 true 时,将正常化文本和空白节点中的行尾字符,并根据它们的类型正常化属性值。因此,将 Normalization 属性设置为 true 会导致性能比将其设置为 false(默认值)时更低。有关更多信息,请参见 XmlTextReader.Normalization 属性。
XmlResolver 属性用于解析以 URI 命名的外部资源(如外部 DTD)或定位一个架构。有关将 XmlResolver 属性用于不同读取器的更多信息,请参见使用 XmlResolver 解析资源。
方案 | XmlResolver | Normalization 属性 |
---|---|---|
需要更高的性能并且不需要任何 DTD 和架构支持。 | 设置为空引用。有关更多信息,请参见 XmlTextReader.XmlResolver 属性。 | 设置为 false。 |
需要文档格式正确,包括外部实体和符合所提供的 DTD 的 DocType。 | 设置为非空引用。所有外部实体必须都是可解析的。有关更多信息,请参见 XmlTextReader.XmlResolver 属性。 | 设置为 true。 |
需要文档格式正确,并且需要对于 DTD XML 是有效的。 | 设置为非空引用。所有外部实体必须都是可解析的。有关更多信息,请参见 XmlValidatingReader.XmlResolver 属性。 | 传递到 XmlValidatingReader 之前对 XmlTextReader 设置为 true。 |
需要文档格式正确并且需要架构验证。 | 设置为非空引用。所有外部实体必须都是可解析的。有关更多信息,请参见 XmlValidatingReader.XmlResolver 属性。 | 传递到 XmlValidatingReader 之前对 XmlTextReader 设置为 true。 |
需要在从 XmlNode 对 XML 数据进行流式处理时文档格式正确。 | 不可用。 | 不可用。 |
XmlValidatingReader 提供了对 XmlTextReader 的验证服务。有关更多信息,请参见使用 XmlValidatingReader 进行 XML 验证。
XmlReader 类提供了对 XML 流或文件的只进访问。当前节点是读取器当前所在的 XML 节点,所有调用的方法和采取的操作都是相对于此当前节点的,并且检索的所有属性都反映了当前节点的值。
读取器是通过对其中一个 Read 方法的调用推进的。下面的示例显示了如何在流中定位来确定当前的节点类型。
[Visual Basic] Option Explicit Option Strict Imports System Imports System.IO Imports System.Xml Namespace Test Public Class MyApp Public Shared Sub Main() Dim rdr As New XmlTextReader("books.xml") ReadandWrite(rdr) End Sub Public Shared Sub ReadandWrite(rdr As XmlReader) 'Read each node in the tree. While rdr.Read() Select Case rdr.NodeType Case XmlNodeType.Element Console.Write(("<" & rdr.Name)) While rdr.MoveToNextAttribute() Console.Write((" " & rdr.Name & "='" & rdr.Value & "'")) End While Console.Write(">") If rdr.IsEmptyElement = True Then Console.Write("#EmptyElement") Else Console.Write("#Element") End If Case XmlNodeType.Text Console.Write(rdr.Value) Console.Write("#Text") Case XmlNodeType.CDATA Console.Write(rdr.Value) Case XmlNodeType.ProcessingInstruction Console.Write(("<?" & rdr.Name & " " & rdr.Value & "?>")) Case XmlNodeType.Comment Console.Write(("<!--" & rdr.Value & "-->")) Case XmlNodeType.Document Console.Write("<?xml version='1.0'?>") Case XmlNodeType.Whitespace Console.Write(rdr.Value) Case XmlNodeType.SignificantWhitespace Console.Write(rdr.Value) Case XmlNodeType.EndElement Console.Write(("</" & rdr.Name & ">")) Console.Write("#EndElement") End Select End While End Sub End Class End Namespace [C#] using System; using System.IO; using System.Xml; namespace Test { public class MyApp { public static void Main() { XmlTextReader rdr = new XmlTextReader("books.xml"); ReadandWrite(rdr); } public static void ReadandWrite(XmlReader rdr) { //Read each node in the tree. while (rdr.Read()) { switch (rdr.NodeType) { case XmlNodeType.Element: Console.Write("<" + rdr.Name); while (rdr.MoveToNextAttribute()) Console.Write(" " + rdr.Name + "='" + rdr.Value + "'"); Console.Write(">"); if (rdr.IsEmptyElement == true) Console.Write("#EmptyElement"); else Console.Write("#Element"); break; case XmlNodeType.Text: Console.Write(rdr.Value); Console.Write("#Text"); break; case XmlNodeType.CDATA: Console.Write(rdr.Value); break; case XmlNodeType.ProcessingInstruction: Console.Write("<?" + rdr.Name + " " + rdr.Value + "?>"); break; case XmlNodeType.Comment: Console.Write("<!--" + rdr.Value + "-->"); break; case XmlNodeType.Document: Console.Write("<?xml version='1.0'?>"); break; case XmlNodeType.Whitespace: Console.Write(rdr.Value); break; case XmlNodeType.SignificantWhitespace: Console.Write(rdr.Value); break; case XmlNodeType.EndElement: Console.Write("</" + rdr.Name + ">"); Console.Write("#EndElement"); break; } } } } }
重复调用 Read 将移到下一个节点,并且这种调用通常在 while 循环中执行。
在 XmlReader 中可用的属性并不是在每个节点类型上都可用。例如,如果当前节点为元素且以正斜线字符“/>”结尾,则 IsEmptyElement 返回 true。对其他任何节点类型调用该属性都返回 false,这是因为该属性不适用于其他节点类型。下面的示例显示了特性和元素属性的用法。它显示了如何检查一个元素是否具有特性。如果元素有特性,则该示例显示如何使用 AttributeCount 属性依次通过这些特性。
[Visual Basic] Public Sub DisplayAttributes(reader As XmlReader) If reader.HasAttributes Then Console.WriteLine(("Attributes of <" & reader.Name & ">")) Dim i As Integer For i = 0 To reader.AttributeCount - 1 reader.MoveToAttribute(i) Console.Write(" {0}={1}", reader.Name, reader.Value) Next i reader.MoveToElement() 'Moves the reader back to the element node. End If End Sub 'DisplayAttributes [C#] public void DisplayAttributes(XmlReader reader) { if (reader.HasAttributes) { Console.WriteLine("Attributes of <" + reader.Name + ">"); for (int i = 0; i < reader.AttributeCount; i++) { reader.MoveToAttribute(i); Console.Write(" {0}={1}", reader.Name, reader.Value); } reader.MoveToElement(); //Moves the reader back to the element node. } }
当 MoveToAttribute 方法定位在元素节点上时,它使您能够在该元素的特性列表中定位。与此类似,当读取器定位在特性节点上时,对 MoveToElement 方法的调用将把读取器定位在拥有当前属性节点的元素处。这是用于在通过元素的特性定位后移回该元素的方法。有关可用方法的完整列表,请参见 XmlReader 成员。
下面的示例读取一个具有 DTD 的 XML 流并打印各节点的节点属性。该示例还通过使用 HasAttributes 属性检查节点是否具有特性。如果有特性,则代码使用 MoveToNextAttribute 方法并打印出特性名称、本地名称和命名空间 URI。
[Visual Basic] Public Shared Sub Main() Dim stream As New StringReader("<!DOCTYPE test [<!ELEMENT test (item|bar)*><!ELEMENT item (item*)><!ATTLIST item xml:space (default|preserve) #IMPLIED><!ELEMENT bar (#PCDATA|b|i)*><!ELEMENT b (#PCDATA)><!ELEMENT i (#PCDATA)>]> <test> <item> <item xml:space=""preserve""> <item/> </item> </item> <bar> <b>This</b> <i>is</i> <b>a test</b> </bar> </test> ") Dim myTxtReader As New XmlTextReader(stream) Dim myReader As New XmlValidatingReader(myTxtReader) myTxtReader.ValidationType = ValidationType.None While myReader.Read() WriteNodeInfo(myReader) End While End Sub 'Main Private Shared Sub WriteNodeInfo(xmlreader As XmlReader) Console.WriteLine("*****************************************************") Console.WriteLine() Console.WriteLine(("NodeType = " + xmlreader.NodeType)) Console.WriteLine(("NodeName = " + xmlreader.Name)) Console.WriteLine(("NodeLocalName = " + xmlreader.LocalName)) Console.WriteLine(("NodeNamespace = " + xmlreader.NamespaceURI)) Console.WriteLine(("NodePrefix = " + xmlreader.Prefix)) Console.WriteLine(("NodeHasValue = " + xmlreader.HasValue)) Console.WriteLine(("NodeValue = [" + xmlreader.Value + "]")) Console.WriteLine(("NodeDepth = " + xmlreader.Depth)) Console.WriteLine(("IsEmptyElement = " + xmlreader.IsEmptyElement.ToString())) Console.WriteLine(("IsDefault = " + xmlreader.IsDefault.ToString())) Console.WriteLine(("QuoteChar = " + xmlreader.QuoteChar)) Console.WriteLine(("XmlSpace = " + xmlreader.XmlSpace)) Console.WriteLine(("XmlLang = " + xmlreader.XmlLang)) Console.WriteLine(("AttributeCount = " + xmlreader.AttributeCount)) Console.WriteLine(("HasAttributes = " + xmlreader.HasAttributes.ToString())) Console.WriteLine(("EOF = " + xmlreader.EOF.ToString())) Console.WriteLine(("ReadState = " + xmlreader.ReadState.ToString())) If xmlreader.HasAttributes Then While True = xmlreader.MoveToNextAttribute() Console.WriteLine(("AttributeName = " + xmlreader.Name)) Console.WriteLine(("AttributeLocalName = " + xmlreader.LocalName)) Console.WriteLine(("AttributeNamespace = " + xmlreader.NamespaceURI)) End While End If Console.WriteLine() Console.WriteLine("***************************************************") Console.WriteLine() End Sub 'WriteNodeInfo [C#] public static void Main() { StringReader stream = new StringReader ("<!DOCTYPE test [<!ELEMENT test (item|bar)*><!ELEMENT item (item*)><!ATTLIST item xml:space (default|preserve) #IMPLIED><!ELEMENT bar (#PCDATA|b|i)*><!ELEMENT b (#PCDATA)><!ELEMENT i (#PCDATA)>]> <test> <item> <item xml:space=\"preserve\"> <item/> </item> </item> <bar> <b>This</b> <i>is</i> <b>a test</b> </bar> </test> "); XmlTextReader myTxtReader = new XmlTextReader (stream); XmlValidatingReader myReader = new XmlValidatingReader (myTxtReader); myTxtReader.ValidationType = ValidationType.None; while (myReader .Read()) WriteNodeInfo(myReader ); } private static void WriteNodeInfo(XmlReader xmlreader) { Console.WriteLine("*****************************************************"); Console.WriteLine(); Console.WriteLine("NodeType = " + xmlreader.NodeType ); Console.WriteLine("NodeName = " + xmlreader.Name); Console.WriteLine("NodeLocalName = " + xmlreader.LocalName ); Console.WriteLine("NodeNamespace = " + xmlreader.NamespaceURI); Console.WriteLine("NodePrefix = " + xmlreader.Prefix); Console.WriteLine("NodeHasValue = " + xmlreader.HasValue); Console.WriteLine("NodeValue = [" + xmlreader.Value +"]"); Console.WriteLine("NodeDepth = " + xmlreader.Depth); Console.WriteLine("IsEmptyElement = " + xmlreader.IsEmptyElement.ToString()); Console.WriteLine("IsDefault = " + xmlreader.IsDefault.ToString()); Console.WriteLine("QuoteChar = " + xmlreader.QuoteChar); Console.WriteLine("XmlSpace = " + xmlreader.XmlSpace); Console.WriteLine("XmlLang = " + xmlreader.XmlLang); Console.WriteLine("AttributeCount = " + xmlreader.AttributeCount); Console.WriteLine("HasAttributes = " + xmlreader.HasAttributes.ToString()); Console.WriteLine("EOF = " + xmlreader.EOF.ToString()); Console.WriteLine("ReadState = " + xmlreader.ReadState.ToString()); if (xmlreader.HasAttributes) { while (true== xmlreader.MoveToNextAttribute()) { Console.WriteLine("AttributeName = "+ xmlreader.Name); Console.WriteLine("AttributeLocalName = "+ xmlreader.LocalName); Console.WriteLine("AttributeNamespace = "+ xmlreader.NamespaceURI); } } Console.WriteLine(); Console.WriteLine("***************************************************"); Console.WriteLine(); }
前三个节点的输出:
***************************************************** NodeType = DocumentType NodeName = test NodeLocalName = test NodeNamespace = NodePrefix = NodeHasValue = True NodeValue = [<!ELEMENT test (item|bar)*><!ELEMENT item (item*)><!ATTLIST item xml:space (default|preserve) #IMPLIED><!ELEMENT bar (#PCDATA|b|i)*><!ELEMENT b (#PCDATA)><!ELEMENT i (#PCDATA)>] NodeDepth = 0 IsEmptyElement = False IsDefault = False QuoteChar = " XmlSpace = None XmlLang = AttributeCount = 0 HasAttributes = False EOF = False ReadState = Interactive *************************************************** ***************************************************** NodeType = Whitespace NodeName = NodeLocalName = NodeNamespace = NodePrefix = NodeHasValue = True NodeValue = [ ] NodeDepth = 0 IsEmptyElement = False IsDefault = False QuoteChar = " XmlSpace = None XmlLang = AttributeCount = 0 HasAttributes = False EOF = False ReadState = Interactive *************************************************** ***************************************************** NodeType = Element NodeName = test NodeLocalName = test NodeNamespace = NodePrefix = NodeHasValue = False NodeValue = [] NodeDepth = 0 IsEmptyElement = False IsDefault = False QuoteChar = " XmlSpace = None XmlLang = AttributeCount = 0 HasAttributes = False EOF = False ReadState = Interactive ***************************************************
有关所有节点类型的列表,请参见 XML 节点类型。
XmlReader 类具有一些可以在读取时修改的属性,以及一些在读取开始后被更改时并不会使新设置影响读取的其他属性。如果默认值不合适,则需要在开始读取前将这些属性设置为正确的值。但是,某些属性可以在开始读取后修改。对于不能在调用 Read 后设置的属性,读取器将引发异常。下表显示可以在读取开始之后修改的属性。
类 | 属性 | 是否可以修改 |
---|---|---|
XmlTextReader | Namespaces | 否 |
XmlTextReader | WhitespaceHandling | 是 |
XmlTextReader | Normalization | 是 |
XmlTextReader | XmlResolver | 是 |
XmlValidatingReader | Namespaces | 否 |
XmlValidatingReader | EntityHandling | 是 |
XmlValidatingReader | XmlResolver | 是 |
XmlValidatingReader | ValidationType | 否 |
XmlValidatingReader 将 XmlResolver 属性传播到所包含的 XmlTextReader 类。XmlNodeReader 类没有可以设置的属性。
为了读取调用中的元素和特性的内容,XmlReader 提供了 ReadInnerXml 和 ReadOuterXml 方法。当定位在节点上时,ReadInnerXml 属性提取该节点中的所有内容,包括标记(从元素标记之后开始,直到元素结束标记)。在 ReadInnerXml 调用后,XmlReader 将被定位在结束元素标记之后。假定下列 XML 输入:
输入
<Book> <Price Retail="29.95" /> <Title BookTitle="My First Book" author="me" /> </Book>
这是当读取器定位在 Book
元素上时调用 ReadInnerXml 所返回的输出:
<Price Retail="29.95" /> <Title BookTitle="My First Book" author="me" />
ReadInnerXml 将读取器定位在正在被读取的元素的结束标记之后。这意味着对于上面的输入,当前节点是 </Book>
结束元素(假定存在)之后的节点。执行 Read 方法将把它再向前推进一个元素。以下代码示例显示了在调用 ReadInnerXml 后流中的当前节点,以及在 ReadInnerXml 之后执行 Read 如何将读取器又向前推进一个节点。
[C#]
public static void Main()
{
string text = @"<book><one><title>ABC</title></one><two><price>24.95</price></two></book>";
XmlTextReader reader = new XmlTextReader(new StringReader(text));
reader.WhitespaceHandling = WhitespaceHandling.None;
// Moves the reader to the 'book' node.
reader.MoveToContent();
// Moves the reader to the 'one' node.
reader.Read();
reader.MoveToContent();
// Reads the inner XML of the 'one' node, which is the entire 'title'
// node, and outputs <title>ABC</title>.
Console.WriteLine(reader.ReadInnerXml());
// The reader is now positioned on the 'two' node.
Console.WriteLine(reader.Name);
// Calling Read will advance the reader beyond the 'two' node to
// the 'price' node.
reader.Read();
// Outputs the node of 'price'.
Console.WriteLine(reader.Name);
}
该代码的输出是:
<title>ABC</title>
two
price
ReadInnerXml 根据 XmlNodeType 分析 XML 并返回内容。下表显示了元素和特性的示例内容、从对 ReadInnerXml 的调用返回的值以及该调用后读取器的位置。
节点类型 | 子内容 | 返回值 | 位置 |
---|---|---|---|
Element | <myelem> text </myelem> | text | 在结束元素 </myelem> 之后的节点上 |
Attribute | <myelem attr1="val1" attr2="val2"> text </myelem> | val1 | 保持在特性节点“attr1”上。 |
其他所有的 XmlNodeType 都返回 string.Empty。
除返回值包括开始标记和结束标记外,ReadOuterXml 方法在行为上类似于 ReadInnerXml 方法。当定位在叶节点上时,该方法与执行 Read 操作相同。
根据当前的 XmlNodeType,ReadOuterXml 分析 XML 并返回各种子内容。下表显示了元素和特性的示例内容、从对 ReadOuterXml 的调用返回的值以及读取器在此调用后的位置。
节点类型 | 子内容 | 返回值 | 位置 |
---|---|---|---|
Element | <elem1> text </elem1> | <elem1> text </elem1> | 定位在 </elem1> 标记后。 |
Attribute | <elem1 attr1="attrValue1" attr2="attrValue2" > text </elem1> | attr1="attrValue1" | 定位在特性节点“attr1”上。 |
其他所有节点类型都将 string.Empty 返回到 ReadOuterXml。
跳过内容的方法有两种。一种方法是调用使用 MoveToContent 方法直接移动到内容的方法。另一种方法是直接调用 Skip 方法,该方法从当前节点跳过子节点。
直接移动到内容
若要移动到内容,请使用 MoveToContent 方法。该方法检查当前节点以查看它是否是内容节点。内容节点被定义为任意 Text、CDATA、Element、EndElement、EntityReference 或 EndEntity 节点。如果该节点不是前述节点类型之一,则将跳过该节点并跳到下一个节点或文件尾。它一直跳,直到找到该类型的节点或到文件的结尾才停止。换句话说,它跳过下列节点类型:
- XmlDeclaration
- ProcessingInstruction
- DocumentType
- Comment
- Attribute
- Whitespace
- SignificantWhitespace
如果应用程序只需要内容,则使用这种类型的内容定位更有效率,而不是调用 Read,后一种做法将读取器移动到下一个节点并强制应用程序测试节点类型,然后确定是否存在要读取的内容,如果有则读取它。
如果应用程序定位在特性节点上,则调用 MoveToContent 将把当前节点位置移动到该特性所属的元素。如果应用程序已经定位在内容节点上,则 MoveToContent 调用将把 NodeType 属性的值返回到应用程序。这些行为使应用程序能够跳过随机的 XML 标记。例如,考虑以下 XML 输入:
<?xml version="1.0"> <!DOCTYPE price SYSTEM "abc"> <!––the price of the book –-> <price>123.4</price>
下列代码查找 price
元素“123.4”并将其文本内容转换为双精度型:
[Visual Basic] If readr.MoveToContent() = XmlNodeType.Element And readr.Name = "price" Then _price = XmlConvert.ToDouble(readr.ReadString()) End If [C#] if (readr.MoveToContent() == XmlNodeType.Element && readr.Name =="price") { _price = XmlConvert.ToDouble(readr.ReadString()); }
在另外一个示例中,MoveToContent 常常用于查找 XML 文件中的 DocumentElement 节点。假定有下面这个称为 file.xml 的 XML 输入文件:
<?xml encoding="utf-8"?> <!-- Can have comment and DOCTYPE nodes here to skip past here --> <phone a="2" b="N"> <data> <d1>Data</d1> <d2>More Data</d2> <d3>Some More Data</d3> </data> </phone>
下列代码将读取器定位到 DocumentElement 节点 <phone>
上:
[Visual Basic] Dim doc as XmlDocument = New XmlDocument() Dim treader as New XmlTextReader("file.xml") treader.MoveToContent() [C#] XmlDocument doc = new XmlDocument(); XmlTextReader treader = new XmlTextReader("file.xml"); treader.MoveToContent();
使用 Skip 方法跳过数据
Skip 方法跳过当前的元素。如果节点类型为 XmlNodeType.Element,则调用 Skip 将跳过该元素的所有内容和元素结束标记。
例如,如果您有下列 XML:
<a name="facts" location="123"> <x/> abc <y/> </a> <b> ... </b>
并且您定位在 <a>
节点或它的任何一个特性上时,调用 Skip 将把您定位在 <b>
节点上。如果定位在叶节点上,如 x
或文本节点 abc
上,则 Skip 将跳到下一个节点,这意味着实际上它的行为与调用 Read 一样。
Skip 将格式正确的规则应用于内容。
XmlTextReader 类是 XmlReader 的实现,它提供快速的,性能优良的分析器。它强制 XML 必须采用正确格式的规则。由于它既没有 DTD 信息,也没有架构信息,因此它既不是验证分析器也不是非验证分析器。它可以读取块中的文本或从流中读取字符。
XmlTextReader 提供了下列功能:
- 强制 XML 必须采用正确格式的规则。
- 检查 DTD 的格式是否正确。然而,并不将 DTD 用于验证、扩展实体引用或添加默认属性。
- 不针对 DTD 或架构进行验证。
- 检查 DOCTYPE 节点是否格式正确。
- 检查实体是否格式正确。对于 EntityReference 节点类型,返回单个空 EntityReference 节点。空 EntityReference 节点是 Value 属性为 string.Empty 的节点。这是因为您没有用来扩展实体引用的 DTD 或架构。XmlTextReader 确保整个 DTD(包括 EntityReference 节点)格式正确。
- 提供性能优良的 XML 分析器,原因是 XmlTextReader 没有涉及验证检查的系统开销。
XmlTextReader 可以从不同输入(如流对象、TextReader 对象以及标识本地文件位置或 Web 站点的 URL)读取数据。
XmlTextReader 使用 XmlResolver 定位外部资源(如 DTD),所以它可以检查 DTD 以查看它们是否格式正确。有关 XmlResolver 的更多信息,请参见使用 XmlResolver 解析资源。
编码声明 <?xml version="1.0" encoding="ISO-8859-5"?>
包含设置文档的编码的编码属性。XmlTextReader 有一个 Encoding 属性,它返回在 XML 声明中的编码属性中找到的字符编码。如果未找到编码属性,则文档的默认值将设置为 UTF-8。
如果读取了外部资源(例如用于扩展实体引用或架构文件的 DTD),则编码将设置为在外部引用中找到的编码值。如果在外部引用中没有找到编码,则默认值设置为 UTF-8。XmlTextReader 支持许多编码,这是由于它使用 System.Text.Encoding Class。因此,该类支持的所有编码也受 XmlTextReader 支持。唯一不支持的编码是将 <?xml
序列映射到 UTF-8 以外的不同字节值(如 UTF-7 和 EBCDIC)的编码。
XmlNodeReader 提供给定 DOM 节点子树的读取器。它从该子树读取并返回节点,包括实体引用节点。
XmlNodeReader 提供了下列功能:
- 强制执行 XML 结构正确性规则。
- 如果在 XmlDocument 中存在 DTD 信息,则扩展默认特性和实体。有关获取默认特性信息的更多信息,请参见 XmlReader.IsDefault 属性。
从 XmlDocument 创建 XmlNodeReader:
[Visual Basic] Dim doc As New XmlDocument() doc.Load("MyXml.xml") Dim nodereader As New XmlNodeReader(doc) While nodereader.Read() ' Read the XmlDocument as a stream of XML End While [C#] XmlDocument doc = new XmlDocument(); doc.Load("MyXml.xml"); XmlNodeReader nodereader = new XmlNodeReader (doc); while (nodereader.Read()) { // Read the XmlDocument as a stream of XML }
也可以用 XmlDocument 中的任何 XmlNode 构造 XmlNodeReader。
下面的示例使用 Select 方法和 XPath 表达式移动到 XmlDocument 中的某个特定节点。然后,它在该位置创建 XmlNodeReader。XML 输入文件 test.xml 包含下列数据:
<root> <child> Child Text </child> </root> [Visual Basic] Imports System Imports System.Xml Public Class Test Public Shared Sub Main() Dim doc As New XmlDocument() doc.Load("test.xml") Dim child As XmlNode = doc.SelectSingleNode("/root/child") If Not (child Is Nothing) Then Dim nr As New XmlNodeReader(child) While nr.Read() Console.WriteLine(nr.Value) End While End If End Sub 'Main End Class 'Test [C#] using System; using System.Xml; public class Test { public static void Main() { XmlDocument doc = new XmlDocument(); doc.Load("test.xml"); XmlNode child = doc.SelectSingleNode("/root/child"); if (child != null) { XmlNodeReader nr = new XmlNodeReader(child ); while (nr.Read() ) Console.WriteLine( nr.Value ); } } }XmlNameTable 类使 XmlReader 类的实现能够在分析数据或对 XML 文档执行比较操作时使用指针比较,而不是字符串比较。在比较和使用元素和特性名时,使用此表将提高 XmlReader 派生类的性能。
XmlNameTable 是一个抽象基类,NameTable 是它的一个实现。NameTable 包含元素和属性名的原子化的版本以及命名空间 URI 和前缀。如果应用程序正对名称进行大量比较,则它可以创建 NameTable。用户可以从读取器的 NameTable 属性获取读取器正在使用的 NameTable。有关原子化的说明,请参见 XmlNameTable 类。
应用程序可以使用 Add 方法将名称添加到该表中。下面的示例显示比较是通过使用 Equals 方法或 == 运算符完成的,它确定在方法调用中提供的对象是否是与当前对象相同的实例。
[Visual Basic] Dim cust As Object = reader.NameTable.Add("Customer") While reader.Read() ' The "if" uses efficient pointer comparison. If cust Is reader.Name Then ... End If End While [C#] object cust = reader.NameTable.Add("Customer"); while (reader.Read()) { // The "if" uses efficient pointer comparison. if (cust == reader.Name) { ... } }自定义 XML 读取器使应用程序能够扩展 XmlReader 或 XmlTextReader,或者定义它自己的自定义读取器。
扩展 XmlTextReader 的示例
下面是一个显示如何扩展 XmlTextReader 以创建将特性转换为元素的读取器的示例。重写的 Read 提供了跟踪当前节点类型的逻辑,并且如果当前节点类型是特性的话,将使用 XmlNodeType.Name 和 XmlNodeType.Value 属性把它作为元素节点类型来公开。
[Visual Basic] Option Explicit Option Strict Imports System Imports System.IO Imports System.Text Imports System.Xml Imports System.Xml.XPath Public Class AttrToElementReader Inherits XmlTextReader Private _ReadAttributes As Boolean = False Private _ReadAttributeValue As Boolean = False Private _AttributeName As String = String.Empty Private _NodeType As XmlNodeType = XmlNodeType.None Public Sub New(fileName As String) MyBase.New(fileName) MyBase.WhitespaceHandling = WhitespaceHandling.None ' Intentionally Empty End Sub Public Overrides Function Read() As Boolean If Not _ReadAttributes Then Dim baseRead As Boolean = MyBase.Read() _ReadAttributes = baseRead And XmlNodeType.Element = MyBase.NodeType And 0 < MyBase.AttributeCount Return baseRead End If 'Read attribues; If _ReadAttributeValue Then _ReadAttributeValue = MyBase.ReadAttributeValue() If Not _ReadAttributeValue Then ' End of attribute. ' End element. _NodeType = XmlNodeType.EndElement Else _NodeType = XmlNodeType.None End If Return True End If _ReadAttributes = MyBase.MoveToNextAttribute() If _ReadAttributes Then _ReadAttributeValue = True _NodeType = XmlNodeType.Element _AttributeName = MyBase.Name Return True Else _ReadAttributeValue = False _NodeType = XmlNodeType.None _AttributeName = String.Empty Return Read() End If End Function Public Overrides ReadOnly Property NodeType() As XmlNodeType Get If XmlNodeType.None = _NodeType Then Return MyBase.NodeType End If Return _NodeType End Get End Property Public Overrides ReadOnly Property Value() As String Get If XmlNodeType.None = _NodeType Then Return MyBase.Value End If Return String.Empty End Get End Property Public Overrides ReadOnly Property Name() As String Get If XmlNodeType.None = _NodeType Then Return MyBase.Name End If Return _AttributeName End Get End Property End Class 'AttrToElementReader [C#] using System; using System.IO; using System.Text; using System.Xml; using System.Xml.XPath; public class AttrToElementReader: XmlTextReader { private bool _ReadAttributes = false; private bool _ReadAttributeValue = false; private string _AttributeName = String.Empty; private XmlNodeType _NodeType = XmlNodeType.None; public AttrToElementReader(String fileName) : base(fileName) { base.WhitespaceHandling = WhitespaceHandling.None; // Intentionally Empty. } public override bool Read() { if (!_ReadAttributes) { bool baseRead = base.Read(); _ReadAttributes = (baseRead && XmlNodeType.Element == base.NodeType && 0 < base.AttributeCount); return baseRead; } // Reading attribues; if (_ReadAttributeValue) { _ReadAttributeValue = base.ReadAttributeValue(); if (!_ReadAttributeValue) { // End of attribute. // End element. _NodeType = XmlNodeType.EndElement; } else { _NodeType = XmlNodeType.None; } return true; } _ReadAttributes = base.MoveToNextAttribute(); if (_ReadAttributes) { _ReadAttributeValue = true; _NodeType = XmlNodeType.Element; _AttributeName = base.Name; return true; } else { _ReadAttributeValue = false; _NodeType = XmlNodeType.None; _AttributeName = String.Empty; return Read(); } } public override XmlNodeType NodeType { get { if (XmlNodeType.None == _NodeType) { return base.NodeType; } return _NodeType; } } public override String Value { get { if (XmlNodeType.None == _NodeType) { return base.Value; } return String.Empty; } } public override String Name { get { if (XmlNodeType.None == _NodeType) { return base.Name; } return _AttributeName; } } }//AttrToElementReader链接 XmlReaders
下面的示例显示一个称为 XmlReaderReader 的实现类,作为一个类,它被用于将 XmlReaders 链接起来,从而通过委托调用来提供一个读取器流。在创建筛选特定的输入的读取器时这很有用,并且对于对 XML 进行特定分析,它也是一个性能优良的解决方案。
从此 XmlReaderReader 派生的类可以将任何 XmlReader 当作输入。在下面的示例中,XmlCustomReader 重载 Read 方法并跳过
price
元素节点,从而将它们从流中移除。[Visual Basic] ' This sample demonstrate the ability to chain XmlReaders together by ' implementing an XmlReaderReader class which aggregates any given ' XmlReader and then delegates the calls to it Namespace Test Public Class MyApp Public Shared Sub Main() Dim reader As New XmlTextReader("books.xml") Dim customreader As New XmlCustomReader(reader) customreader.ReadandWrite() End Sub Class XmlCustomReader Inherits XmlReaderReader Public Sub New(reader As XmlTextReader) MyBase.New(reader) End Sub Public Overrides Function Read() As Boolean Dim result As Boolean result = MyBase.Read() Select Case MyBase.NodeType Case XmlNodeType.Element If MyBase.Name.Equals("price") Then MyBase.Skip() End If Case Else End Select Return result End Function Public Sub ReadandWrite() ' Read each node in the tree. While Read() Select Case NodeType Case XmlNodeType.Element Console.Write(("<" + Name)) While MoveToNextAttribute() Console.Write((" " + Name + "='" + Value + "'")) End While Console.Write(">") Case XmlNodeType.Text Console.Write(Value) Case XmlNodeType.CDATA Console.Write(Value) Case XmlNodeType.ProcessingInstruction Console.Write(("<?" + Name + " " + Value + "?>")) Case XmlNodeType.Comment Console.Write(("<!--" + Value + "-->")) Case XmlNodeType.Document Console.Write("<?xml version='1.0'?>") Case XmlNodeType.Whitespace Console.Write(Value) Case XmlNodeType.SignificantWhitespace Console.Write(Value) Case XmlNodeType.EndElement Console.Write(("</" + Name + ">")) End Select End While End Sub End Class Public Class XmlReaderReader Inherits XmlTextReader Private _Reader As XmlTextReader Public Sub New(reader As XmlTextReader) _Reader = reader End Sub ' XmlReader methods and properties. Public Overrides ReadOnly Property NodeType() As XmlNodeType Get Return _Reader.NodeType End Get End Property Public Overrides ReadOnly Property Name() As [String] Get Return _Reader.Name End Get End Property Public Overrides ReadOnly Property LocalName() As [String] Get Return _Reader.LocalName End Get End Property Public Overrides ReadOnly Property NamespaceURI() As [String] Get Return _Reader.NamespaceURI End Get End Property Public Overrides ReadOnly Property Prefix() As [String] Get Return _Reader.Prefix End Get End Property Public Overrides ReadOnly Property HasValue() As Boolean Get Return _Reader.HasValue End Get End Property Public Overrides ReadOnly Property Value() As String Get Return _Reader.Value End Get End Property Public Overrides ReadOnly Property Depth() As Integer Get Return _Reader.Depth End Get End Property Public Overrides ReadOnly Property BaseURI() As String Get Return _Reader.BaseURI End Get End Property Public Overrides ReadOnly Property IsEmptyElement() As Boolean Get Return _Reader.IsEmptyElement End Get End Property Public Overrides ReadOnly Property IsDefault() As Boolean Get Return _Reader.IsDefault End Get End Property Public Overrides ReadOnly Property QuoteChar() As Char Get Return _Reader.QuoteChar End Get End Property Public Overrides ReadOnly Property XmlSpace() As XmlSpace Get Return _Reader.XmlSpace End Get End Property Public Overrides ReadOnly Property XmlLang() As String Get Return _Reader.XmlLang End Get End Property Public Overrides ReadOnly Property AttributeCount() As Integer Get Return _Reader.AttributeCount End Get End Property Overloads Public Overrides Function GetAttribute(name As String) As String Return _Reader.GetAttribute(name) End Function Overloads Public Overrides Function GetAttribute(name As String, namespaceURI As String) As String Return _Reader.GetAttribute(name, namespaceURI) End Function Overloads Public Overrides Function GetAttribute(i As Integer) As String Return _Reader.GetAttribute(i) End Function Default Public Overrides Overloads ReadOnly Property Item(i As Integer) As String Get Return _Reader(i) End Get End Property Default Public Overrides Overloads ReadOnly Property Item(name As String) As String Get Return _Reader(name) End Get End Property Default Public Overrides Overloads ReadOnly Property Item(name As String, namespaceURI As String) As String Get Return _Reader(name, namespaceURI) End Get End Property Overloads Public Overrides Function MoveToAttribute(name As String) As Boolean Return _Reader.MoveToAttribute(name) End Function Overloads Public Overrides Function MoveToAttribute(name As String, ns As String) As Boolean Return _Reader.MoveToAttribute(name, ns) End Function Overloads Public Overrides Sub MoveToAttribute(i As Integer) _Reader.MoveToAttribute(i) End Sub Public Overrides Function MoveToFirstAttribute() As Boolean Return _Reader.MoveToFirstAttribute() End Function Public Overrides Function MoveToNextAttribute() As Boolean Return _Reader.MoveToNextAttribute() End Function Public Overrides Function MoveToElement() As Boolean Return _Reader.MoveToElement() End Function ' This is the only place that needs to be changed. Public Overrides Function Read() As Boolean Return _Reader.Read() End Function Public Overrides ReadOnly Property EOF() As Boolean Get Return _Reader.EOF End Get End Property Public Overrides Overloads Sub Close() _Reader.Close() End Sub Public Overrides ReadOnly Property ReadState() As ReadState Get Return _Reader.ReadState End Get End Property Public Overrides Function ReadString() As String Return _Reader.ReadString() End Function Public Overrides Function ReadInnerXml() As String Return _Reader.ReadInnerXml() End Function Public Overrides Function ReadOuterXml() As String Return _Reader.ReadOuterXml() End Function Public Overrides ReadOnly Property NameTable() As XmlNameTable Get Return _Reader.NameTable End Get End Property Public Overrides Function LookupNamespace(prefix As String) As String Return _Reader.LookupNamespace(prefix) End Function Public Overrides Overloads Sub ResolveEntity() _Reader.ResolveEntity() End Sub Public Overrides Function ReadAttributeValue() As Boolean Return _Reader.ReadAttributeValue() End Function End Class End Class End Namespace [C#] using System; using System.Xml; // This sample demonstrate the ability to chain XmlReaders together by // implementing an XmlReaderReader class which aggregates any given // XmlReader and then delegates the calls to it. namespace Test { public class MyApp { public static void Main() { XmlTextReader reader = new XmlTextReader("books.xml"); XmlChainingReader customreader = new XmlChainingReader(reader); customreader.ReadandWrite(); } class XmlChainingReader : XmlReaderReader { public XmlChainingReader (XmlReader reader) : base (reader) {} public override bool Read () { bool result; result = base.Read(); switch (base.NodeType) { case XmlNodeType.Element: if (base.Name.Equals("price")) base.Skip(); break; default: break; } return result; } public void ReadandWrite() { // Read each node in the tree. while (Read()) { switch (NodeType) { case XmlNodeType.Element: Console.Write("<" + Name); while (MoveToNextAttribute()) Console.Write(" " + Name + "='" + Value + "'"); Console.Write(">"); break; case XmlNodeType.Text: Console.Write(Value); break; case XmlNodeType.CDATA: Console.Write(Value); break; case XmlNodeType.ProcessingInstruction: Console.Write("<?" + Name + " " + Value + "?>"); break; case XmlNodeType.Comment: Console.Write("<!--" + Value + "-->"); break; case XmlNodeType.Document: Console.Write("<?xml version='1.0'?>"); break; case XmlNodeType.Whitespace: Console.Write(Value); break; case XmlNodeType.SignificantWhitespace: Console.Write(Value); break; case XmlNodeType.EndElement: Console.Write("</" + Name + ">"); break; } } } } public class XmlReaderReader : XmlReader { XmlReader _Reader; public XmlReaderReader (XmlReader reader ) { _Reader = reader; } // XmlReader methods and properties. public override XmlNodeType NodeType { get { return _Reader.NodeType; } } public override String Name { get { return _Reader.Name; } } public override String LocalName { get { return _Reader.LocalName; } } public override String NamespaceURI { get { return _Reader.NamespaceURI; } } public override String Prefix { get { return _Reader.Prefix; } } public override bool HasValue { get { return _Reader.HasValue; } } public override string Value { get { return _Reader.Value; } } public override int Depth { get { return _Reader.Depth; } } public override string BaseURI { get { return _Reader.BaseURI; } } public override bool IsEmptyElement { get { return _Reader.IsEmptyElement; } } public override bool IsDefault { get { return _Reader.IsDefault; } } public override char QuoteChar { get { return _Reader.QuoteChar; } } public override XmlSpace XmlSpace { get { return _Reader.XmlSpace; } } public override string XmlLang { get { return _Reader.XmlLang; } } public override int AttributeCount { get { return _Reader.AttributeCount; } } public override string GetAttribute(string name) { return _Reader.GetAttribute( name ); } public override string GetAttribute(string name, string namespaceURI) { return _Reader.GetAttribute( name, namespaceURI ); } public override string GetAttribute(int i) { return _Reader.GetAttribute( i ); } public override string this [ int i ] { get { return _Reader[ i ]; } } public override string this [ string name ] { get { return _Reader[ name ]; } } public override string this [ string name,string namespaceURI ] { get { return _Reader[ name, namespaceURI ]; } } public override bool MoveToAttribute(string name) { return _Reader.MoveToAttribute( name ); } public override bool MoveToAttribute(string name, string ns) { return _Reader.MoveToAttribute( name, ns ); } public override void MoveToAttribute(int i) { _Reader.MoveToAttribute( i ); } public override bool MoveToFirstAttribute() { return _Reader.MoveToFirstAttribute(); } public override bool MoveToNextAttribute() { return _Reader.MoveToNextAttribute(); } public override bool MoveToElement() { return _Reader.MoveToElement(); } // // This is the only place that needs to be changed. // public override bool Read() { return _Reader.Read(); } public override bool EOF { get { return _Reader.EOF; } } public override void Close() { _Reader.Close(); } public override ReadState ReadState { get { return _Reader.ReadState; } } public override string ReadString() { return _Reader.ReadString(); } public override string ReadInnerXml() { return _Reader.ReadInnerXml(); } public override string ReadOuterXml() { return _Reader.ReadOuterXml(); } public override XmlNameTable NameTable { get { return _Reader.NameTable; } } public override string LookupNamespace(string prefix) { return _Reader.LookupNamespace( prefix ); } public override void ResolveEntity() { _Reader.ResolveEntity(); } public override bool ReadAttributeValue() { return _Reader.ReadAttributeValue(); } } } }和 SAX 读取器一样,XmlReader 是一个只进只读游标。它提供了对输入的快速和非缓存的流式访问。它可以读取流或文档。它使用户可以提取数据,并跳过对应用程序没有意义的记录。较大的差异在于 SAX 模型是一个“推送”模型,其中分析器将事件推到应用程序,在每次读取了一个新节点时通知应用程序,而使用 XmlReader 的应用程序可以随意从读取器提取节点。这种提取模型的优点在于以下方面:
优点 说明 状态管理 推送模型要求内容处理程序生成非常复杂的状态机器。提取模型客户端通过自然的、由上而下的过程优化简化了状态管理。 多个输入流 提取模型允许客户端汇合多个输入流。而要在推送模型中进行此操作是非常复杂的。 分层 推送模型可以建立在提取模型的顶部。反之则不行。 避免多余的字符串复制 数据通常被从分析器缓冲区读入字符串对象,然后字符串对象被推送到客户端缓冲区。提取模型使客户端能够提供给分析器一个可以将字符串直接写入的缓冲区。 有选择的处理 推送模型通知客户端每个项,包括特性、处理指令和空白,而提取模型客户端可以跳过项,只处理那些对应用程序有意义的项。这使得可以生成效率极高的应用程序。而且,可以预先设置影响处理 XML 流的方式(例如正常化)的属性。有关跳过内容的更多信息,请参见用 XmlReader 跳过内容。