自作のXmlWriterをXslCompiledTransformにわたす
XmlWriterを自分で直接実装することはなくても、XmlTextWriterのサブクラスを作るのは、よくやりますよね。
あれ、やらない?変だなぁ。じゃぁ、みんな、こんなの作るときどうやってるんだろう。「セルの中で改行したいときには、コンテント(内容)中で普通に改行するんじゃなくて、 って書きなさい」っていう謎仕様なんですけど。あ、いや、上のリンク先には書いてありませんが。でも、そうなの。仕様なの。
単にLinq to XMLで書き出すだけなら何も問題ないんですけど、XSLTで処理した結果を自作XmlWriterで出力しようとしてハマったので、その回避方法を書いておきます。
自作したXmlWriterクラスのインスタンスを、いったん XmlWriter.Create(XmlWriter, XmlWriterSettings) にわたし、返ってきたインスタンスを XslCompiledTransform.Transform にわたす。これでOK。
ただし、注意点がひとつだけあって、それは、自作XmlWriterクラスのSettingsプロパティは、常にnullを返すようになっていなければいけない。まぁ、XmlWriterのSettingsのデフォルト実装は一律nullを返すので、そのままにしておけばいいだけなんですけど。
えー、なんちゅうか、ですね、これに気づくまで2ヶ月悩み続けた、という。XslCompiledTransformとXmlWriterの間には、公開されていない秘密のインターフェースがあって、一般人が使えるXmlWriterのサブクラスはそのインターフェースを使ってないので、XslCompiledTransformと組み合わせると激しく誤動作する(例えば、xsl:output で doctype-public 指定してるのに DOCTYPE 宣言が生成されないとか)というのはすぐに気づいたが、回避方法がわからなかったんですよ。まさか、XmlWriterSettingsに秘密の(internalの)プロパティがあって、そこにdoctype-publicが入っているので、XmlWriter側の責任でそのinternalのdoctype-publicを読み取って、XmlWriter.WriteDocTypeが呼ばれなくても正しいタイミング(つまり、文書ルート要素に対するWriteStartElementの直前)で勝手にDOCTYPE宣言を作らなければならない、なんていう仕様、これをなんとか実装しようとしていたんです。2ヶ月間。
でも、それはハズレの道だったんですね。
たぶん、マイクロソフトも、こんな仕様をクラスライブラリの外側で実装できるわけがないことはわかっていて、ちゃんと救済策を用意してくれていたわけです。それが、っていうか、そのためだけの目的で作られたと思われる XmlWriter.Create(XmlWriter, XmlWriterSettings) なわけです。
だったら、どうしてちゃんとドキュメントに書いておいてくれないんだ、せめてヒントだけでも、って思うわけですが、答えがわかってからドキュメント読んだら書いてありました。本当にヒントだけですが。
This method allows you add additional features to an underlying XmlWriter object.
訳:このメソッドにより、元のXmlWriterオブジェクトに追加機能を加えることができます。
この「additional features」っていうのが、XslCompiledTransformとXmlWriterとの間の非公開の様々なインターフェースの処理(上でかいたDOCTYPEの件以外にもたくさんある様子)ということを控えめに表現したものらしい。
うーん、なんちゅうかなぁ。


最近のコメント