XMLの外部リソース解決は読み込み時だけとは限らない

以前の日記で、DTD検証を行うXmlReaderの生成に関して言及した際に、外部のDTDリソースを解決するためにXmlReaderSettingsクラスのXmlResolverプロパティをセットするコード片を書いた。

//リゾルバの生成(ここではXmlUrlResolverクラスを使用)
XmlResolver resolver = new XmlUrlResolver();
//設定オブジェクトの生成
XmlReaderSettings settings = new XmlReaderSettings();
settings.XmlResolver = resolver;
settings.ProhibitDtd = false;
settings.ValidationType = ValidationType.DTD;

XmlResolverを利用したDTDリソース解決は、XMLの読み込み時だけに必要なものだと思っていたのだが、どうもそうでは無かったようだ。
というのも、XMLを出力するために、以下のようなXMLドキュメントと、その要素を生成するコードを書いたとする。

XmlDocument doc = new XmlDocument();
XmlDocumentType docType =doc.CreateDocumentType("hoge", null, "hoge.dtd", null);
doc.AppendChild(docType);
XmlElement root = doc.CreateElement("root");
doc.AppendChild(root);

このコードは、"hoge.dtd"という名前の、ローカルに配置されていることを前提とするDTDを必要とする、以下のようなXMLのための要素を生成するが、

<?xml version="1.0"?>
<!DOCTYPE hoge SYSTEM "hoge.dtd">
<root></root>

実際にサンプルを書いて動かしてみると、XMLでいうところのドキュメントタイプを出力する行

XmlDocumentType docType =doc.CreateDocumentType("hoge", null, "hoge.dtd", null);

を実行すると、"hoge.dtd"という名前のDTDリソースが解決できないと例外を投げるのである。(System.IO.FileNotFoundException)
従って、このコード片はXMLを読み込む時のようにXmlResolverを使用してDTDlリソースを解決するように、書き換える必要がある。

XmlDocument doc = new XmlDocument();
XmlResolver resolver = new XmlUrlResolver();
doc.XmlResolver = resolver;
XmlDocumentType docType =doc.CreateDocumentType("hoge", null, "hoge.dtd", null);
doc.AppendChild(docType);
XmlElement root = doc.CreateElement("root");
doc.AppendChild(root);

XmlResolverとして、ここでは.NET標準のXmlUrlResolverクラスを使用しているが、これはあくまで例である。今回の例のようにローカルのファイルシステムに配置されている(相対パスを含む)DTDリソースを解決するためには、XmlResolverの派生クラスを用意したほうが良い場合がある。