共用方式為


System.Xml.XmlReader 類別

本文提供此 API 參考文件的補充備註。

XmlReader 提供檔或數據流中 XML 數據的正向唯讀取權。 這個類別符合 W3C Extensible Markup Language (XML) 1.0 (第四版) 和 XML 1.0 (第三版) 命名空間 建議。

XmlReader 方法可讓您流覽 XML 資料並讀取節點的內容。 類別的屬性會反映目前節點的值,也就是讀取器的位置。 ReadState 屬性值表示 XML 讀取器的目前狀態。 例如,屬性會由 XmlReader.Read 方法設定為 ReadState.Initial,並由 XmlReader.Close 方法設定為 ReadState.ClosedXmlReader 也會針對 DTD 或架構提供數據一致性檢查和驗證。

XmlReader 會使用提取模型來擷取數據。 此模型:

  • 藉由自然、由上而下的程式精簡來簡化狀態管理。
  • 支援多個輸入數據流和分層。
  • 可讓客戶端為剖析器提供直接寫入字串的緩衝區,因而避免需要額外的字串複製。
  • 支援選擇性處理。 客戶端可以跳過項目並處理應用程式感興趣的項目。 您也可以事先設定屬性,以管理 XML 數據流的處理方式(例如正規化)。

建立 XML 讀取器

使用 Create 方法來建立 XmlReader 實例。

