XmlLite
ネイティブ C++ 用の小さくて高速な XML パーサー
Kenny Kerr
この記事の内容 : :
- XmlLite と他の利用可能な XML パーサーとの比較
- XmlLite の利点と制限
- XML の読み取りと書き込み
- XML セキュリティの考慮事項
|
この記事は次のテクノロジを使用しています:
XML、C++
|

コンテンツ
.NET Framework の成功にもかかわらず、マイクロソフトは引き続きネイティブ C++ の開発に真剣に取り組んでいます。これについては、ネイティブ C++ で書かれたアプリケーションを対象とする、高パフォーマンスでオーバーヘッドの少ない XML リーダーおよびライタを備えている XmlLite の紹介で説明されています。
マネージ コードでは、System.Xml 名前空間を通じて XML を広くサポートしています。また、COM に依存している従来の Visual Basic® および C++ アプリケーションでは、Microsoft® XML コア サービス (MSXML) から類似機能にアクセスできます。ただし、これは高速で無駄のない XML パーサーを必要とするネイティブ C++ 開発者にとって、あまり魅力的なオプションではありません。そのような場合は、XmlLite を使用します。
この記事では、XmlLite を使用して実行できる操作について説明します。最初に、明確にするために、XmlLite の初期リリースではまだ提供していない機能について簡単に説明します。まず、Document Object Model (DOM) 実装、XML スキーマ検証、または文書型定義 (DTD) 検証は提供していません。カーソル ベースのナビゲーション (XPath など)、スタイル シート、シリアル化などの高度な機能もサポートしていません。ただし必要に応じて、Microsoft.NET Framework のほとんどの XML 機能が XmlReader および XmlWriter クラスの上位に構築されているように、XmlLite の上位に構築された機能によってそのギャップを埋めることができます。
それでは、XmlLite が提供する機能とはどのようなものでしょうか。簡単に言うと、キャッシュのない転送専用のパーサー (プル プログラミング モデルを提供) およびキャッシュのない転送専用の XML ジェネレータです。どちらも有益な機能であることが証明されています。
新しい XML パーサーが開発された理由
毎日使用するライブラリに慣れ、XML を広範に使用している開発者が、最新の XML パーサーに関して難しい質問をしてくるのも当然です。新しいパーサーの価値を把握するには、まず今日の XML パーサーの展望について考えてみましょう。
言うまでもなく、アプリケーションで既に .NET Framework が使用されている場合は、選択が非常に簡単です。そのまま System.Xml を使用します。この裏付けとして、XmlLite は、.NET Framework の XmlReader クラスおよび XmlWriter クラスの設計を基にして設計されています。C++ で書かれたマネージ アプリケーションから XmlLite を使用することに通常メリットはありません。XmlLite の機能は、XmlReader クラスおよび XmlWriter クラスが提供する機能よりもずっと軽量になっています (図 1 の表は、XmlLite の主なタイプが .NET Framework のどのタイプに対応するかを示しています)。これに対して、アプリケーションが排他的にネイティブ コードを利用する場合、マイクロソフトのテクノロジに関する限り、これまで MSXML が一般的なソリューションでした。

