XMLフロンティア探訪
第11回 実は新構文になっているRELAX NG

日本発のXMLスキーマ言語として登場したRELAX。昨年秋には、その後継となるRELAX NGがOASISから発表されたが、実はRELAXとRELAX NGの構文は大きく異なっている。その背景と、新しく代わったRELAX NG構文について、お届けする。(編集局)

川俣 晶
株式会社ピーデー
2002/4/9

今回の主な内容
RELAXとRELAX NGは違う
村田氏とクラーク氏
RELAX NGで書く最も簡単なスキーマ
例1:要素の内容に文字列を許す
例2:要素に属性を付ける
例3:要素に子要素を持たせる
例4:指定した順番での要素の出現

例5:任意の内容を選択するには 例6:内容の任意回数の繰り返しを許す
例7:内容の有無の任意選択
例8:内容が揃っていれば順番は問わない
例9:要素の混合
次回はRELAX NGの機能をさらに深く紹介

RELAXとRELAX NGは違う

 XMLのスキーマ言語がいくつか存在することは皆さんご存じだろう。XML 1.0の勧告に含まれるDTDも一種のスキーマ言語といえるし、W3CはXML Schemaという言語を勧告している。また、日本でRELAXと呼ばれるスキーマ言語が作られていることをご存じの方も多いと思う。そして、現在は、RELAX NG(リラクシング)と呼ばれる言語が、OASISで標準化されている。

編集注:RELAXとRELAX NGについては、@ITの下記の記事が参考になる。

 ・日本発のXML標準になるか、 RELAXが目指しているもの
 ・XMLフロンティア(1)スキーマ戦争最前線
 ・W3Cスキーマに対抗するXMLの新仕様「RELAX NG」

 ここで問題なのは、RELAXとRELAX NGは名前がよく似ており、RELAX NGがRELAXの後継仕様に当たるといえるにもかかわらず、構文がまったく異なっていることだ。つまり、RELAXを知っていたとしても、その知識ではRELAX NGのスキーマを読み書きすることはできない。RELAX NGを使うためには新しい構文を学ばねばならないのだ。その事実に気付いていない方も多いようなので、今回はRELAX NGの構文の概要を紹介したいと思う。

村田氏とクラーク氏

 RELAX NGの技術的な解説に入る前に、RELAX NGに至る経緯を説明しておこう。もともと、XMLのスキーマ言語としてはDTDが存在したわけだが、名前空間に対応しないなど、いくつかの問題があるため、新しいスキーマ言語が必要とされていた。それに対応するためにW3CはXML Schemaを開発したが、これには多くの問題点が存在することが知られている。しかし、改善の方向に向かう見込みがないどころか、権威の力でXML Schemaを押し付けようとする状況もあり、多くの関係者の心が、XML Schemaから(あるいはW3Cから)離れていった。

 そのうちの1人が、W3C XMLワーキンググループの唯一の日本人メンバーであった村田真氏である。村田氏は、XML Schemaに代わるより良いスキーマ言語としてRELAX(より正確にはRELAX CORE)を作成した。そしてもう1人、同じようにW3Cから離れていったのがXML界の天才、ジェームズ・クラーク(James Clark)氏である。クラーク氏もXML Schemaに代わるより良いスキーマ言語としてTREXを作成した。しかし、ほぼ同じ機能を持つ異なるスキーマ言語が複数あることはXML Schema陣営を利するだけ、ということで両者は一致し、共同でRELAX NGという言語を作成することになった。その際、名前はRELAXという言葉を継承してRELAX NGとし、具体的な構文はTREXのものを発展させて使用することになった。その結果、名前はRELAXとよく似ているが構文が異なる言語が生まれることになったわけである。そのような事情により、RELAX NGの構文は、RELAXではなく、TREXによく似ているのである。

