ものがたり(旧)

atsushieno.hatenablog.com に続く

XSD Inference (1)概論

本当はポイントを絞って書こうと思ったのだけど、いきなり何の前提もなしに書き始めると意味不明になるので(もともとここに書いてることなんてrandom thoughtsだけど…)、最初に概論だけでも書いておこう。

追記: 書き始めてから気づいたけど、僕が書こうとしているのはコレの実装について。VS 2005 October CTPではXmlSchemaInferenceという名前になっている。ちなみにこの前身はMicrosoft.XSDInferenceというもので、gotdotnetでソースも含めて入手することが出来る。ソースはたったの2000行以下なので、そんなに高度なことをしているわけではない。

XmlSchemaInferenceのAPIは、XmlReaderと既存のXmlSchemaSetから、XmlSchemaSetを推測(生成)して返す。実際には、引数XmlSchemaSetと戻り値XmlSchemaSetは(僕が実験した限りでは)同じものだ。

スキーマ推測が始まる前に、引数XmlSchemaSetは(もしあれば)Compile()される。この目的は、GlobalElementsやGlobalAttributesを取得するため、でもあるが、むしろ重要なのは、不正なXmlSchemaを排斥することにある*1

次に、document elementの処理に入る。まず、XmlReaderのdocument elementのQNameに合致するglobal elementを探して、もし無ければ新しくグローバル要素を定義する。もし既存の定義があれば、現在のXmlReaderの要素と、その定義内容が矛盾しないように、要素型の拡張が行われる(これがXmlSchemaInferenceの主要な課題)。

新しい要素を定義するとき、その名前空間が重要な問題になる。新しいXmlSchemaElementは、そのコンテナとなるXmlSchemaのTargetNamespaceが、XmlReader.NamespaceURIに合致しなければならないのだ。そして、新しい要素型の名前は、Compile()しない限りQualifiedNameは空っぽなので、

ここで、新しく定義されたスキーマコンポーネントは、XmlSchemaSet.GlobalElementsなどのテーブルから取得できないことに注意しなければならない。GlobalElementsはpost schema compilation informationなのだけど、新しく定義された要素はコンパイルされていないので、この中に含まれていないのである。スキーマ推測するときは、使用するコンポーネントがpost compilation informationでないかどうかを、常に意識する必要がある。

ちなみに、外部の名前空間に属する子要素が無い限り、document element以外の要素がグローバル要素として定義されることはない(すべてcomplexTypeの内容として推測されてしまう)。外部の名前空間に属する要素はrefで参照することしか出来ないので、必然的にrefで参照される要素はグローバルに定義される必要がある。この設計の理由はよく分かっていない(確かに、同じ名前で全然違う内容の要素を混同することになるが、一方で異なる親を持つ要素の定義は全然別に作られてしまう。どっちが妥当だろうか?)

*1:実際にはこのやり方は賢くなくて、僕だったらXmlSchemaInferenceのスキーマはXmlReaderで渡させて、サブセットスキーマでvalidateして非サポートスキーマコンポーネントを拒絶するのだけど、あいにくこのクラスはそのようにはなっていない。そのせいでMS.NETにはあんなバグやこんなバグがあったりする。