Figure 1 XmlLite のクラスと .NET Framework の対応
| XmlLite |
.NET Framework |
| IXmlReader インターフェイス |
XmlReader クラス |
| IXmlWriter インターフェイス |
XmlWriter クラス |
| XmlReaderProperty 列挙型 |
XmlReaderSettings クラス |
| XmlWriterProperty 列挙型 |
XmlWriterSettings クラス |
CreateXmlReaderInputWithEncodingCodePage CreateXmlReaderInputWithEncodingName CreateXmlWriterOutputWithEncodingCodePage CreateXmlWriterOutputWithEncodingName |
エンコーディング クラス |
| XmlNodeType 列挙型 |
XmlNodeType 列挙型 |
MSXML は、まったく異なる 2 つの XML パーサーを提供します。1 つ目は、さまざまな形態で使用できる DOM 実装です。比較的小さな XML ドキュメントで作業しており、メモリ内で読み取りと書き込みを行うために XML ドキュメントへのランダム アクセスが必要な場合は、DOM 実装が適しています。より新しいバージョンの MSXML では、Simple API for XML (SAX2) の実装が導入されています。ただし、それが実際に "簡単" であるかには異論があります。SAX2 を使用する場合は、実行に移す前に、少なくとも 2 つの COM インターフェイスを実装する必要があります。1 つは XML ドキュメントのさまざまなノードの通知を受け取るインターフェイスで、もう 1 つはパーサー エラーの通知を受け取るインターフェイスです。
SAX2 実装が MSXML に追加された理由は、DOM 実装とは異なり、SAX2 パーサーによって XML ドキュメントがストリームとして読み取られ、各種のノードに到達したときに通知されるためです。つまり、アプリケーションのメモリ消費量が、解析中のドキュメントのサイズによって変わるわけではありません。
SAX2 に関する問題と、.NET Framework で SAX2 の実装が提供されていない理由は、SAX2 モデル固有の複雑さにあります。SAX2 ではインターフェイスまたはイベントを実装する必要があり、開発者はより間接的なプログラミング モデルを採用せざるを得ないため、アプリケーションが必然的に複雑になるという別の状況に対処する必要があります。その代わり、.NET Framework の XmlReader クラスと XmlWriter クラス、および XmlLite のIXmlReader インターフェイスと IXmlWriter インターフェイスは、外部の状態または通知を管理する必要のない関数内で直接使用できる簡単なパーサーを提供します。
MSXML SAX2 実装と比較した場合、XmlLite は設計が単純であるため、パフォーマンスを大幅に向上させることができます。SAX2 パーサーが DOM 実装よりも大きなドキュメントの処理を得意としていますが、XmlLite と比べると劣ります。
簡潔に言うと、XmlLite は MSXML より性能が優れており、ネイティブ C++ から非常に使いやすくなっています。現在でも MSXML は、Visual Basic および COM ベースのスクリプト言語にとって最も有効なソリューションですが、ネイティブ Visual C++® には、専用に設計された XML パーサーが追加されました。XmlLite は Windows Vista™ 以降のバージョンに付属していますが、Windows® XP および Windows Server® 2003 の 32 ビットおよび 64 ビット バージョンでもアップデートを利用できます。このアップデート パッケージには COM 登録が含まれていないので、インストールおよびバージョン管理に関する MSXML の問題は発生しません。
COM "Lite"
XmlLite は人気を集めそうな名前として付けられたのではなく、実際には軽量な XML パーサーという意味から付けられました。XmlLite では、COM の最も優れた点 (プログラミング規範や規則など) を利用し、COM 登録、ランタイム サービス、プロキシ、スレッド モデル、マーシャリングなどの複雑で不必要になる可能性のある部分は削除されています。
XmlLite.dll からエクスポートされた関数は、XML リーダーとライタを作成します。これらにアクセスするには、XmlLite.lib にリンクし、Windows SDK から XmlLite.h ヘッダー ファイルをインクルードします。結果として得られる COM 型インターフェイスでは、使い慣れた IUnknown インターフェイス メソッドが有効期間管理に使用されます。COM IStream インターフェイスにも役割があり、ストレージを表します。この点を除けば、COM に対する依存関係がないので、COM クラスを登録する必要も、必須の CoInitialize 関数を呼び出す必要もありません。Active Template Library (ATL) の CComPtr クラスが、COM の残りの処理を行います。ただし、XmlLite はシングル スレッド シナリオでのパフォーマンスを維持するために、スレッドセーフにはなっていないので、スレッドの安全性は自分で対処する必要があります。
次のサンプルでは、COM_VERIFY マクロを使用して、チェックする必要のある HRESULT をメソッドが返す場所を明確に特定します。これは、例外をスローするか、または HRESULT を返す適切なエラー処理に置き換えることができます。
XML を読み取る
XmlLite には、IXmlReader インターフェイスの実装を返す CreateXmlReader 関数が用意されています。
CComPtr<IXmlReader> reader;
COM_VERIFY(::CreateXmlReader(__uuidof(IXmlReader),
reinterpret_cast<void**>(&reader),
0));
オプションですが、CComPtr クラス テンプレートを使用すると、インターフェイス ポインタが直ちに解放されるようになります。
CreateXmlReader は、ボイド ポインタに対するポインタだけでなく、インターフェイス識別子 (IID) を受け取ります。これは、呼び出し元でインターフェイス ポインタのタイプを指定できるようにする COM プログラミングの共通パターンです。ここで示す例では、タイプに関連付けられている GUID を抽出するマイクロソフト固有のキーワードである __uuidof 演算子を使用し、インターフェイスの IID を取得します。CreateXmlReader の最終引数は、呼び出し元でメモリ割り当てを制御できるようにするため、オプションの IMalloc 実装を受け取ります。
リーダーを作成したら、リーダーが入力として使用するストレージを指定する必要があります。ストレージを表す IStream インターフェイスを使用すると、ストリーム実装で XmlLite を使用できます。
CComPtr<IStream> stream;
// Create stream object here...
COM_VERIFY(reader->SetInput(stream));
(ストリームについては、この記事の後半で説明します。)
XML リーダーの入力を設定したら、Read メソッドを繰り返し呼び出して読み取りを行うことができます。Read メソッドは、呼び出しが成功するたびにノード タイプを返すオプションの引数を受け取ります。Read メソッドは、次のノードがストリームからの読み取りに成功すると S_OK を返し、ストリームの終わりに達すると S_FALSE を返します。以下は、ノードの列挙方法の例です。
HRESULT result = S_OK;
XmlNodeType nodeType = XmlNodeType_None;
while (S_OK == (result = reader->Read(&nodeType)))
{
// Get node-specific info
}
現在のノードの属性を列挙するには、MoveToFirstAttribute メソッドおよび MoveToNextAttribute メソッドを使用します。両方のメソッドは、リーダーの位置が正常に変更された場合は S_OK を返し、これ以上属性が存在しない場合は S_FALSE を返します。次の例では、特定のノードの属性を列挙する方法を示します。
for (HRESULT result = reader->MoveToFirstAttribute();
S_OK == result;
result = reader->MoveToNextAttribute())
{
// Get attribute-specific info
}
IXmlReader の Read メソッドを呼び出すと、ノードの属性が内部コレクション内に自動的に保存されます。これにより、MoveToAttributeByName メソッドで名前を指定して、リーダーを特定の属性に移動できるようになります。ただし、属性の列挙および保存は、アプリケーション固有のデータ構造で行った方が効果的です。GetAttributeCount メソッドを使用して現在のノードの属性の数を調べることもできます。
ノードまたは属性が決まったら、情報の取得は簡単です。ここでは、特定のノードの名前空間 URI およびローカル名を取得する方法を示します。
PCWSTR namespaceUri = 0;
UINT namespaceUriLength = 0;
COM_VERIFY(reader->GetNamespaceUri(&namespaceUri,
&namespaceUriLength));
PCWSTR localName = 0;
UINT localNameLength = 0;
COM_VERIFY(reader->GetLocalName(&localName,
&localNameLength));
文字列値を返す IXmlReader メソッドはすべてこのパターンに従います。最初の引数は、ワイド文字ポインタ定数へのポインタを受け取ります。2 番目の引数はオプションです。0 に設定されていない場合は、Null ターミネータを除く文字単位で測定される文字列の長さを返します。
以下にパフォーマンスに重点を置いたもう 1 つの例を示します。IXmlReader メソッドから返された文字列ポインタが有効なのは、リーダーを別のノードに移動するまで、または新しい入力ストリームの設定や IXmlReader インターフェイスの解放などの方法で現在のノードを無効するまでに限られます。つまり、IXmlReader はストリームのコピーを呼び出し元に返しません。
.NET Framework のメソッドとは異なり、IXmlReader では、入力されたコンテンツを読み取るためのメソッドが提供されていません。たとえば、特定の要素または属性に数字または日付が含まれている場合、最初に文字列表記を取得し、必要に応じてそれを変換する必要があります。.NET Framework の XmlReader クラスに存在する他のヘルパ メソッドの多くは、IXmlReader には存在しませんが、ヘルパ関数として書き込むことができます。XmlLite は、最小限のインターフェイスである C++ の設計思想に厳密に準拠しています。
図 2 は、IXmlReader を使用した XML ドキュメントの読み取りに関連するオブジェクトおよび抽象化を示しています。IStream はどのようなストレージでも抽象化することができます。また、ここで示したファイルはあくまで一般的な例ですのでご了承ください。
図 2 リーダー
XML を書き込む
XmlLite には、IXmlWriter インターフェイスの実装を返す CreateXmlWriter 関数が用意されています。
CComPtr<IXmlWriter> writer;
COM_VERIFY(::CreateXmlWriter(__uuidof(IXmlWriter),
reinterpret_cast<void**>(&writer),
0));
ライタを作成したら、ライタが入力として使用するストレージを指定する必要があります。
CComPtr<IStream> stream;
// Create stream object here
COM_VERIFY(writer->SetOutput(stream));
記述を始める前に、ライタ プロパティを変更できます。XmlWriterProperty 列挙型では、使用可能なプロパティを定義します。たとえば、XML 出力を人間が読み取れるようにインデントするかどうかを指定するとします。これは、SetProperty メソッドで指定できます。
COM_VERIFY(writer->SetProperty(XmlWriterProperty_Indent, TRUE));
IXmlWriter メソッドを使用して、基になるストリームへの書き込みを開始できます。XmlLite は XML の断片をサポートします。完全な XML ドキュメントの書き込みを行う場合は、まず XML 宣言を記述する WriteStartDocument メソッドを呼び出します。宣言は使用中のエンコードに依存しますが、既定では UTF-8 です。これはほとんどの場合に適しています (テキスト エンコードについてはこの後すぐに取り上げます)。さまざまなノードタイプ、属性、および値を書き込むための数多くの WriteXxx メソッドが用意されています。
次の例について考えてみましょう。
COM_VERIFY(writer->WriteStartDocument(XmlStandalone_Omit));
COM_VERIFY(writer->WriteStartElement(0, L"html",
L"http://www.w3.org/1999/xhtml"));
COM_VERIFY(writer->WriteStartElement(0, L"head", 0));
COM_VERIFY(writer->WriteElementString(0, L"title", 0, L"My Web Page"));
COM_VERIFY(writer->WriteEndElement()); // </head>
COM_VERIFY(writer->WriteStartElement(0, L"body", 0));
COM_VERIFY(writer->WriteElementString(0, L"p", 0, L"Hello world!"));
COM_VERIFY(writer->WriteEndDocument());
WriteStartDocument メソッドは、ストリームへの XML 宣言の書き込みを処理します。1 つの引数は、スタンドアロン ドキュメント宣言を表示するかどうか、表示する場合はどの値を保持するかを示す XmlStandalone 列挙型の値を受け取ります。XML の断片を書き込む場合、一般的に WriteStartDocument の呼び出しは省略します。
WriteStartElement メソッドは 3 つの引数を受け取ります。最初の引数は、要素の名前空間プレフィックス (オプション) を指定し、2 番目の引数は要素のローカル名を指定し、3 番目の引数は名前空間 URI (オプション) を指定します。WriteElementString は、XmlLite が提供する非常に便利なメソッドの 1 つです。XHTML ドキュメントのタイトルを記述する次のコードは、上記の例で使用した WriteElementString に相当します。
COM_VERIFY(writer->WriteStartElement(0, L"title", 0));
COM_VERIFY(writer->WriteString(L"My Web Page"));
COM_VERIFY(writer->WriteEndElement());
WriteElementString メソッドは厳密には必要ありませんが、非常に便利なメソッドです
最後に、WriteEndDocument メソッドはドキュメントを閉じます。body 要素と html 要素が明示的に閉じられなかったことに気付いたかもしれません。WriteEndDocument は、開いている要素を自動的に閉じます。また、ライタを解放すると、残りの要素も閉じられます。ただし、明示的に閉じられていない要素がある場合、注意しないとバグが発生する可能性があります。それは、ストリームの有効期間とライタの有効期間にしばしば違いがあるからです。すべてのライタが基になるストリームに書き込まれていることを確認する必要がある場合は、IXmlWriter の Flush メソッドを呼び出します。
図 3 は、IXmlWriter を使用した XML ドキュメントの書き込みに関連するオブジェクトおよび抽象化のフローを示しています。IStream はどのようなストレージでも抽象化することができます。また、ここで示したファイルはあくまで一般的な例ですのでご了承ください。
図 3 ライタ
ストリームを操作する
ここまでは、ストリームについて詳しく説明していませんでした。フル機能の XML ライブラリとは異なり、XmlLite では、一般的な保存場所 (ファイル、ネットワーク プロトコル上など) に対する読み取りおよび書き込みの機能がサポートされていません。そのため、読み取りまたは書き込みを行うストレージに IStream 実装を行う必要があります。IStream インターフェイスの実装は複雑ではありませんが、多くの場合、実装が既に存在する可能性があるため、実装の必要はありません。
CreateStreamOnHGlobal 関数は、仮想メモリによって支援される IStream 実装を提供します。最初の引数は、GlobalAlloc 関数を使用して作成されたオプションのメモリ ハンドルです。0 を渡すだけで、CreateStreamOnHGlobal はメモリ オブジェクトを作成します。次の例では、システム メモリによって支援され、必要に応じて動的に成長する IStream 実装を作成します。
CComPtr<IStream> stream;
COM_VERIFY(::CreateStreamOnHGlobal(0, TRUE, &stream));
ストリームを解放すると、メモリが解放されます。
SHCreateStreamOnFile 関数は、他にも役に立つ IStream 実装を提供し、ファイルによって支援される IStream を作成します。
CComPtr<IStream> stream;
COM_VERIFY(::SHCreateStreamOnFile(L"D:\\Sample.xml",
STGM_WRITE | STGM_SHARE_DENY_WRITE,
&stream));
読み取り時のテキスト エンコード
XmlLite の既定では書き込みに UTF-8 を使用しますが、読み取り時にテキスト エンコードの検出を試みるとき、この動作をオーバーライドすることができます。まず、自動的に取得できるものを見てみましょう。ストリームを指定すると、IXmlReader は XML の先頭にあるバイト オーダー マークを使用してエンコードのヒントを検出します。また IXmlReader は、XML 宣言で指定されたエンコードを行います。この両方の特徴は XML パーサーにもあります。エンコード情報が定義されていない入力ストリームがあり、XmlLite が使用中のエンコードを経験則的に特定できない場合は、コード ページまたはエンコード名を指定して、IXmlReader を特定のエンコードに設定できます。
ストリームを直接 IXmlReader に渡す代わりに、IXmlReaderInput インターフェイスを装って XML リーダーの入力オブジェクトを作成することができます。入力オブジェクトを作成し、入力ストリームをラップするために 2 つの関数が提供されています。CreateXmlReaderInputWithEncodingCodePage 関数は、コードページ番号形式でエンコードを受け入れます。CreateXmlReaderInputWithEncodingName 関数は、標準的な名前でエンコードを受け入れます。また、関数には同一の署名があります。要約として、XML リーダーの入力ストリームの設定方法を以下に示します。
CComPtr<IStream> stream;
// Create stream object here
COM_VERIFY(reader->SetInput(stream));
エンコードをオーバーライドするには、コードを次のように変更します。
CComPtr<IStream> stream;
// Create stream object here
CComPtr<IXmlReaderInput> input;
COM_VERIFY(::CreateXmlReaderInputWithEncodingName(stream,
0, // default allocator
L"ISO-8859-8",
TRUE, // hint
0, // base URI
&input));
COM_VERIFY(reader->SetInput(input));
最初の引数は、XML リーダーが読み取るストリームを示します。2 番目の引数は、オプションの IMalloc 実装を受け取ります。提供されている場合は、XML リーダーの実装をオーバーライドします。3 番目の引数は、エンコード名を指定します。ドキュメント
msdn2.microsoft.com/ms752827.aspx には、最初から用意されているエンコードの一覧が記載されています。他のエンコードをサポートするために、IMultiLanguage2 インターフェイスの実装を提供できます。次の引数は、指定したエンコードを使用する必要があるか、またはそれがあくまでヒントなのかどうかを示します。TRUE を指定すると、パーサーは推奨されているエンコードの使用を試みますが、それが正常に行われない場合は、自由に実際のエンコードを決めることができます。FALSE を指定すると、パーサーは推奨されているエンコードを試みますが、入力ストリームと一致しないとエラーを返します。次の引数は、外部エンティティの解決に使用されるベース URI を受け取ります。最後の引数は、SetInput メソッドに渡すために入力オブジェクトを示すインターフェイス ポインタを返します。
書き込み時のテキスト エンコード
XML ライタは、SetOutput メソッドに渡されるオブジェクトに基づいて、使用するエンコードを決定します。オブジェクトが IStream インターフェイス、または制限付き ISequentialStream インターフェイスを実装する場合、XML ライタは UTF-8 エンコードを使用します。この動作をオーバーライドするには、XML ライタ出力オブジェクトを作成します。出力オブジェクトを作成し、出力ストリームをラップするために 2 つの関数が提供されています。CreateXmlWriterOutputWithEncodingCodePage 関数は、コードページ番号形式でエンコードを受け入れ、CreateXmlWriterOutputWithEncodingName 関数は、標準的な名前でエンコードを受け入れます。また、関数には同一の署名があります。通常、XML ライタの出力ストリームの設定は次のようになります。
CComPtr<IStream> stream;
// Create stream object here
COM_VERIFY(writer->SetOutput(stream));
既定のエンコードをオーバーライドするには、次のコードを書き込みます。
CComPtr<IStream> stream;
// Create stream object here
CComPtr<IXmlWriterOutput> output;
COM_VERIFY(::CreateXmlWriterOutputWithEncodingName(stream,
0,
L"ISO-8859-8",
&output));
COM_VERIFY(writer->SetOutput(output));
最初の引数は、XML ライタが書き込むストリームを示します。2 番目の引数は、オプションの IMalloc 実装を受け取ります。提供されている場合は、XML ライタの実装をオーバーライドします。3 番目の引数は、エンコード名を指定します。最後の引数は、SetOutput メソッドに渡すために出力オブジェクトを示すインターフェイス ポインタを返します。
大きなデータ値を取り扱う
大きなデータ値を読み取る際にメモリ使用量を制限するために、XML リーダーでは値をブロックに分割して読み取るメカニズムが提供されています。IXmlReader ReadValueChunk メソッドは、一連の最大文字数を読み取り、2 回目以降の呼び出しを予測してリーダーを先へ移動させます。この例では、大きなデータ値を読み取るために ReadValueChunk を繰り返し呼び出す方法を示します。
CString value;
WCHAR chunk[256] = { 0 };
HRESULT result = S_OK;
UINT charsRead = 0;
while (S_OK == (result = reader->ReadValueChunk(chunk,
countof(chunk),
&charsRead)))
{
value.Append(chunk, charsRead);
}
データがなくなると、ReadValueChunk は S_FALSE を返します。この例では、CString オブジェクトにチャンクを書き込みます。これは、チャンクの長さを管理する方法と、その長さによってチャンクの利点がどのように損なわれるかを示すことのみを目的としています。
セキュリティの考慮事項
XML 指向のアプリケーションでは、信頼されていないソースの XML を必ず取り扱う必要があります。XmlLite は、把握しているまたは今後起こりうる脆弱性からアプリケーションを保護するための多数の機能を備えています。
XML ドキュメントには、外部エンティティの参照を含めることができます。これらの参照を自動的に解決する XML パーサーもあります。場合によっては役に立ちますが、XML リゾルバがさまざまな脅威を緩和するように慎重に書き込まれない場合は、このアプローチはセキュリティを脅かす手段になってしまいます。XmlLite は外部エンティティを自動的に解決しません。また、XML リゾルバも提供しません。必要に応じて独自の実装を用意するには、IXmlResolver インターフェイスを実装し、IXmlReader SetProperty メソッドで XmlReaderProperty_XmlResolver プロパティを使用し、リーダーにリゾルバを使用するように指示します。
XML ドキュメントには、DTD の処理命令を含めることもできます。XmlLite は、XML スキーマまたは DTD を使用したドキュメント検証はサポートしませんが、DTD エンティティ拡張および既定の属性はサポートします。DTD には外部エンティティの参照を含めることができるため、アプリケーションがさまざまな攻撃にさらされてしまう可能性があります。XmlLite の既定では、DTD 処理は無効になっています。XmlReaderProperty_DtdProcessing プロパティを DtdProcessing_Parse 値に設定することにより、DTD 処理を行うことができます。XmlReaderProperty_MaxEntityExpansion で制御されている DTD エンティティ拡張攻撃 (Billion Laughs Attack としても知られる) に対する組み込みの緩和策もあります。このプロパティの既定値は 100,000 です。
攻撃者が XML を使用してアプリケーションを攻撃できるもう 1 つの方法は、非常に長い名前を付けてドキュメントを作成することです。攻撃を防げない場合は、大量のメモリを消費し、サービス拒否攻撃を引き起こします。対応策については既にヒントを与えていました。このような脅威を緩和する明確な対策方法の 1 つは、前のセクションで説明したように、大きなデータ値をブロックに分割して読み取ることです。また、カスタム IMalloc 実装にメモリ割り当ての制限を与えることも便利な方法です。ランダム アクセスをサポートする入力ストリームを指定し、XmlReaderProperty_RandomAccess プロパティを使用して属性のキャッシングを避けるように XML リーダーに指示することもできます。これは、開始要素タグの読み取りに使用するメモリ量を減少させますが、パーサーが要求に従いさまざまな属性値を取得するために何度も検索を繰り返すので、解析の速度を低下させる可能性があります。
必要以上に深い XML 階層もシステム リソースをすぐに消費します。攻撃者による必要以上に深い階層を持つ XML ドキュメントの提供を防ぐには、XmlReaderProperty_MaxElementDepth プロパティを使用して、パーサーが許可する階層の深さを制限します。このプロパティの既定値は 256 です。
まとめ
XmlLite は、ネイティブ C++ アプリケーション用の強力な XML パーサーを備えています。パフォーマンスを強調し、使用するシステムリソースを認識します。またさまざまな特徴を制御する柔軟性に富んでいます。一般的なテキスト エンコードをすべてサポートしている XmlLite は、ネイティブ C++ アプリケーションでの XML の使用を簡素化する非常に役に立つ実用的なツールです。詳細については、
msdn2.microsoft.com/ms752872.aspx (英語) にある XmlLite ドキュメントを参照してください。