本文提供了此 API 参考文档的补充说明。
XmlReader 提供对文档或流中 XML 数据的正向只读访问权限。 此类符合 W3C 可扩展标记语言 (XML) 1.0(第四版)和 XML 1.0 中的命名空间(第三版)建议。
XmlReader 方法允许你浏览 XML 数据并读取节点的内容。 类的属性反映当前节点的值,即读取器的位置。 属性值 ReadState 指示 XML 读取器的当前状态。 例如,属性通过ReadState.Initial方法设置为XmlReader.Read,通过ReadState.Closed方法设置为XmlReader.Close。 XmlReader 还提供针对 DTD 或架构的数据一致性检查和验证。
XmlReader 使用拉取模型检索数据。 此模型:
- 通过自然的自上而下的过程优化来简化状态管理。
- 支持多个输入流和分层。
- 使客户端能够向分析器提供直接写入字符串的缓冲区,从而避免了额外的字符串复制。
- 支持选择性处理。 客户端可以跳过某些项目,并处理应用程序感兴趣的那些项目。 还可以提前设置属性来管理 XML 流的处理方式(例如规范化)。
创建 XML 读取器
尽管 .NET 提供XmlReader类的具体实现(例如XmlTextReader类、XmlNodeReader类和XmlValidatingReader类),但我们建议仅在以下情况下使用这些专用类:
- 如果要从 XmlNode 对象读取 XML DOM 子树,请使用该 XmlNodeReader 类。 (但是,此类不支持 DTD 或架构验证。)
- 如果必须根据请求扩展实体,则不希望文本内容规范化,或者不希望返回默认属性,请使用 XmlTextReader 该类。
若要指定要在 XML 读取器上启用的功能集,请将对象 System.Xml.XmlReaderSettings 传递给 Create 该方法。 可以使用单个 System.Xml.XmlReaderSettings 对象创建具有相同功能的多个读取器,或修改该 System.Xml.XmlReaderSettings 对象以创建具有不同功能集的新读取器。 还可以将功能轻松添加到现有读取器。
如果不使用 System.Xml.XmlReaderSettings 对象,则使用默认设置。 请参阅Create 参考页以获取详细信息。
XmlReader 会在发生 XML 分析错误时引发 XmlException。 引发异常后,读取器的状态不可预测。 例如,报告的节点类型可能与当前节点的实际节点类型不同。 使用 ReadState 属性检查读取器是否处于错误状态。
验证 XML 数据
若要定义 XML 文档的结构及其元素关系、数据类型和内容约束,请使用文档类型定义(DTD)或 XML 架构定义语言 (XSD) 架构。 如果 XML 文档满足 W3C XML 1.0 建议定义的所有语法要求,则认为 XML 文档格式良好。 如果格式良好且符合 DTD 或架构定义的约束,则它被视为有效。 (请参阅 W3C XML 架构第 1 部分:结构和W3C XML 架构第 2 部分:数据类型 建议。因此,尽管所有有效的 XML 文档都格式良好,但并非所有格式正确的 XML 文档都有效。
可以针对 DTD、内联 XSD 架构或存储在对象中的 XmlSchemaSet XSD 架构(缓存)验证数据;这些方案在 Create 参考页上介绍。 XmlReader 不支持 XML 数据缩减 (XDR) 架构验证。
在类上使用 XmlReaderSettings 以下设置来指定实例支持的验证类型(如果有 XmlReader )。
使用此 XmlReaderSettings 成员 | 若要指定 |
---|---|
DtdProcessing 属性 | 是否允许 DTD 处理。 默认值是禁止 DTD 处理。 |
ValidationType 属性 | 读取器是否应验证数据,以及要执行的验证类型(DTD 或架构)。 默认值为无数据验证。 |
ValidationEventHandler 事件 | 用于接收有关验证事件的信息的事件处理程序。 如果未提供事件处理程序,则在第一次验证错误时抛出一个 XmlException 异常。 |
ValidationFlags 属性 | 其他验证选项(通过 XmlSchemaValidationFlags 枚举成员): - AllowXmlAttributes -- 允许实例文档中的 XML 属性 (xml:* ) 即使在架构中未定义它们时也是如此。 这些属性根据其数据类型进行验证。 请参阅XmlSchemaValidationFlags参考页以了解特定情况下要使用的设置。 (默认禁用。)- ProcessIdentityConstraints --在验证期间遇到的进程标识约束(xs:ID 、、xs:IDREF xs:key 、xs:keyref 、xs:unique )。 (默认启用)。)- ProcessSchemaLocation --处理由 xsi:schemaLocation 或 xsi:noNamespaceSchemaLocation 属性指定的架构。 (默认启用)。)- ProcessInlineSchema -- 在验证过程中处理内联 XML 架构。 (默认禁用。)- ReportValidationWarnings --如果发生验证警告,则报告事件。 如果没有 DTD 或 XML 架构来验证特定元素或属性,通常会发出警告。
ValidationEventHandler 用于通知。 (默认禁用。) |
Schemas | 用于验证的 XmlSchemaSet。 |
XmlResolver 属性 | 用于解析和访问外部资源的 XmlResolver。 这可以包括外部实体,如 DTD 和架构,以及 XML 架构中包含的任何 xs:include 或 xs:import 元素。 如果您未指定XmlResolver,则XmlReader使用默认XmlUrlResolver且没有用户凭据。 |
数据一致性
默认情况下,由 Create 该方法创建的 XML 读取器满足以下符合性要求:
根据 W3C XML 1.0 建议,将新行和属性值规范化。
所有实体将自动扩展,不需要用户操作。
即使在读取器未验证的情况下,文档类型定义中声明的默认属性也会始终添加。
允许声明映射到正确的 XML 命名空间 URI 的 XML 前缀。
单个
NotationType
属性声明和NmTokens
单个Enumeration
属性声明中的表示法名称是不同的。
使用这些 XmlReaderSettings 属性可以指定要启用的一致性检查的类型:
使用此属性XmlReaderSettings | 到 | 违约 |
---|---|---|
CheckCharacters 属性 | 启用或禁用对以下内容的检查: - 字符是否均在合法 XML 字符范围内,定义见“W3C XML 1.0 建议”的 2.2 字符部分。 - 所有 XML 名称都有效,由 W3C XML 1.0 建议的 2.3 通用语法构造 部分定义。 当此属性设置为 true (默认值)时,如果 XML 文件包含非法字符或无效的 XML 名称(例如,元素名称以数字开头),则会引发XmlException异常。 |
已启用字符和名称验证。 将CheckCharacters设置为 false 可关闭字符实体引用的字符检查。 如果读取器正在处理文本数据,则无论此设置如何,它始终检查 XML 名称是否有效。
注意: 当存在 DTD 时,XML 1.0 建议需要文档级一致性。 因此,如果读取器被配置为支持ConformanceLevel.Fragment,但 XML 数据中包含文档类型定义(DTD),则会引发XmlException。 |
ConformanceLevel 属性 | 选择要强制执行的一致性级别: - Document。 符合 格式正确的 XML 1.0 文档的规则。 - Fragment。 符合格式标准的文档片段适用的规则,这些片段可以用作外部分析实体。 - Auto.符合读取器决定的级别。 如果数据不符合XmlException,则会引发异常。 |
Document |
浏览节点
当前节点是 XML 读取器当前放置的 XML 节点。 所有 XmlReader 方法都执行与此节点相关的作,并且所有 XmlReader 属性都反映当前节点的值。
通过以下方法可以轻松浏览节点和分析数据。
使用此 XmlReaderSettings 方法 | 到 |
---|---|
Read | 读取第一个节点,然后每次前进一个节点来读取整个流。 此类调用通常在 while 循环中执行。使用 NodeType 属性获取当前节点的类型(例如属性、注释、元素等)。 |
Skip | 跳过当前节点的子级并移动到下一个节点。 |
MoveToContent 和 MoveToContentAsync | 跳过非内容节点并移动到下一个内容节点或文件末尾。 非内容节点包括ProcessingInstruction、DocumentType、Comment和WhitespaceSignificantWhitespace。 内容节点包括非空白文本、 CDATA和 EntityReferenceEndEntity。 |
ReadSubtree | 读取元素及其所有子元素,然后返回一个新XmlReader实例,并设置为ReadState.Initial。 此方法可用于围绕 XML 元素创建边界;例如,如果要将数据传递到另一个组件进行处理,并且想要限制组件可以访问的数据量。 |
有关在读取整个文本流时一次前进一个节点并显示每个节点的类型的示例,请参阅 XmlReader.Read 参考页面。
以下部分介绍如何读取特定类型的数据,例如元素、属性和类型化数据。
读取 XML 元素
下表列出了类为处理元素提供的方法和属性 XmlReader 。 在 XmlReader 元素上定位之后,节点属性(例如 Name)将反映元素值。 除了下面所述的成员之外,类的任何常规方法和属性 XmlReader 还可用于处理元素。 例如,可以使用 ReadInnerXml 该方法读取元素的内容。
注释
有关开始标记、结束标记和空元素标记的定义 ,请参阅 W3C XML 1.0 建议的第 3.1 部分。
使用此 XmlReader 成员 | 到 |
---|---|
IsStartElement 方法 | 检查当前节点是否为开始标记或空元素标记。 |
ReadStartElement 方法 | 检查当前节点是否为元素,并将读取器前进到下一个节点(调用IsStartElement后紧接着调用Read)。 |
ReadEndElement 方法 | 检查当前节点是否为结束标记,然后将读取器移动到下一个节点。 |
ReadElementString 方法 | 读取纯文本元素。 |
ReadToDescendant 方法 | 将 XML 读取器前进到具有指定名称的下一个后代(子级)元素。 |
ReadToNextSibling 方法 | 将 XML 读取器前进到具有指定名称的下一个同级元素。 |
IsEmptyElement 属性 | 检查当前元素是否具有结束元素标记。 例如: - <item num="123"/> (IsEmptyElement 为 true .)- <item num="123"> </item> (IsEmptyElement 是 false ,虽然元素的内容为空。) |
有关读取元素文本内容的示例,请参阅该方法 ReadString 。 以下示例使用 while
循环处理元素。
while (reader.Read()) {
if (reader.IsStartElement()) {
if (reader.IsEmptyElement)
{
Console.WriteLine($"<{reader.Name}/>");
}
else {
Console.Write("<{0}> ", reader.Name);
reader.Read(); // Read the start tag.
if (reader.IsStartElement()) // Handle nested elements.
Console.Write("\r\n<{0}>", reader.Name);
Console.WriteLine(reader.ReadString()); //Read the text content of the element.
}
}
}
While reader.Read()
If reader.IsStartElement() Then
If reader.IsEmptyElement Then
Console.WriteLine("<{0}/>", reader.Name)
Else
Console.Write("<{0}> ", reader.Name)
reader.Read() ' Read the start tag.
If reader.IsStartElement() Then ' Handle nested elements.
Console.Write(vbCr + vbLf + "<{0}>", reader.Name)
End If
Console.WriteLine(reader.ReadString()) 'Read the text content of the element.
End If
End If
End While
读取 XML 属性
XML 属性通常存在于元素上,但它们也允许在 XML 声明和文档类型节点上找到。
定位在元素节点上时, MoveToAttribute 该方法允许你浏览元素的属性列表。 请注意,调用后MoveToAttribute,节点属性,例如NameNamespaceURI,并Prefix反映该属性的属性,而不是属性所属的元素的属性。
该 XmlReader 类提供这些方法和属性来读取和处理元素上的属性。
使用此 XmlReader 成员 | 到 |
---|---|
HasAttributes 属性 | 检查当前节点是否有任何属性。 |
AttributeCount 属性 | 获取当前元素上的属性数。 |
MoveToFirstAttribute 方法 | 移动到元素中的第一个属性。 |
MoveToNextAttribute 方法 | 移动到元素中的下一个属性。 |
MoveToAttribute 方法 | 移动到指定的属性。 |
GetAttribute 方法或 Item[] 属性 | 获取指定属性的值。 |
IsDefault 属性 | 检查当前节点是否是从 DTD 或架构中定义的默认值生成的属性。 |
MoveToElement 方法 | 移动到拥有当前属性的元素。 使用此方法在浏览其属性后返回到元素。 |
ReadAttributeValue 方法 | 将属性值分析为一个或多个Text 或EntityReference EndEntity 节点。 |
任何常规 XmlReader 方法和属性也可用于处理属性。 例如,在 XmlReader 定位于某个属性上之后,Name 和 Value 属性反映该属性的值。 还可以使用任何内容 Read
方法获取特性的值。
此示例使用 AttributeCount 属性在元素上的所有属性中导航。
// Display all attributes.
if (reader.HasAttributes) {
Console.WriteLine("Attributes of <" + reader.Name + ">");
for (int i = 0; i < reader.AttributeCount; i++) {
Console.WriteLine($" {reader[i]}");
}
// Move the reader back to the element node.
reader.MoveToElement();
}
' Display all attributes.
If reader.HasAttributes Then
Console.WriteLine("Attributes of <" + reader.Name + ">")
Dim i As Integer
For i = 0 To (reader.AttributeCount - 1)
Console.WriteLine(" {0}", reader(i))
Next i
' Move the reader back to the element node.
reader.MoveToElement()
End If
此示例使用 MoveToNextAttribute 循环中的 while
方法在属性中导航。
if (reader.HasAttributes) {
Console.WriteLine("Attributes of <" + reader.Name + ">");
while (reader.MoveToNextAttribute()) {
Console.WriteLine($" {reader.Name}={reader.Value}");
}
// Move the reader back to the element node.
reader.MoveToElement();
}
If reader.HasAttributes Then
Console.WriteLine("Attributes of <" + reader.Name + ">")
While reader.MoveToNextAttribute()
Console.WriteLine(" {0}={1}", reader.Name, reader.Value)
End While
' Move the reader back to the element node.
reader.MoveToElement()
End If
读取 XML 声明节点上的属性
当 XML 读取器定位在 XML 声明节点上时,该 Value 属性将版本、独立和编码信息作为单个字符串返回。 由 XmlReader 方法、Create 类和 XmlTextReader 类创建的 XmlValidatingReader 对象会将版本、独立项和编码项作为特性公开。
读取文档类型节点上的特性
当 XML 读取器定位在文档类型节点上时, GetAttribute 该方法和 Item[] 属性可用于返回 SYSTEM 和 PUBLIC 文本的值。 例如,调用 reader.GetAttribute("PUBLIC")
返回 PUBLIC 值。
读取处理指令节点上的属性
当定位 XmlReader 在处理指令节点上时,该 Value 属性将返回整个文本内容。 处理指令节点中的项不被视为属性。 无法使用GetAttribute或MoveToAttribute方法读取它们。
读取 XML 内容
XmlReader 类包括以下成员,这些成员从 XML 文件读取内容,并将内容作为字符串值返回。 (若要返回 CLR 类型,请参阅 “转换为 CLR 类型”。)
使用此 XmlReader 成员 | 到 |
---|---|
Value 属性 | 获取当前节点的文本内容。 返回的值取决于节点类型;有关详细信息, Value 请参阅参考页。 |
ReadString 方法 | 获取作为字符串的元素或文本节点的内容。 此方法将在遇到处理指令和注释时停止运行。 有关此方法如何处理特定节点类型的详细信息,请参阅 ReadString 参考页。 |
ReadInnerXml 和 ReadInnerXmlAsync 方法 | 获取当前节点的所有内容,包括标记,但不包括开始和结束标记。 例如,对于:<node>this<child id="123"/></node> ReadInnerXml 返回: this<child id="123"/> |
ReadOuterXml 和 ReadOuterXmlAsync 方法 | 获取当前节点及其子节点的所有内容,包括标记和开始/结束标记。 例如,对于:<node>this<child id="123"/></node> ReadOuterXml 返回: <node>this<child id="123"/></node> |
转换为 CLR 类型
可以使用类(下表中列出的)成员 XmlReader 读取 XML 数据并将值作为公共语言运行时 (CLR) 类型而不是字符串返回。 这些成员使你能够在最适合编码任务的表示形式中获取值,而无需手动分析或转换字符串值。
ReadElementContentAs 方法只能在元素节点类型上调用。 这些方法不能用于包含子元素或混合内容的元素。 调用时,对象 XmlReader 读取开始标记,读取元素内容,然后越过结束元素标记。 将忽略处理指令和注释并展开实体。
ReadContentAs 方法读取当前读取器位置的文本内容,如果 XML 数据没有任何与之关联的架构或数据类型信息,请将文本内容转换为请求的返回类型。 文本、空白、有效空白和 CDATA 节串联在一起。 系统会跳过注释和处理指令,并自动解析实体引用。
该 XmlReader 类使用 W3C XML 架构第 2 部分:数据类型 建议定义的规则。
如果元素由于其格式而无法轻松转换为 CLR 类型,则可以使用架构映射来确保成功转换。 下面的示例使用 .xsd 文件将 hire-date
元素 xs:date
转换为类型,然后使用 ReadElementContentAsDateTime 该方法将元素作为 DateTime 对象返回。
输入(hireDate.xml):
<employee xmlns="urn:empl-hire">
<ID>12365</ID>
<hire-date>2003-01-08</hire-date>
<title>Accountant</title>
</employee>
架构 (hireDate.xsd):
<?xml version="1.0"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="urn:empl-hire" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="employee">
<xs:complexType>
<xs:sequence>
<xs:element name="ID" type="xs:unsignedShort" />
<xs:element name="hire-date" type="xs:date" />
<xs:element name="title" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
代码:
// Create a validating XmlReader object. The schema
// provides the necessary type information.
XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.Schemas.Add("urn:empl-hire", "hireDate.xsd");
using (XmlReader reader = XmlReader.Create("hireDate.xml", settings)) {
// Move to the hire-date element.
reader.MoveToContent();
reader.ReadToDescendant("hire-date");
// Return the hire-date as a DateTime object.
DateTime hireDate = reader.ReadElementContentAsDateTime();
Console.WriteLine($"Six Month Review Date: {hireDate.AddMonths(6)}");
}
' Create a validating XmlReader object. The schema
' provides the necessary type information.
Dim settings As XmlReaderSettings = New XmlReaderSettings()
settings.ValidationType = ValidationType.Schema
settings.Schemas.Add("urn:empl-hire", "hireDate.xsd")
Using reader As XmlReader = XmlReader.Create("hireDate.xml", settings)
' Move to the hire-date element.
reader.MoveToContent()
reader.ReadToDescendant("hire-date")
' Return the hire-date as a DateTime object.
Dim hireDate As DateTime = reader.ReadElementContentAsDateTime()
Console.WriteLine("Six Month Review Date: {0}", hireDate.AddMonths(6))
End Using
输出:
Six Month Review Date: 7/8/2003 12:00:00 AM
异步编程
大多数XmlReader方法都有异步对应项,其方法名称的末尾带有“Async”。 例如,异步等效项 ReadContentAsObject 为 ReadContentAsObjectAsync.
以下方法可用于异步方法调用:
- GetAttribute
- MoveToAttribute
- MoveToFirstAttribute
- MoveToNextAttribute
- MoveToElement
- ReadAttributeValue
- ReadSubtree
- ResolveEntity
以下部分介绍了没有异步对应项的方法的异步用法。
ReadStartElement 方法
public static async Task ReadStartElementAsync(this XmlReader reader, string localname, string ns)
{
if (await reader.MoveToContentAsync() != XmlNodeType.Element)
{
throw new InvalidOperationException(reader.NodeType.ToString() + " is an invalid XmlNodeType");
}
if ((reader.LocalName == localname) && (reader.NamespaceURI == ns))
{
await reader.ReadAsync();
}
else
{
throw new InvalidOperationException("localName or namespace doesn’t match");
}
}
<Extension()>
Public Async Function ReadStartElementAsync(reader As XmlReader, localname As String, ns As String) As Task
If (Await reader.MoveToContentAsync() <> XmlNodeType.Element) Then
Throw New InvalidOperationException(reader.NodeType.ToString() + " is an invalid XmlNodeType")
End If
If ((reader.LocalName = localname) And (reader.NamespaceURI = ns)) Then
Await reader.ReadAsync()
Else
Throw New InvalidOperationException("localName or namespace doesn’t match")
End If
End Function
ReadEndElement 方法
public static async Task ReadEndElementAsync(this XmlReader reader)
{
if (await reader.MoveToContentAsync() != XmlNodeType.EndElement)
{
throw new InvalidOperationException();
}
await reader.ReadAsync();
}
<Extension()>
Public Async Function ReadEndElementAsync(reader As XmlReader) As task
If (Await reader.MoveToContentAsync() <> XmlNodeType.EndElement) Then
Throw New InvalidOperationException()
End If
Await reader.ReadAsync()
End Function
ReadToNextSibling 方法
public static async Task<bool> ReadToNextSiblingAsync(this XmlReader reader, string localName, string namespaceURI)
{
if (localName == null || localName.Length == 0)
{
throw new ArgumentException("localName is empty or null");
}
if (namespaceURI == null)
{
throw new ArgumentNullException("namespaceURI");
}
// atomize local name and namespace
localName = reader.NameTable.Add(localName);
namespaceURI = reader.NameTable.Add(namespaceURI);
// find the next sibling
XmlNodeType nt;
do
{
await reader.SkipAsync();
if (reader.ReadState != ReadState.Interactive)
break;
nt = reader.NodeType;
if (nt == XmlNodeType.Element &&
((object)localName == (object)reader.LocalName) &&
((object)namespaceURI ==(object)reader.NamespaceURI))
{
return true;
}
} while (nt != XmlNodeType.EndElement && !reader.EOF);
return false;
}
<Extension()>
Public Async Function ReadToNextSiblingAsync(reader As XmlReader, localName As String, namespaceURI As String) As Task(Of Boolean)
If (localName = Nothing Or localName.Length = 0) Then
Throw New ArgumentException("localName is empty or null")
End If
If (namespaceURI = Nothing) Then
Throw New ArgumentNullException("namespaceURI")
End If
' atomize local name and namespace
localName = reader.NameTable.Add(localName)
namespaceURI = reader.NameTable.Add(namespaceURI)
' find the next sibling
Dim nt As XmlNodeType
Do
Await reader.SkipAsync()
If (reader.ReadState <> ReadState.Interactive) Then
Exit Do
End If
nt = reader.NodeType
If ((nt = XmlNodeType.Element) And
((CObj(localName) = CObj(reader.LocalName))) And
(CObj(namespaceURI) = CObj(reader.NamespaceURI))) Then
Return True
End If
Loop While (nt <> XmlNodeType.EndElement And (Not reader.EOF))
Return False
End Function
ReadToFollowing 方法
public static async Task<bool> ReadToFollowingAsync(this XmlReader reader, string localName, string namespaceURI)
{
if (localName == null || localName.Length == 0)
{
throw new ArgumentException("localName is empty or null");
}
if (namespaceURI == null)
{
throw new ArgumentNullException("namespaceURI");
}
// atomize local name and namespace
localName = reader.NameTable.Add(localName);
namespaceURI = reader.NameTable.Add(namespaceURI);
// find element with that name
while (await reader.ReadAsync())
{
if (reader.NodeType == XmlNodeType.Element && ((object)localName == (object)reader.LocalName) && ((object)namespaceURI == (object)reader.NamespaceURI))
{
return true;
}
}
return false;
}
<Extension()>
Public Async Function ReadToFollowingAsync(reader As XmlReader, localName As String, namespaceURI As String) As Task(Of Boolean)
If (localName = Nothing Or localName.Length = 0) Then
Throw New ArgumentException("localName is empty or null")
End If
If (namespaceURI = Nothing) Then
Throw New ArgumentNullException("namespaceURI")
End If
' atomize local name and namespace
localName = reader.NameTable.Add(localName)
namespaceURI = reader.NameTable.Add(namespaceURI)
' find element with that name
While (Await reader.ReadAsync())
If ((reader.NodeType = XmlNodeType.Element) And
(CObj(localName) = CObj(reader.LocalName)) And
(CObj(namespaceURI) = CObj(reader.NamespaceURI))) Then
Return True
End If
End While
Return False
End Function
ReadToDescendant 方法
public static async Task<bool> ReadToDescendantAsync(this XmlReader reader, string localName, string namespaceURI)
{
if (localName == null || localName.Length == 0)
{
throw new ArgumentException("localName is empty or null");
}
if (namespaceURI == null)
{
throw new ArgumentNullException("namespaceURI");
}
// save the element or root depth
int parentDepth = reader.Depth;
if (reader.NodeType != XmlNodeType.Element)
{
// adjust the depth if we are on root node
if (reader.ReadState == ReadState.Initial)
{
parentDepth--;
}
else
{
return false;
}
}
else if (reader.IsEmptyElement)
{
return false;
}
// atomize local name and namespace
localName = reader.NameTable.Add(localName);
namespaceURI = reader.NameTable.Add(namespaceURI);
// find the descendant
while (await reader.ReadAsync() && reader.Depth > parentDepth)
{
if (reader.NodeType == XmlNodeType.Element && ((object)localName == (object)reader.LocalName) && ((object)namespaceURI == (object)reader.NamespaceURI))
{
return true;
}
}
return false;
}
<Extension()>
Public Async Function ReadToDescendantAsync(reader As XmlReader, localName As String, namespaceURI As String) As Task(Of Boolean)
If (localName = Nothing Or localName.Length = 0) Then
Throw New ArgumentException("localName is empty or null")
End If
If (namespaceURI = Nothing) Then
Throw New ArgumentNullException("namespaceURI")
End If
' save the element or root depth
Dim parentDepth As Integer = reader.Depth
If (reader.NodeType <> XmlNodeType.Element) Then
' adjust the depth if we are on root node
If (reader.ReadState = ReadState.Initial) Then
parentDepth -= 1
Else
Return False
End If
ElseIf (reader.IsEmptyElement) Then
Return False
End If
' atomize local name and namespace
localName = reader.NameTable.Add(localName)
namespaceURI = reader.NameTable.Add(namespaceURI)
' find the descendant
While (Await reader.ReadAsync() And reader.Depth > parentDepth)
If (reader.NodeType = XmlNodeType.Element And
(CObj(localName) = CObj(reader.LocalName)) And
(CObj(namespaceURI) = CObj(reader.NamespaceURI))) Then
Return True
End If
End While
Return False
End Function
安全注意事项
使用 XmlReader 类时,请考虑以下事项:
从 XmlReader 引发的异常可能会泄露你不希望在应用中显示的路径信息。 你的应用必须捕获异常并相应地处理它们。
如果担心拒绝服务问题或处理的是不受信任的源,请不要启用 DTD 处理。 XmlReader 方法创建的 Create 对象默认情况下禁用 DTD 处理。
如果启用了 DTD 处理,则可以使用 XmlSecureResolver 来限制 XmlReader 可以访问的资源。 还可以设计应用,使 XML 处理受内存和时间限制。 例如,可以在 ASP.NET 应用中配置超时限制。
XML 数据可以包含对外部资源的引用,例如架构文件。 默认情况下,外部资源是通过使用 XmlUrlResolver 没有用户凭据的对象解析的。 可以通过执行以下任一操作来进一步增强安全性:
通过将XmlReader属性设置为XmlReaderSettings.XmlResolver对象,限制XmlSecureResolver可访问的资源。
不允许 XmlReader 通过设置 XmlReaderSettings.XmlResolver 属性为
null
来打开任何外部资源。
默认情况下,不会设置 ProcessInlineSchema 对象的 ProcessSchemaLocation 和 XmlReaderSettings 验证标志。 XmlReader这有助于在处理来自不受信任的源的 XML 数据时防范基于架构的攻击。 设置了这些标志后,XmlResolver 对象的 XmlReaderSettings 用于在 XmlReader 中解析实例文档中遇到的架构位置。 如果XmlResolver属性设置为
null
,即使ProcessInlineSchema和ProcessSchemaLocation验证标志已设置,架构位置也不会解析。验证期间添加的架构添加新类型,并可以更改正在验证的文档的验证结果。 因此,只能从受信任的源解析外部架构。
建议在高可用性场景下验证不受信任的大型 XML 文档时禁用 ProcessIdentityConstraints 标志,特别是当架构对文档的大部分内容具有身份约束时。 默认情况下,此标志处于启用状态。
XML 数据可以包含大量属性、命名空间声明、嵌套元素等,这些属性需要大量时间来处理。 若要限制发送到该 XmlReader输入的大小,可以:
通过设置 MaxCharactersInDocument 属性来限制文档的大小。
通过设置 MaxCharactersFromEntities 属性来限制扩展实体导致的字符数。
为
IStream
创建XmlReader的自定义实现。
该方法 ReadValueChunk 可用于处理大型数据流。 此方法一次读取少量字符,而不是为整个值分配单个字符串。
读取具有大量唯一本地名称、命名空间或前缀的 XML 文档时,可能会出现问题。 如果您使用派生自XmlReader的类,并且对每个项调用LocalName、Prefix或NamespaceURI属性,那么返回的字符串将被添加到NameTable中。 NameTable 包含的集合的大小绝不会减小,这会导致字符串句柄的虚拟内存泄漏。 对此,一种缓解措施是从 NameTable 类进行派生并强制实施最大大小配额。 (无法阻止使用 NameTable,也无法在 NameTable 已满时进行切换)。 另一种缓解措施是避免使用提到的属性,而是尽可能将 MoveToAttribute 方法与 IsStartElement 方法一起使用;这些方法不返回字符串,从而避免过度填充 NameTable 集合的问题。
XmlReaderSettings 对象可以包含敏感信息,例如用户凭据。 不受信任的组件可以使用 XmlReaderSettings 对象及其用户凭据创建 XmlReader 对象来读取数据。 缓存 XmlReaderSettings 对象或将对象从一个组件传递到 XmlReaderSettings 另一个组件时要小心。
不接受来自不受信任的源的支持组件,例如 NameTable、 XmlNamespaceManager对象和 XmlResolver 对象。