雖然 .NET 提供 XmlReader 類別的具體實作,例如 XmlTextReaderXmlNodeReaderXmlValidatingReader 類別,但我們建議您只在下列案例中使用特製化類別:

  • 當您想要從 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 建議所定義的所有語法需求,則會視為格式良好。 如果格式良好且也符合其 DTD 或架構所定義的條件約束,則會將其視為有效。 (請參閱 W3C XML 架構第 1 部分:結構W3C XML 架構第 2 部分:數據類型 建議。因此,雖然所有有效的 XML 檔都格式良好,但並非所有格式正確的 XML 檔都是有效的。

您可以針對 DTD、內嵌 XSD 架構,或儲存在 XmlSchemaSet 物件中的 XSD 架構來驗證資料;這些案例會在 Create 參考頁面上描述。 XmlReader 不支援 XML-Data 縮減 (XDR) 架構驗證。

您可以在 XmlReaderSettings 類別上使用下列設定來指定 XmlReader 實例支援的驗證類型。

使用此 XmlReaderSettings 會員 若要指定
DtdProcessing 屬性 是否允許 DTD 處理。 預設值是不允許 DTD 處理。
ValidationType 屬性 讀取器是否應該驗證數據,以及要執行的驗證類型 (DTD 或架構)。 預設情況下無數據驗證。
ValidationEventHandler DocumentCompleted 事件 用來接收驗證事件相關信息的事件處理程式。 如果未提供事件處理程式,則會在第一個驗證錯誤時擲回 XmlException
ValidationFlags 屬性 透過 XmlSchemaValidationFlags 列舉成員的其他驗證選項:

- AllowXmlAttributes-- 允許實例檔中的 XML 屬性 (xml:*),即使它們未在架構中定義也一樣。 屬性會根據其數據類型進行驗證。 如需在特定案例中使用的設定,請參閱 XmlSchemaValidationFlags 參考頁面。 (預設為停用。)
- ProcessIdentityConstraints --處理驗證期間遇到的xs:IDxs:IDREFxs:keyxs:keyrefxs:unique等身分識別條件約束。 (預設為啟用。)
- ProcessSchemaLocation --處理由 xsi:schemaLocationxsi:noNamespaceSchemaLocation 屬性指定的架構。 (預設為啟用。)
- ProcessInlineSchema-- 在驗證期間處理內嵌 XML 架構。 (預設為停用。)
- ReportValidationWarnings--如果發生驗證警告,請報告事件。 當沒有 DTD 或 XML 架構可驗證特定專案或屬性時,通常會發出警告。 ValidationEventHandler 用於發送通知。 預設為未啟用。
Schemas 用於驗證的 XmlSchemaSet
XmlResolver 屬性 用來解析和存取外部資源的 XmlResolver。 這可以包含外部實體,例如 DTD 和綱要,以及 XML 架構中包含的任何 xs:includexs:import 元素。 如果您未指定 XmlResolver,則 XmlReader 會使用沒有使用者認證的預設 XmlUrlResolver

數據一致性

Create 方法所建立的 XML 讀取器預設符合下列合規性需求:

  • 新的行和屬性值會根據 W3C XML 1.0 建議正規化。

  • 所有實體都會自動展開。

  • 即使讀取器未驗證,在文件類型定義中宣告的預設屬性也一律會加入。

  • 允許 XML 前置詞宣告對應至正確的 XML 命名空間 URI。

  • 單一 NotationType 屬性宣告中的表示法名稱,以及單一 Enumeration 屬性宣告中的 NmTokens 是相異的。

使用這些 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 略過目前節點的子系,並移至下一個節點。
MoveToContentMoveToContentAsync 略過非內容節點,並移至下一個內容節點或檔尾。

非內容節點包括 ProcessingInstructionDocumentTypeCommentWhitespaceSignificantWhitespace

內容節點包含非空白文字、CDATAEntityReferenceEndEntity
ReadSubtree 讀取一個元素及其所有子元素,並傳回設定為 ReadState.Initial的新 XmlReader 實例。

這個方法很適合用來建立 XML 元素周圍的界限;例如,如果您想要將數據傳遞至另一個元件進行處理,而且您想要限制元件可以存取的數據量。

如需一次流覽文本流中的一個節點並顯示每個節點類型的範例,請參閱 XmlReader.Read 參考頁面。

下列各節說明如何讀取特定類型的數據,例如元素、屬性和具型別的數據。

讀取 XML 元素

下表列出 XmlReader 類別提供處理元素的方法和屬性。 將 XmlReader 放置在專案上之後,節點屬性,例如 Name,會反映專案值。 除了下面所述的成員之外,XmlReader 類別的任何一般方法和屬性也可以用來處理元素。 例如,您可以使用 ReadInnerXml 方法來讀取項目的內容。

備註

請參閱 W3C XML 1.0 建議 的第 3.1 節,以了解開始標記、結束標記和空白元素標記的定義。

使用此 XmlReader 成員 發往
IsStartElement 方法 檢查目前節點是否為開始標記或空白項目標記。
ReadStartElement 方法 檢查目前的節點是否為元素,並將讀取器移動到下一個節點(呼叫 IsStartElement,然後接著 Read)。
ReadEndElement 方法 檢查目前的節點是否為結束標籤,並將讀取器前進到下一個節點。
ReadElementString 方法 讀取純文字元素。
ReadToDescendant 方法 將 XML 讀取器前進到具有指定名稱的下一個子代 (child) 元素。
ReadToNextSibling 方法 將 XML 讀取器前進到具有指定名稱的下一個同級元素。
IsEmptyElement 屬性 檢查目前元素是否有結束元素標籤。 例如:

- <item num="123"/>IsEmptyElementtrue
- <item num="123"> </item>IsEmptyElementfalse,雖然元素的內容是空的。

如需讀取元素文字內容的範例,請參閱 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 之後,NameNamespaceURIPrefix 等節點屬性會反映該屬性的屬性,而不是屬性所屬元素的屬性。

XmlReader 類別會提供這些方法和屬性,以讀取和處理元素上的屬性。

使用此 XmlReader 成員 發往
HasAttributes 屬性 檢查目前節點是否有任何屬性。
AttributeCount 屬性 取得目前元素上的屬性數目。
MoveToFirstAttribute 方法 移至 元素中的第一個屬性。
MoveToNextAttribute 方法 移至元素中的下一個屬性。
MoveToAttribute 方法 移至指定的屬性。
GetAttribute 方法或 Item[] 屬性 取得指定屬性的值。
IsDefault 屬性 檢查目前節點是否為從 DTD 或架構中定義之預設值產生的屬性。
MoveToElement 方法 移至擁有目前屬性的元素。 使用此方法來在瀏覽其屬性後回到那個元素。
ReadAttributeValue 方法 將屬性值剖析為一或多個 TextEntityReferenceEndEntity 節點。

任何一般 XmlReader 方法和屬性也可以用來處理屬性。 例如,在屬性上放置 XmlReader 之後,NameValue 屬性會反映屬性的值。 您也可以使用任何內容 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

這個範例會使用 while 迴圈中的 MoveToNextAttribute 方法來巡覽屬性。

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 屬性會以單一字串的形式傳回版本、獨立和編碼資訊。 XmlReaderCreate 方法、XmlTextReader 類別和 XmlValidatingReader 類別建立的物件,會將版本、獨立性和編碼專案公開為屬性。

檔案類型節點上的讀取屬性

當 XML 讀取器位於檔案類型節點上時,可以使用 GetAttribute 方法和 Item[] 屬性來傳回 SYSTEM 和 PUBLIC 常值的值。 例如,呼叫 reader.GetAttribute("PUBLIC") 會傳回 PUBLIC 值。

處理指令節點上的讀取屬性

XmlReader 位於處理指令節點上時,Value 屬性會傳回整個文字內容。 處理指令節點中的專案不會被視為屬性。 無法使用 GetAttributeMoveToAttribute 方法來讀取它們。

讀取 XML 內容

XmlReader 類別包含下列成員,這些成員會從 XML 檔案讀取內容,並以字串值的形式傳回內容。 (若要傳回 CLR 類型,請參閱 轉換成 CLR 類型

使用此 XmlReader 成員 發往
Value 屬性 取得目前節點的文字內容。 傳回的值取決於節點類型;如需詳細資訊,請參閱 Value 參考頁面。
ReadString 方法 以字串形式取得元素或文字節點的內容。 此方法會在處理指令和批注時停止。

如需此方法如何處理特定節點類型的詳細資訊,請參閱 ReadString 參考頁面。
ReadInnerXmlReadInnerXmlAsync 方法 取得目前節點的所有內容,包括標記,但不包括開始和結束標籤。 例如,針對:

<node>this<child id="123"/></node>

ReadInnerXml 會傳回:

this<child id="123"/>
ReadOuterXmlReadOuterXmlAsync 方法 取得目前節點及其子系的所有內容,包括標記和開始/結束標記。 例如,針對:

<node>this<child id="123"/></node>

ReadOuterXml 會傳回:

<node>this<child id="123"/></node>

轉換成 CLR 類型

您可以使用 XmlReader 類別的成員(下表所列)來讀取 XML 數據,並以 Common Language Runtime (CLR) 類型傳回值,而不是字串。 這些成員可讓您在表示法中取得最適合編碼工作的值,而不需要手動剖析或轉換字串值。

  • ReadElementContentAs 方法只能在專案節點類型上呼叫。 這些方法不能用於包含子元素或混合內容的元素上。 呼叫時,XmlReader 物件會讀取開始標記、讀取元素內容,然後移動結束元素標記。 系統會忽略處理指令和批注,並展開實體。

  • ReadContentAs 方法會讀取目前讀取器位置的文字內容,如果 XML 數據沒有任何與其相關聯的架構或數據類型資訊,請將文字內容轉換為要求的傳回型別。 文字、空格符、顯著空白和 CDATA 區段會串連。 會略過批注和處理指令,並自動解析實體參考。

XmlReader 類別會使用 W3C XML 架構第 2 部分:資料類型 建議所定義的規則。

使用此 XmlReader 方法 傳回此 CLR 類型
ReadContentAsBooleanReadElementContentAsBoolean Boolean
ReadContentAsDateTimeReadElementContentAsDateTime DateTime
ReadContentAsDoubleReadElementContentAsDouble Double
ReadContentAsLongReadElementContentAsLong Int64
ReadContentAsIntReadElementContentAsInt Int32
ReadContentAsStringReadElementContentAsString String
ReadContentAsReadElementContentAs 您使用 returnType 參數指定的類型
ReadContentAsObjectReadElementContentAsObject 最適當的類型,如 XmlReader.ValueType 屬性所指定。 如需對應資訊,請參閱 System.Xml 類別 中的 類型支援。

如果專案因為其格式而無法輕易轉換成 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

下列方法可以用於非同步方法呼叫:

下列各節說明沒有異步對應專案之方法的異步使用方式。

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 處理。 Create 方法所建立的 XmlReader 物件,默認會停用 DTD 處理。

    如果您已啟用 DTD 處理,您可以使用 XmlSecureResolver 來限制 XmlReader 可以存取的資源。 您也可以設計應用程式,讓 XML 處理受到記憶體和時間限制。 例如,您可以在 ASP.NET 應用程式中設定逾時限制。

  • XML 數據可以包含外部資源的參考,例如架構檔案。 根據預設,外部資源會使用沒有使用者認證的 XmlUrlResolver 物件來解析。 您可以執行下列其中一項來進一步保護此專案:

  • 預設不會設定 XmlReaderSettings 物件的 ProcessInlineSchemaProcessSchemaLocation 驗證旗標。 這有助於在處理來自不受信任來源的 XML 數據時,保護 XmlReader 免受架構型攻擊。 設定這些旗標時,XmlReaderSettings 物件的 XmlResolver 會用來解析在 XmlReader實例檔中遇到的架構位置。 如果 XmlResolver 屬性設定為 null,即使已設定 ProcessInlineSchemaProcessSchemaLocation 驗證旗標,也不會解析架構位置。

    在驗證期間新增的架構會新增類型,並可變更正在驗證之文件的驗證結果。 因此,只能從信任的來源解析外部架構。

    建議您在高可用性情境下驗證不受信任的大型 XML 文件時,於文件內大部分包含身分識別條件約束的架構上,停用 ProcessIdentityConstraints 旗標。 預設會啟用此旗標。

  • XML 數據可以包含大量的屬性、命名空間宣告、巢狀專案等等,需要大量的的時間來處理。 若要限制傳送至 XmlReader的輸入大小,您可以:

  • ReadValueChunk 方法可用來處理大型數據流。 這個方法會一次讀取少量字元,而不是為整個值配置單一字串。

  • 讀取具有大量唯一本機名稱、命名空間或前置詞的 XML 檔時,可能會發生問題。 如果您使用衍生自 XmlReader的類別,而且呼叫每個項目的 LocalNamePrefixNamespaceURI 屬性,則會將傳回的字串新增至 NameTableNameTable 所持有的集合容量永遠不會減少,導致字串句柄的虛擬記憶體洩漏。 一種緩和措施是從 NameTable 類別衍生,並強制執行最大尺寸限制。 (無法防止使用 NameTable,也無法在 NameTable 已滿時切換 NameTable)。 另一個緩和措施是避免使用所述的屬性,並盡可能使用 MoveToAttribute 方法與 IsStartElement 方法;這些方法不會傳回字串,因此請避免填滿 NameTable 集合的問題。

  • XmlReaderSettings 物件可以包含敏感性資訊,例如用戶認證。 不受信任的元件可以使用 XmlReaderSettings 物件及其使用者認證來建立 XmlReader 對象來讀取數據。 當快取 XmlReaderSettings 物件,或將 XmlReaderSettings 物件從一個元件傳遞至另一個元件時,請小心。

  • 不接受來自不受信任來源的支援元件,例如 NameTableXmlNamespaceManagerXmlResolver 物件。