RELAX NGで書く最も簡単なスキーマ

 まず、最も簡単なスキーマから記述してみよう。RELAX NGでも、スキーマはXML文書として記述する。ただし、XMLではなく従来のプログラム言語のような雰囲気で記述するRELAX NG Non-XML Syntaxという文法も存在するので注意が必要である。つまり、XMLではない書式で書かれたRELAX NGスキーマも存在するということである。とはいえ、正規のRELAX NGは、XML文書として記述されたものであり、XMLではない構文はオプションである。

 さて、最も小さいXML文書は、トップレベルの要素だけを持ち、その内容は空であるようなものになる。以下のようなXML文書がそれに当たる。

編集注:以下のXML文書には説明のために先頭に行番号を付けています。また、背景が緑色のXML文書は、スキーマを考えるためのXML文書。背景が水色のXML文書が、RELAX NGによるスキーマです。

1: <?xml version="1.0"?>
2: <a/>

 このXML文書を妥当とするRELAX NGスキーマは以下のように記述する。

1: <?xml version="1.0"?>
2: <element name="a" xmlns="http://relaxng.org/ns/structure/1.0">
3:   <empty/>
4: </element>

編集注:「あるXML文書を妥当とする」は、そのXML文書を妥当なXML文書にする、という意味で使っている。スキーマ言語によって文書型が定義されているXML文書を「妥当なXML文書」と呼び、スキーマ言語によって文書型が定義されてはいないが、XML文法に従って記述されているXML文書を「整形式なXML文書」と呼ぶ。

 まず1行目はXML宣言なので読み飛ばす。2行目のelement要素は、要素についての定義を記述するために使用される。ここでは、name属性に指定された名前(a)を要素名とする要素について定義しようとしている。なお、xmlns属性が付いているが、これはRELAX NG自身の名前空間を示すためのもので、これはXML文書の最初に1個だけあれば通常は問題ないものである。

 2行目で示されているのは、aという要素があるということだけで、その詳細は、element要素の内容として記述されている。この例では、3行目にempty要素だけが記述されているが、これは内容モデルが空であることを示す要素である。つまり、DTDなら、<!ELEMENT a EMPTY>と記述することに等しい。

例1:要素の内容に文字列を許す

 このままでは意味のある情報は何も書き込めないので、a要素の内容として文字列を記述できるようにスキーマを変更してみよう。つまり、以下のようなXML文書を妥当とするようにしてみよう。

1: <?xml version="1.0"?>
2: <a>Extensible Markup Language</a>

 単純に文字列を許したければ、内容モデルとしてtext要素を記述すればよい。以下は、前の例のempty要素をtext要素に入れ替えたものである。

