ものがたり(旧)

atsushieno.hatenablog.com に続く

XSD Inference (2) 属性の推測

さて、一番最初に、XSD Inferenceは属性定義と要素定義からなる、という話を書いた。今日は、比較的簡単な内容である属性定義について書こう。処理フローとしては、ホントは要素定義が先に来るんですけどね。

対象

まず、推測の対象となる属性は限定される。xmlns:*は(スキーマ定義もされないし)推測の対象とはならない。同様に、xsi:*も対象外になる。ただし、xsi:nilだけは要素型をnillable="true"にする、という別の用途で使われるので注意。

定義の内容は簡単だ。名前と任意性(use: required/optional)、型。

xs:attribute要素の生成

名前は自明のものだけど、属性に名前空間が付いている場合は注意しなければならない。名前空間が付いている属性はグローバル属性だから、グローバル属性を定義した上で、そのグローバル属性をrefで参照するxs:attributeを生成しなければならない。これはその属性を含んでいる要素の名前空間とグローバル属性の名前空間が一緒でも必要だ。また、これに関連して、xml:*属性は、http://www.w3.org/2001/xml.xsd の内容がXmlSchemaSetに含まれる。これは推測されない。既定の内容がそのまま推測結果となる。

use = "required" ? "optional" ?

出現頻度は、XmlSchemaInferenceのOccurenceプロパティがRestrictedの場合にだけ問題になるのだけど(Relaxedの場合は常にoptional)、use="required"は、限られた場合にだけ成立しうる推測だ。

ある要素型eの属性使用定義aが、あるeのインスタンスxを推測するとき、属性aがあれば、それはrequiredになる。もし次のインスタンスyに無ければ、それはoptionalでなければならない。逆に、yで初めて出てきた属性bがあるとき、bも定義しなければならないのだが、このときbはoptionalでなければならない。そうしないとaが不正になる。つまり、属性を推測するときは、その要素定義が新しく推測されようとしているのか、既存のものを拡張しようとしているのかを、常に意識する必要がある(このfreshnessのチェック対象は要素定義であって、属性定義ではないことに注意)。

type="?"

最後に、属性のデータ型だが、属性はsimple typeのみなので、比較的楽に行える。まず、新しい属性定義の場合、その文字列値から推測すれば良いが、既存の属性定義と属性型の定義がある場合には、新しい属性の文字列値をそのデータ型で解析して問題がないかを調べなければならない。もしNGだったら、新しい値から推測されるデータ型との共通の基底データ型を見つけなければならない。ちなみにある文字列型からデータ型を推測するという話については、今後の回でまとめる予定。

complexType

ちなみに、属性を定義するためには、要素の型がcomplexTypeでなければならない。このための変換は必要に応じてなされるのだけど、要素定義も既に何らかの型情報を持っているかもしれないので、ここでは多少の調整が必要になる。

  • 型情報が無ければ(type属性もsimpleType/complexType子要素も無い場合)、単にcomplexTypeが生成される
  • 内容がsimpleTypeの場合、complexType/simpleContent/extensionが生成され、その子あるいはbase属性の値として、そのsimpleTypeが埋められる
  • 内容がcomplexTypeの場合は問題にならない

属性はすべてこのcomplexType、あるいはその内容モデルに含まれるextension/restrictionに追加される。

attributeGroup, anyAttribute

属性推測で邪魔になるのは、xs:attributeGroupやxs:anyAttributeといったコンポーネント。特にattributeGroupは、そのグループが他のどこで使われるか分からないという重大な問題がある。なので、グループに対する参照が出てきたら、エラーにしてしまう、というのが良いだろう*1。もしかしたらグループ参照も問題なく推測・解決できるのかもしれないけど、僕には確信が無い。wildcardもけっこうめんどくさいので、とりあえず無視しても良いだろう。

以上で属性推測に関するトピックは大体さらったと思う。次回はcontent typeの推測について書く予定。

*1:ちなみにMS.NETは、そもそもそんなものが来ることを予期していなかったようなInvalidCastExceptionで落ちる