読者です 読者をやめる 読者になる 読者になる

XML Schema、DTD、RELAX NG SchemaをRELAX NG Compact Schemaに変換するためにやったこと

XSLT Emacs

前回エントリの続きです。

表題の通り、RNC形式のスキーマへの変換について掘り下げていきます。
実質は、ほぼXML SchemaからRELAX NG Schemaへの変換についての言及となっていますが。

DTD/RELAX NG Schema → RELAX NG Compact Schema

Trangを使っています。
変換前と変換後のファイルをきちんと検証したわけではありませんが、 ググると情報が結構出てくるのでスキーマ変換ではメジャーなツールなのではと思っています。
特に問題があるというような情報が見つからなかったということもあり、現在のところ、 DTDRELAX NG SchemaをRELAX NG Compact Schemaにする場合は、このツールに丸投げです。

XML SchemaRELAX NG Compact Schema

問題はXML Schemaです。
まず、上記のTrangはXML Schemaへの変換はできますが、XML Schemaからの変換ができないようでした。
で、他のツールを探したところ、以下のリンクが見つかりました。
http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.java.dev.msv%22

ですが、試してみると正しく変換されていないようでした。
私の使い方が悪いのかも知れませんが、情報があまりなく、どれも英語なのでよく理解できない・・・。
というわけで、他のツールを探すと、以下のURLにあるXSDtoRNG.xslが見つかりました。
http://code.google.com/p/xsdtorngconverter/

試してみると、ぱっと見た感じ上手くいってそうでした。
このツールは、XML SchemaRELAX NG Schemaに変換してくれます。
なので、XML Schemaはこのツールで一旦RELAX NG Schemaに変換後、 改めてRELAX NG Compact Schemaに変換することにしました。

よしよし、これで変換できるじゃん!って喜んだのですが、そう簡単にはいきませんでした。

要素に指定する識別子の扱いの違い

どのスキーマでも、型を定義して、それを別の場所で参照する、ということが可能です。

XML Schemaの場合、complexTypeなどにより定義(name属性が型名)し、 他の要素からはtype属性や、ref属性にその型名を指定することで参照可能になります。
name属性はNCNameであり、type属性やref属性はQnameです。
型名をスキーマ名前空間内でのみ一意になるようにしておけば、 参照する側は名前空間も含めた型名を指定することで目的の型が参照可能となる仕組みです。

対してRELAX NG Schemaの場合、defineにより定義(name属性が型名)し、refで参照(name属性が型名)します。
で、どちらもname属性はNCNameとなっており、名前空間を指定して型を参照できません。

つまり、XML Schemaでは複数の名前空間内に同じ型名の型が定義されていても、 それらを区別できますが、それをRELAX NG Schemaに変換すると、それらは区別できなくなってしまいます。

この問題により、恐らく、どんな対処をしても正しく変換できない場合があることになります。

上記XSLT(XSDtoRNG.xsl)は恐らく汎用目的で公開されているものなので、何も対処はありませんでしたが、 私の場合は、nXML-modeで動作するスキーマに変換できれば良かったので、以下のように変換するようにしました。

  1. 変換するファイルがXML Schemaの場合、ファイル内に定義された名前空間とプレフィックスを検索し、 そのファイル内に独自タグでその情報を追記する。
  2. XSLTでは、その独自タグの情報から、name属性やtype属性の値を"名前空間名.型名"で統一して変換する。

これにより、稀なケースを除いては正しく型が参照できるようになったと思います。

省略されたschemaLocation

XML Schemaのimport要素では通常schemaLocation属性で対象スキーマのファイルパスを指定するのですが、 スキーマコンバータが名前空間からパスを割り出せる場合は、省略可能となっています。
しかし、RELAX NG Schemaではhref属性で必ずパスを記述しなければなりません。

なので、変換するファイルがXML Schemaの場合には、そのファイルに以下の処理を実施しています。

  • 既に登録されているスキーマの中に、対象の名前空間を定義したものがあれば、その情報を追記する。
  • なければ、プロンプトを表示し、ユーザに対象スキーマのパスを入力してもらい、それを追記する。

abstractなelement要素

XML Schemaでは抽象的なelement要素が定義できます。
これは、そのelement要素のタグがXML内に記述できるわけではなく、 substitutionGroup属性を持つelement要素のタグが実際には使用可能であることを意味していますが、 上記XSLTには、これに対応する変換処理がなかったので追加しました。

単純型と複合型の判断

XML Schemaでは既存の型を拡張/制限した新たな型を定義する際、type属性やbase属性に 単純型と複合型、両方のタイプの型が指定できます。
RELAX NG Schemaではdataまたはref要素が、それに対応する役割ですが、配下に定義できる要素が違うため、 変換の際には、単純型/複合型の判断が必要になります。

が、上記XSLTではそれが大分大雑把だったので、もう少しちゃんと判断できるように以下の処理を加えました。

適切とはいえないと思われますが、私の知識では、これがいっぱいいっぱいでした。

未定義の型への参照

スキーマによっては、どこにも定義されていない型をtype属性などに指定している場合がありました。
スキーマの記述ミスなんじゃないかと思うのですが、公開されているスキーマで幾つかそういうことがあり、 よくわかりません。
使用するスキーマコンバータによっては問題にならないのかも知れません。
しかし、nXML-modeではrng-set-document-type-and-validate実行でエラーになってしまい、 そういうスキーマは使用できません。

仕方ないので、そのスキーマで参照されている型は全て空の型として最初に定義する処理を追加しました。

その他

XSLTは使ったことがなかったのですが、上記の経緯により、上記XSLTを見ながら初めていろいろ調べました。
そうしてみると、上記XSLTは大分変換が大雑把な所があり、上記以外にもエラーを回避するために いろいろ直しています。
恐らく、上記XSLTを作られた方は、XML SchemaRELAX NG Schemaの正確な変換が不能だとわかっていたので それなりな変換処理しか作らなかったのでしょう。

というわけで、私の加えた変更の全ては記しませんが、genrnc.elと同じディレクトリに同梱されている xsd2rng.xslがそのファイルです。

変更内容が詳しく知りたい方は、XSDtoRNG.xslとxsd2rng.xslをdiffしてみて下さい。