1: <?xml version="1.0"?>
2: <element name="a" xmlns="http://relaxng.org/ns/structure/1.0">
3:   <text/>
4: </element>

 これは、DTDなら、<!ELEMENT a (#PCDATA)>と記述することに等しい。

例2:要素に属性を付ける

 以下のXML文書のように、要素aに属性bが付いている場合に妥当とするには、どのようにスキーマを記述すればよいのだろうか。

1: <?xml version="1.0"?>
2: <a b="Extensible">Markup Language</a>

 これを記述したのが下記のスキーマである。

1: <?xml version="1.0"?>
2: <element name="a" xmlns="http://relaxng.org/ns/structure/1.0">
3:   <attribute name="b">
4:     <text/>
5:   </attribute>
6:   <text/>
7: </element>

 DTDでは、<!ATTLIST …>で属性を記述するが、RELAX NGではattribute要素を使う。attribute要素を、属性を付けたい要素のelement要素の子として記述すればよい。属性の名前は、attribute要素のname属性に名前を記述する。属性の内容についての指定は、attribute要素の内容に記述する。単なる文字列の場合は、text要素を記述する。

例3:要素に子要素を持たせる

 要素の中に子要素のあるXML文書を妥当とするにはどうしたらよいのだろうか。例えば、以下のようなXML文書を妥当とするにはどうスキーマを記述すればよいだろうか。

1: <?xml version="1.0"?>
2: <a>
3:   <b>Extensible</b>Markup Language
4: </a>

 以下のように、element要素の子要素としてelement要素を記述すればよい。

1: <?xml version="1.0"?>
2: <element name="a" xmlns="http://relaxng.org/ns/structure/1.0">
3:   <element name="b">
4:     <text/>
5:   </element>
6:   <text/>
7: </element>

 これを見れば分かるとおり、element要素はネストしてよい。ネストすることにより、要素の内容に要素を記述できることを示す。DTDでは、このような場合、複数の<ELEMENT …>を並べることになるが、RELAX NGではelement要素をネストすることで表現できる。

 もう1つ注目すべきことは、要素に属性を付ける記述と、要素に子要素を付ける記述が非常によく似ていることである。2つのサンプルスキーマを見比べていただきたい。要素の名前が、attributeかelementかの違いはあるが、記述すべき場所は同じである。このように、属性と要素を非常に似通った方法で記述できることが、RELAX NGの特徴の1つである。

例4:指定した順番での要素の出現

 一連の要素が、指定された順番で出現した場合のみ、妥当とするにはどうしたらよいのだろうか。例えば、以下のようなXMLを妥当とするには、どのようにスキーマを記述すればよいのだろうか。

1: <?xml version="1.0"?>
2: <a>
3:   <b>Extensible</b>
4:   <c>Markup</c>
5:   <d>Language</d>
6: </a>

 以下のように記述すれば、これを実現できる。

1: <?xml version="1.0"?>
2: <element name="a" xmlns="http://relaxng.org/ns/structure/1.0">
3:   <element name="b">
4:     <text/>
5:   </element>
6:   <element name="c">
7:     <text/>
8:   </element>
9:   <element name="d">
10:     <text/>
11:   </element>
12: </element>

 単純に、出現してほしい順に、要素を指定するためのelement要素を記述していくだけでよい。このように順番に記述したelement要素は、その順番で出現しなければ妥当としないという機能を含んでおり、特に順番を強制する要素などは存在しない。なお、DTDの場合は、内容モデルをカンマ区切りで記述した機能に相当するものである。この例なら、<!ELEMENT a (b,c,d)>のように記述することに等しい。

例5:任意の内容を選択するには

 列挙した内容のどれか1つが出現すれば妥当とするにはどうしたらよいだろうか。例えば、以下の3つのXML文書のいずれでも妥当としたいとする。

妥当にしたい文書その1
1: <?xml version="1.0"?>
2: <a>
3:   <b>Extensible</b>
4: </a>
妥当にしたい文書その2
1: <?xml version="1.0"?>
2: <a>
3:   <c>Markup</c>
4: </a>
妥当にしたい文書その3
1: <?xml version="1.0"?>
2: <a>
3:   <d>Language</d>
4: </a>

 これを実現するスキーマは以下のように記述する。

1: <?xml version="1.0"?>
2: <element name="a" xmlns="http://relaxng.org/ns/structure/1.0">
3:   <choice>
4:     <element name="b">
5:       <text/>
6:     </element>
7:     <element name="c">
8:       <text/>
9:     </element>
10:     <element name="d">
11:       <text/>
12:     </element>
13:   </choice>
14: </element>

 このスキーマのポイントはchoice要素である。choice要素の子要素に列挙された内容は、そのいずれか1つがマッチすれば妥当ということになる。つまり、この場合は、b要素、c要素、d要素のいずれか1つだけが存在すれば妥当ということになる。DTDなら、<!ELEMENT a (b|c|d)>のように記述することに等しい。

例6:内容の任意回数の繰り返しを許す

 同じ内容が何回出現しても妥当としたい場合がある。例えば、以下のようなXML文書で、b要素の個数が何個に増減しても妥当と見なしたいとする。

1: <?xml version="1.0"?>
2: <a>
3:   <b>Extensible</b>
4:   <b>Markup</b>
5:   <b>Language</b>
6: </a>

 これを実際に記述したスキーマは以下のようになる。

1: <?xml version="1.0"?>
2: <element name="a" xmlns="http://relaxng.org/ns/structure/1.0">
3:   <zeroOrMore>
4:     <element name="b">
5:       <text/>
6:     </element>
7:   </zeroOrMore>
8: </element>

 このスキーマで注目すべきは、zeroOrMore要素である。この要素の内容は、0回以上の任意の回数の繰り返しを許す。これと類似の要素に、oneOrMore要素もある。こちらの方は、1回以上の任意の回数の繰り返しを許す。1回も書かなくても許す場合はzeroOrMore要素を、1回は少なくとも記述すべきときはoneOrMore要素を使う。DTDでいうと、zeroOrMore要素は、<!ELEMENT a (b*)>、oneOrMore要素は、<!ELEMENT a (b+)>のように記述することに等しい。

例7:内容の有無の任意選択

 ある内容を書いてもよいし、書かなくてもよい場合がある。例えば、以下の2つのXML文書は要素bの有無が相違点だが、どちらも妥当としたいとする。

妥当にしたい文書その1
1: <?xml version="1.0"?>
2: <a>
3:   <b>Extensible Markup Language</b>
4: </a>
妥当にしたい文書その2
1: <?xml version="1.0"?>
2: <a/>

 これを実現するスキーマは以下のようになる。

1: <?xml version="1.0"?>
2: <element name="a" xmlns="http://relaxng.org/ns/structure/1.0">
3:   <optional>
4:     <element name="b">
5:       <text/>
6:     </element>
7:   </optional>
8: </element>

 ここで注目すべきは、optional要素である。この要素の内容は、存在しても存在していなくても、妥当として扱われる。DTDなら、<!ELEMENT a (b?)>のように記述することに等しい。

例8:内容が揃っていれば順番は問わない

 ある一連の内容が記述されている必要はあるが、順番を問わないという場合がある。つまり、全ての内容がそろっていればよく、それがどのような順番で並んでいるかは関係ないという場合である。例えば、以下のようなXML文書がどちらでも同じように妥当であると見なしたいとする。

妥当にしたい文書その1
1: <?xml version="1.0"?>
2: <a>
3:   <b>Extensible</b>
4:   <c>Markup</c>
5:   <d>Language</d>
6: </a>
妥当にしたい文書その2
1: <?xml version="1.0"?>
2: <a>
3:   <c>Markup</c>
4:   <d>Language</d>
5:   <b>Extensible</b>
6: </a>

 これを実現するスキーマは以下のとおりである。

1: <?xml version="1.0"?>
2: <element name="a" xmlns="http://relaxng.org/ns/structure/1.0">
3:   <interleave>
4:     <element name="b">
5:       <text/>
6:     </element>
7:     <element name="c">
8:       <text/>
9:     </element>
10:     <element name="d">
11:       <text/>
12:     </element>
13:   </interleave>
14: </element>

 ポイントはinterleave要素である。interleave要素は、その子として記述された内容を、順番を問わずすべて出現させることを指定する。なお、XMLのDTDには、interleave要素に対応する機能は存在しない。そのため、interleave要素を使用したRELAX NGスキーマは単純な変換ではDTD形式に直すことはできない。

例9:要素の混合

 要素の間に文字列が入っても構わない場合がある。例えば、以下のXML文書のようにどの位置に文字列が入っても妥当にしたい場合がある。

1: <?xml version="1.0"?>
2: <a>Extensible <b>Markup</b> Language</a>

 これを実現したスキーマは以下のとおりである。

1: <?xml version="1.0"?>
2: <element name="a" xmlns="http://relaxng.org/ns/structure/1.0">
3:   <mixed>
4:     <element name="b">
5:       <text/>
6:     </element>
7:   </mixed>
8: </element>

 ここで注目すべきは、mixed要素である。mixed要素の内容は、どの位置に文字列が出現しても許される。DTDで記述する場合は、#PCDATAを含む内容モデルを記述することで、同じ意味を表現する。つまり、DTDとは表現の方法がかなり違うことになる。

次回はRELAX NGの機能をさらに深く紹介

 さて、ここまでお読みになってお分かりのとおり、RELAX NGはRELAXに比べるとはるかにとっつきやすい構文になっていることが分かるだろう。しかし、とっつきやすさは、スキーマ言語の持つ1つの側面にすぎない。次回は、RELAX NGのより深い、表面からは見えにくいしゃれた機能を紹介していきたいと考えている。

「連載 XMLフロンティア探訪」


XML & SOA フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

HTML5+UX 記事ランキング

本日月間