連載
.NETで簡単XML
第3回 XML文書を読み書きするプログラムの作成
株式会社ピーデー 川俣 晶
2003/04/08
|
 |
XmlValidatingReaderクラスとXmlTextReaderクラス
XmlTextReaderクラスは便利なのだが、実はXML文書を処理するという観点からは問題がある。例えば、以下のサンプル・プログラムを見ていただきたい。Windowsアプリケーションのテンプレートでプロジェクト作成後に次のようなコードを書き加えたものである。
以下は追加するImportsステートメント(C#ではusingディレクティブ)である。
以下は書き加える文字列定数とフォームのLoadメソッドの内容である。
Private Const target As String = "<?xml version='1.0' ?>" _
& "<!DOCTYPE a [ <!ENTITY ent 'ok'> ]>" _
& "<a>&ent;</a>"
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim reader As XmlTextReader = New XmlTextReader(New StringReader(target))
While reader.Read()
Trace.WriteLine("ノードを発見: " + reader.NodeType.ToString())
If reader.NodeType = XmlNodeType.Text Then
Trace.WriteLine(reader.Value)
End If
If reader.NodeType = XmlNodeType.EntityReference Then
Trace.WriteLine(reader.Name)
End If
End While
End Sub
|
|
追加する文字列定数とフォームのLoadメソッドの内容(VB.NET版) |
追加する文字列定数とフォームのLoadメソッドの内容(C#版) |
これを実行すると、Visual Studio .NETの出力ウィンドウに以下のような内容が出力される。
ノードを発見: XmlDeclaration
ノードを発見: DocumentType
ノードを発見: Element
ノードを発見: EntityReference
ent
ノードを発見: EndElement
|
|
出力ウィンドウに表示される内容 |
プログラムの内容を簡単にいえば、文字列定数targetの内容をXML文書としてXmlTextReaderクラスで読み込む機能を記述している。ここでは、第1回目では説明していないXMLの機能である「実体(entity)」を使用している。これは一種のマクロであり、あらかじめ定義しておいた文字列と文書中の文字列を置き換える機能を持っている。つまり、「ent」という名前の実体は「ok」という文字列であると定義しておき、「&ent;」と記述したら、それは「ok」という文字列に置き換えて利用するというものである。しかし、出力を見ると分かる通り、「EntityReference」つまり実体を参照するノードを発見しただけで、「ok」という文字列は発見できていない。これでは使い勝手がよくない。
これを解決するために、XmlTextReaderクラスとは別に用意されているXmlValidatingReaderクラスを使用することができる。XmlValidatingReaderクラスは、XmlTextReaderクラスと同じくXmlReaderクラスを継承している。そのため、XmlValidatingReaderクラスとXmlTextReaderクラスは多くのメソッドやプロパティが共通していて、同じように使用できる。しかし、同じような目的のクラスというわけではなく、XmlValidatingReaderクラスには、スキーマを用いて文書の妥当性を検証するという明確な役割が存在する。つまり、妥当性を検証しながらXML文書を読み取るのが主要な用途であるといえる。では逆に、妥当性を検証しないでXML文書を読み取るのがXmlTextReaderクラスの役割なのかというと、そうでもない。XML1.0勧告では、妥当性を検証しない場合でも、内部実体を展開することを要求している。これを満たすには、XmlValidatingReaderクラスを妥当性検証抜きのモードで使う必要がある。
具体的にこれを実現するには、以下のようにソース・コードを書き換える。以下は、フォームのLoadイベントの内容を書き換えたものである。
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim reader As XmlValidatingReader = New XmlValidatingReader( _
New XmlTextReader(New StringReader(target)))
reader.ValidationType = ValidationType.None
While reader.Read()
Trace.WriteLine("ノードを発見: " + reader.NodeType.ToString())
If reader.NodeType = XmlNodeType.Text Then
Trace.WriteLine(reader.Value)
End If
If reader.NodeType = XmlNodeType.EntityReference Then
Trace.WriteLine(reader.Name)
End If
End While
End Sub
|
|
XmlValidatingReaderクラスを使用するサンプル(VB.NET版) |
XmlValidatingReaderクラスを使用するサンプル(C#版) |
これを実行すると以下のような結果になる。
ノードを発見: XmlDeclaration
ノードを発見: DocumentType
ノードを発見: Element
ノードを発見: Text
ok
ノードを発見: EndElement
|
|
出力ウィンドウに表示される内容 |
このように、「EntityReference」つまり実体を参照するノードを発見することはなくなり、その代わりに「Text」つまりテキストを参照するノードを発見している。その内容も、実体を展開した内容となっている。
なお、.NET Frameworkのドキュメントによると、OASIS(Organization for the Advancement of Structured Information Standards)が提供するXMLの適合テストにパスする形で利用するには、XmlTextReaderおよびXmlValidatingReaderの設定を、次の例外を除いて既定(デフォルト)の設定にする必要があるとしている。
- XmlTextReaderでNormalizationプロパティをtrueに設定する(既定値はfalse)。
- XmlValidatingReaderでValidationCallbackハンドラを割り当る(既定では例外をスローする)。
- XmlValidatingReaderのValidationTypeプロパティをValidationType.DTDに設定する(既定はValidationType Auto)。
完全にXML1.0勧告を満たす形でXmlReaderを使うには、以上のような情報にも注意を払う必要がある。
Insider.NET 記事ランキング
本日
月間