System.Xml.XmlReader クラス

この記事では、この API のリファレンス ドキュメントを補足して解説します。

XmlReader では、ドキュメントまたはストリーム内の XML データに対する順方向専用の読み取り専用アクセスが提供されます。 このクラスは、W3C 勧告拡張マークアップ言語 (XML) 1.0 (第 4 版) および W3C 勧告 XML 1.0 の名前空間 (第 3 版) に準拠しています。

XmlReader メソッドを使用すると、XML データ間を移動し、ノードの内容を読み取ります。 クラスのプロパティには、リーダーが配置されている現在のノードの値が反映されます。 ReadState プロパティ値は、XML リーダーの現在の状態を示します。 たとえば、プロパティは XmlReader.Read メソッドによって ReadState.Initial に設定され、XmlReader.Close メソッドによって ReadState.Closed に設定されます。 XmlReader では、DTD またはスキーマに対するデータ準拠チェックと検証も提供されます。

XmlReader では、データを取得するためにプル モデルを使用します。 このモデルでは次のようになります。

  • 自然なトップダウン型の洗練された手続きによって状態管理が簡単になります。
  • 複数の入力ストリームと階層化をサポートします。
  • 文字列が直接書き込まれるバッファーをクライアントからパーサーに渡すことができます。そのため、余分な文字列コピーが不要になります。
  • 選択的な処理をサポートします。 クライアントは、項目をスキップし、目的のアプリケーションの項目を処理できます。 また、プロパティを事前に設定して、XML ストリームの処理方法 (正規化など) を管理することもできます。

XML リーダーを作成する

Create メソッドを使用して XmlReader インスタンスを作成します。

.NET には、XmlReader クラス (XmlTextReaderXmlNodeReaderXmlValidatingReader クラスなど) が具体的に実装されていますが、特殊なクラスは次のシナリオでのみ使用することをお勧めします。

  • XmlNode オブジェクトから XML DOM サブツリーを読み取る場合は、XmlNodeReader クラスを使用します。 (ただし、このクラスは DTD またはスキーマの検証をサポートしていません。)
  • 要求に応じてエンティティを展開する必要がある場合、テキスト コンテンツを正規化したくない場合、または既定の属性を返したくない場合は、XmlTextReader クラスを使用します。

XML リーダーで有効にする機能のセットを指定するには、Create メソッドに System.Xml.XmlReaderSettings オブジェクトを渡します。 単一の System.Xml.XmlReaderSettings オブジェクトを使用して、同じ機能を持つ複数のリーダーを作成したり、System.Xml.XmlReaderSettings オブジェクトを変更して別の機能セットを持つ新しいリーダーを作成したりできます。 既存のリーダーに簡単に機能を追加することもできます。

System.Xml.XmlReaderSettings オブジェクトを使用しない場合は、既定の設定が使用されます。 詳細については、Create のリファレンス ページを参照してください。

XmlReader では、XmlException on XML 解析エラーがスローされます。 例外がスローされた後、リーダーの状態は予測できません。 たとえば、報告されたノード型は、現在のノードの実際のノード型と異なる場合があります。 ReadState プロパティを使用して、リーダーがエラー状態であるかどうかを調べます。

XML データの検証

XML ドキュメントの構造、その要素間のリレーションシップ、データ型、および内容の制約を定義するには、ドキュメント型定義 (DTD: Document Type Definition) または XML スキーマ定義言語 (XSD) スキーマを使用します。 XML ドキュメントは、W3C 勧告『XML 1.0』で定義されているすべての構文要件を満たしている場合、整形式であると見なされます。 整形式であり、DTD またはスキーマによって定義された制約にも準拠している場合は、有効と見なされます。 (W3C 勧告『XML Schema Part 1: Structures』および『XML Schema Part 2: Datatypes』を参照してください。)したがって、有効な XML ドキュメントはすべて整形式ですが、整形式の XML ドキュメントがすべて有効であるとは限りません。

DTD、インライン XSD スキーマ、または XmlSchemaSet オブジェクト (キャッシュ) に格納されている XSD スキーマに対してデータを検証できます。これらのシナリオについては、Create リファレンス ページで説明します。 XmlReader では、XML-Data Reduced (XDR) スキーマの検証はサポートされていません。

XmlReaderSettings クラスで次の設定を使用して、XmlReader インスタンスがサポートする検証の種類を指定します (ある場合)。

この XmlReaderSettings メンバーを使用する 指定する型
DtdProcessing プロパティ DTD 処理を許可するかどうか。 既定では、DTD 処理は許可されません。
ValidationType プロパティ リーダーがデータを検証するかどうか、および実行する検証の種類はどれか (DTD またはスキーマ)。 既定では、データ検証は実行されません。
ValidationEventHandler イベント 検証イベントに関する情報を受け取るイベント ハンドラー。 イベント ハンドラーが提供されていない場合は、最初の検証エラーに対して XmlException がスローされます。
ValidationFlags プロパティ XmlSchemaValidationFlags 列挙メンバーを通じた追加の検証オプション:

- AllowXmlAttributes-- スキーマに定義されていない場合でも、インスタンス ドキュメント内の XML 属性 (xml:*) を許可します。 これらの属性は、それらのデータ型に基づいて検証されます。 特定のシナリオで使用する設定については、XmlSchemaValidationFlags リファレンス ページを参照してください。 (既定で無効です。)
- ProcessIdentityConstraints --検証中に検出された ID 制約 (xs:IDxs:IDREFxs:keyxs: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 属性宣言の表記名と単一の Enumeration 属性宣言の NmTokens は異なります。

有効にする準拠チェックの種類を指定するには、次の XmlReaderSettings プロパティを使用します。

この XmlReaderSettings プロパティを使用する 受信先 既定値
CheckCharacters プロパティ 次のチェックを有効または無効にします。

- 文字は W3C 勧告『XML 1.0』の 2.2 文字セクションで定義されている有効な XML 文字の範囲内です。
- W3C 勧告『XML 1.0』の 2.3 共通構文コンストラクト セクションで定義されているように、すべての XML 名が有効です。

このプロパティを true (既定値) に設定すると、XML ファイルに無効な文字または無効な XML 名が含まれている場合 (要素名が数字で始まる場合など) は、XmlException 例外がスローされます。
文字と名前のチェックが有効になっています。

CheckCharactersfalse に設定すると、文字エンティティ参照に対する文字のチェック機能がオフになります。 リーダーがテキスト データを処理している場合、この設定に関係なく、XML 名が有効であることが常に調べられます。 注: XML 1.0 勧告では、DTD がある場合にはドキュメント レベルの一致が必要です。 したがって、リーダーが ConformanceLevel.Fragment をサポートするように構成されているにもかかわらず、XML データにドキュメント型定義 (DTD) が含まれている場合は、XmlException がスローされます。
ConformanceLevel プロパティ 適用する次の準拠レベルを選択します。

- Document= 整形式の XML 1.0 ドキュメントの規則に準拠します。
- Fragment= 外部解析されたエンティティとして使用できる整形式のドキュメント フラグメントの規則に準拠します。
- Auto。リーダーが決定したレベルに準拠します。

データが準拠していない場合は、 XmlException 例外がスローされます。
Document

現在のノードとは、XML リーダーの現在位置にある XML ノードのことです。 すべての XmlReader メソッドは、このノードに関係のある操作を実行し、すべての XmlReader プロパティに現在のノードの値が反映されます。

次のメソッドを使用すると、ノード間を簡単に移動して、データを解析できます。

この XmlReaderSettings メソッドを使用する 受信先
Read 最初のノードを読み取り、ストリームを 1 ノードずつ進みます。 そのような呼び出しは通常 while ループ内で実行されます。

NodeType プロパティを使用して、現在のノードの型 (属性、コメント、要素など) を取得します。
Skip 現在のノードの子をスキップし、次のノードに移動します。
MoveToContent および MoveToContentAsync 非コンテンツ ノードをスキップし、次のコンテンツ ノードまたはファイルの末尾に移動します。

非コンテンツ ノードには ProcessingInstructionDocumentTypeCommentWhitespace、および SignificantWhitespace が含まれます。

コンテンツ ノードには、空白以外のテキスト、CDATAEntityReference、および EndEntity があります。
ReadSubtree 要素とそのすべての子を読み取り、ReadState.Initial に設定された新しい XmlReader インスタンスを返します。

このメソッドは、XML 要素の周囲に境界を作成する場合に便利です。たとえば、処理のためにデータを別のコンポーネントに渡し、コンポーネントがアクセスできるデータの量を制限する場合などです。

テキスト ストリーム間を一度に 1 ノードずつ移動し、各ノードの種類を表示する例については、XmlReader.Read リファレンス ページを参照してください。

次のセクションでは、要素、属性、型指定されたデータなど、特定の種類のデータを読み取ることができる方法について説明します。

XML 要素を読み取る

次の表には、XmlReader クラスで要素の処理のために提供されるメソッドとプロパティを一覧表示します。 XmlReader が 1 つの要素上に位置した後、Name などのノードのプロパティには要素の値が反映されます。 下に説明するメンバーに加えて、XmlReader クラスの任意の一般メソッドとプロパティも要素の処理に使用できます。 たとえば、ReadInnerXml メソッドを使用して要素のコンテンツを読むことができます。

Note

開始タグ、終了タグ、および空の要素タグの定義については、W3C 勧告『XML 1.0』のセクション 3.1 を参照してください。

この XmlReader メンバーを使用する 受信先
IsStartElement メソッド 現在のノードが開始タグまたは空要素タグであるかどうかを調べます。
ReadStartElement メソッド 現在のノードが要素であるか調べ、リーダーを次のノードに進めます (IsStartElement、その後に Read)。
ReadEndElement メソッド 現在のノードが終了タグであるか調べ、リーダーを次のノードに進めます。
ReadElementString メソッド テキストのみの要素を読み取ります。
ReadToDescendant メソッド XML リーダーを指定された名前を持つ次の子孫 (子) 要素に進めます。
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("<{0}/>", 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 メソッド 属性値を解析して、1 つ以上の TextEntityReferenceEndEntity の各ノードに格納します。

一般の 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("  {0}", 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(" {0}={1}", 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 プロパティは、バージョン、スタンドアロン、およびエンコーディング情報を単一の文字列として返します。 Create メソッドにより作成された XmlReader オブジェクト、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 Schema Part 2: Datatypes』に定義された規則を使用します。

この XmlReader メソッドを使用する この CLR 型を返すには
ReadContentAsBoolean および ReadElementContentAsBoolean Boolean
ReadContentAsDateTime および ReadElementContentAsDateTime DateTime
ReadContentAsDouble および ReadElementContentAsDouble Double
ReadContentAsLong および ReadElementContentAsLong Int64
ReadContentAsInt および ReadElementContentAsInt Int32
ReadContentAsString および ReadElementContentAsString String
ReadContentAs および ReadElementContentAs returnType パラメーターで指定する型
ReadContentAsObject および ReadElementContentAsObject 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: {0}", 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 オブジェクトを使用して解決されます。 この点については、次のいずれかの方法でセキュリティを強化できます。

  • ProcessInlineSchema オブジェクトの ProcessSchemaLocation および XmlReaderSettings の検証フラグは、既定では設定されていません。 このことは、信頼できないソースからの XML データを処理する際に、スキーマ ベースの攻撃から XmlReader を保護するのに役立ちます。 これらのフラグが設定されていると、XmlResolver オブジェクトの XmlReaderSettingsXmlReader のインスタンス ドキュメント中に出現したスキーマの場所を解決するために使用されます。 XmlResolver プロパティが null に設定されている場合は、たとえ ProcessInlineSchema および ProcessSchemaLocation の検証フラグが設定されていても、スキーマの場所は解決されません。

    検証を実行しているときにスキーマを追加すると新しい型が追加されるため、検証しているドキュメントの検証結果を変えることができます。 結果として、信頼できるソースからの外部スキーマだけが解決されるようにする必要があります。

    高可用性が必要なシナリオで、信頼できない大きな XML ドキュメントの大きな部分を ID 制約のあるスキーマに対して検証する際には、ProcessIdentityConstraints フラグは無効にすることが推奨されます。 このフラグは既定で有効になっています。

  • XML データには、処理に長時間を要する大量の属性、名前空間宣言、入れ子になった要素などが含まれることがあります。 XmlReader に送信される入力のサイズを制限するには、次の操作を行うことができます。

    • MaxCharactersInDocument プロパティを設定して、ドキュメントのサイズを制限します。

    • MaxCharactersFromEntities プロパティを設定して、エンティティの展開により生成される文字数を制限します。

    • XmlReader のカスタム IStream の実装を作成します。

  • ReadValueChunk メソッドを使用して、大規模なデータ ストリームを処理できます。 このメソッドは、値全体に 1 つの文字列を割り当てる代わりに、一度に少数の文字を読みます。

  • 一意のローカル名、名前空間、またはプレフィックスを大量に含んだ XML ドキュメントを読み込むと、問題が発生する場合があります。 XmlReader から派生したクラスを使用している場合、LocalNamePrefix、または NamespaceURI のプロパティが項目ごとに呼び出され、それによって返された文字列が NameTable に追加されます。 NameTable が保持するコレクションのサイズは決して減ることがありません。その結果、文字列ハンドルの実質的なメモリ リークが発生する場合があります。 この問題に対する軽減策の 1 つとして、NameTable からの派生クラスを作成し、最大サイズのクォータを指定します。 (NameTable の使用を回避したり、サイズが上限に達したときに NameTable を切り替えたりする方法はありません)。 別の軽減策では、前述のプロパティを使用せずに、MoveToAttribute メソッドと IsStartElement メソッドを使用します。これらのメソッドでは、文字列が返されないため、NameTable コレクションがあふれてしまう問題を回避できます。

  • XmlReaderSettings オブジェクトはユーザー資格情報など、重要な情報を含むことがあります。 信頼できないコンポーネントが XmlReaderSettings オブジェクトとそのユーザー資格情報を使用して、XmlReader オブジェクトを作成し、データを読む可能性があります。 XmlReaderSettings オブジェクトをキャッシュしたり、XmlReaderSettings オブジェクトをあるコンポーネントから別のコンポーネントに渡したりする場合には注意してください。

  • 信頼できないソースからの NameTableXmlNamespaceManager、および XmlResolver オブジェクトなどのサポート コンポーネントは受け入れないようにします。