XMLフロンティア探訪
第10回 XHTMLモジュールを利用した言語開発
(完結編)

前々回、上司から呼び出されて、自社の情報システム用に新しい言語をXMLで作ることになったA君。彼が作った言語を理解するために、2回に分けてXHTMLのモジュールについて解説してきた。今回はついに、A君がどうやって1週間で言語を作り上げたのか、その内容が明らかになる。(編集局)

川俣 晶
株式会社ピーデー
2002/3/5

今回の主な内容
パパッとお手軽にいこう!
考えるのは1つだけ
スキーマもパパッといこう
スキーマの中味をのぞくと
1カ所だけ書き換える
うまくスキーマが機能するか
A君の結論

パパッとお手軽にいこう!

 前回までは、便利なモジュールの宝庫であるModularization of XHTMLの機能と、それを利用した言語の例としてXHTML Basicを解説してきた。今回は、XHTMLとは関係のないモジュールをさらに組み合わせて言語を作ってみよう。もちろん、パパッとお手軽にいこう!

 A君が作った今回の言語は、実はXHTML Basicがあれば機能としては十分なのだが、うるさいお客さんの名前を正確に指定するために、「JIS TR X 0047 XMLによる画像参照交換方式」(これを使うと、いわゆる外字を指定する情報を文書に埋め込むことが可能になる。以下、TR X 0047)の機能も使えるようにしたい、というストーリーで流れを見てみよう。

今昔文字鏡は、国内だけでなく、甲骨文字から、現代中国で使われている簡体字、変体仮名などまで扱っている。フォントの無償配布も行っている。

 TR X 0047は、登録されているグリフ(Glyph:いわゆる文字の形)の名前を記述する方法を定めている。名前は、ISO/IEC 10036という規格で定められた手順で登録する。実はすでに「今昔文字鏡」とよばれる漢字検索システムで使用される番号(文字鏡番号)に対応する9万字分のグリフが登録されており、これらについては今昔文字鏡を利用することで、あらためて登録することなく指定し、表示できる(今昔文字鏡については、「XMLフロンティア探訪 第2回 XMLで解決する外字問題 」参照)。

 例えば、吉田茂元首相の吉の字は、上が「土」になるのが正しいが、このグリフは、ISO/IEC 10036/RA//Glyphs:10003290という名前で登録済みである。これは国際的に認められた名前で、地球上のどこに行っても、この名前は上が「土」の吉であると認められる。実際にこれをXML文書の中に書き込む場合は、http://www.xml.gr.jp/PRE/Referenceという名前空間に属するname属性を記述する。

考えるのは1つだけ

 さて、XHTML BasicとTR X 0047を併用する場合は、頭を使って考える作業が1つだけ発生する。それは、TR X 0047のname属性を、XHTML Basicのどの要素に付けるか、だ。考え方はいくつかある。例えば、img要素で文字を示す画像データを指定して、それに名前としてname属性を付けるという選択もある。また、span要素に付けて文字についての説明をspan要素の内容に書けるようにする、という選択もある。ここでは、後者の方法を取ろう。つまり、以下のように記述可能にする。

<p><span glyph:name="ISO/IEC 10036/RA//Glyphs:10003290"
>吉(上が「土」の吉)
</span>田茂</p>

 このように記述しておけば、文字を厳密に処理する必要のない場合はname属性を無視して 括弧内の(上が「土」の吉)という説明を見て納得すればよいし、厳密に処理する必要がある場合は、ISO/IEC 10036/RA//Glyphs:10003290という名前の下の桁を切り出して文字鏡番号と見なして、今昔文字鏡のフォントで表示印刷するようにすればよい。

 というわけで、これで言語は完成である。とてもお手軽であることが分かるだろう。

スキーマもパパッといこう

 言語はできたが、この言語仕様を日本語で説明するのでは厳密とはいえない。厳密な定義を与えるには、やはりスキーマを書かねばならない(スキーマについてはスキーマの用語解説を参照)。そして、モジュールを用いた言語作成を行うときには、スキーマもモジュールを活用できる。

 ここでは、RELAX NGと呼ばれるスキーマ言語を使って、上記の新言語に対応するスキーマを記述してみよう。

 最初に思い付くのは、新言語のスキーマのほとんどの部分はXHTML Basicと同等であるということだ。XHTML Basicはメジャーな言語なので、そのスキーマはインターネットを検索すれば手に入るだろうと予測できる。そして、手に入れたらその中の属性の定義を1個書き加えればおしまい、と思うかもしれない。

 だが、それはうまい方法ではない。手に入れたスキーマを書き換えてだれかに提出する、ということは、何かあったとき、そのスキーマ全体をメンテナンスする必要に迫られるからだ。もし使用したスキーマ(のオリジナル)に何か問題が発見された場合、修正個所をすべて調べ上げて、自分の言語のスキーマにも反映させなければならない。そのような手間は掛けたくはない。それは手抜きのためではなく、手間を掛けるということは、間違いが入り込む可能性が高まることを意味するからだ。

 さて、実際にXHTMLをRELAX NGで記述したスキーマがModularization of XHTML in RELAX NGとして存在する。これは、RELAX NGの作成者の1人であるJames Clark氏が記述したものだ。このサイトからは、XHTMLのモジュール1個ごとに対応するスキーマを入手できる。以下が入手可能なスキーマの一覧である。名前からどのモジュールが対応するかは容易に推測できると思う(拡張子rngは、そのファイルがRELAX NG形式であることを示している)。

applet.rng attribs.rng base.rng basic-form.rng
basic-table.rng bdo.rng csismap.rng datatypes.rng
edit.rng events.rng form.rng frames.rng
hypertext.rng iframe.rng image.rng inlstyle.rng
legacy.rng link.rng list.rng meta.rng
nameident.rng object.rng param.rng pres.rng
ruby.rng script.rng ssismap.rng struct.rng
style.rng table.rng target.rng text.rng

スキーマの中味をのぞくと

 さて、これらのバラバラのファイルはそのままでは使えない。いくつかのモジュールを組み合わせて初めて独立した言語としての体裁が整うからだ。このサイトでは、モジュールをまとめるスキーマとして、XHTML Basic用と、XHTML Strict用のファイルが用意されている。では、XHTML Basic用のファイルの中身をのぞいてみよう。

<!-- XHTML Basic -->
    
<grammar ns="http://www.w3.org/1999/xhtml"
xmlns="http://relaxng.org/ns/structure/1.0">
     
<include href="modules/datatypes.rng"/>
<include href="modules/attribs.rng"/>
<include href="modules/struct.rng"/>
<include href="modules/text.rng"/>
<include href="modules/hypertext.rng"/>
<include href="modules/list.rng"/>
<include href="modules/basic-form.rng"/>
<include href="modules/basic-table.rng"/>
<include href="modules/image.rng"/>
<include href="modules/param.rng"/>
<include href="modules/object.rng"/>
<include href="modules/meta.rng"/>
<include href="modules/link.rng"/>
<include href="modules/base.rng"/>
    
</grammar>

 たったこれだけである。これを見れば説明するまでもないだろう。6〜19行目までに記述されているinclude要素を削ったり加えたりすれば、使用するモジュールを増減できるのは明らかだ。その程度のことは、RELAX NGの文法を知らなくても実現できそうだ。

 さて、今回作成した言語には、XHTMLには存在しないTR X 0047のname属性を付け加えたいと考えているので、include要素を付け加えるだけでは済まない。これを解決する1つの方法は、span属性の宣言を含むtext.rngファイルを書き換えて属性を追加する方法である。しかし、既存の要素に属性を追加するぐらいなら、ほかにもそのようなモジュールが実際にあるので、それと同様のものを作ることはできるはずだ。つまり、text.rngを書き換えるのではなく、別個にglyph.rngというファイルを作り、これを指定するinclude要素を追加すれば実現できる。そうすれば、James Clark氏が提供したファイルモジュールを1文字も書き換えないで、目的を達することができるはずだ。

1カ所だけ書き換える

 同じようなことをやっているモジュールとして、サーバサイドイメージマップのモジュールがある。これは、img要素に属性を1個付け加えるものだ。これを参考にspan要素にname属性を追加するモジュールを書いてみよう。

<?xml version="1.0" encoding="iso-8859-1"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
     
  <define name="span.attlist" combine="interleave">
    <optional>
      <attribute name="name" ns="http://www.xml.gr.jp/PRE/Reference">
        <ref name="Text.datatype"/>
      </attribute>
    </optional>
  </define>
   
</grammar>

 書き換えるのは、まず、4行目にあるdefine要素のname属性の中身だ。このXHTMLのモジュール定義の中では、各要素の属性の定義を直接記述するのではなく、独立した名前を与えてそれを間接的に参照するようになっている。span要素の属性には、span.attlistという名前が与えられていることが、text.rngファイルを見ると分かる。その名前を書き込まなければならない。次に6行目にあるように、attribute要素の値をnameという名前に変更する。また、この属性は名前空間が違うので、ns属性も追加しておく。7行目に当たる部分は、属性のデータ型を指定するのだが、name属性は普通のテキストとしてグリフ名が入ってほしいところなので、datatype.rngモジュールで宣言されているデータ型の名前の中から、Text.datatypeを選んで、それを参照(ref)するように記述した。

 以上で出来上がりである。多少RELAX NGの文法を知らないと書けないが、この程度のスキーマを扱うだけなら根を詰めて勉強する必要もないだろう。また、これでXHTMLと併用できるTR X 0047のモジュールはできてしまったので、これ以降同じようなことをやる人は、もう一度同じことを書く必要もないわけだ。

 さて、このモジュールのファイル名を書き込んだinclude要素をXHTML Basicのスキーマに付け加えよう。これはもはやXHTML Basicとは違う言語だから、mylang.rngのようなファイル名で保存しておこう。

<!-- My Language -->
    
<grammar ns="http://www.w3.org/1999/xhtml"
xmlns="http://relaxng.org/ns/structure/1.0">
     
<include href="modules/datatypes.rng"/>
<include href="modules/attribs.rng"/>
<include href="modules/struct.rng"/>
<include href="modules/text.rng"/>
<include href="modules/hypertext.rng"/>
<include href="modules/list.rng"/>
<include href="modules/basic-form.rng"/>
<include href="modules/basic-table.rng"/>
<include href="modules/image.rng"/>
<include href="modules/param.rng"/>
<include href="modules/object.rng"/>
<include href="modules/meta.rng"/>
<include href="modules/link.rng"/>
<include href="modules/base.rng"/>
<include href="modules/glyph.rng"/>
   
</grammar>

 20行目と1行目のコメントだけが書き換えられていることが分かるだろう。

うまくスキーマが機能するか

 さて、このスキーマで本当に正しいXML文書とそうではないXML文書をうまく区別できるのだろうか?

 以下のXML文書に、XHTML Basicのスキーマを適用するとエラーになるはずである。XHTML Basicには、TR X 0047のname属性は含まれていないからだ。しかし、いまここで作った言語はエラーにならずに通るはずである。

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>test001</title>
  </head>
  <body xmlns:glyph="http://www.xml.gr.jp/PRE/Reference">
    <p><span glyph:name="ISO/IEC 10036/RA//Glyphs:10003290"
    >吉</span>田茂</p>
  </body>
</html>

 そこで、RELAX NGのバリデータであるjingで確認してみよう。バリデータとは、ある文書がスキーマに適合しているかどうかをチェックするプログラムである。まず、上のXML文書がXHTML Basicのスキーマに適合しているかどうかをチェックさせると、下記のようなメッセージがでる。

Q:\aWrite\@it\xmlfront\010\xhtml_schemas>jing xhtml-basic.rng 
test001.xml Error at URL 
"file:/Q:/aWrite/@it/xmlfront/010/xhtml_schemas/test001.xml",
 line number 6, column number 4: attribute "name" from namespace
"http://www.xml.gr.jp /PRE/Reference" not allowed at this point;
ignored
    
Q:\aWrite\@it\xmlfront\010\xhtml_schemas>

 結果は見てのとおりだ。name属性は許されていないというメッセージがバシッと出てエラーになっている。一方、同じXML文書を、name属性のモジュールを加えたmylang.rngのスキーマに適合しているかどうかバリデータに確認させると、以下のようになる。

Q:\aWrite\@it\xmlfront\010\xhtml_schemas>jing mylang.rng test001.xml
    
Q:\aWrite\@it\xmlfront\010\xhtml_schemas>

 何のメッセージも出ていないのは、エラーが起きていないことを示している。

A君の結論

 前々回で、A君はほんの1週間で、みんなが納得するような成果を挙げたと書いたが、ここまでお読みいただければ、A君が何をしたのかお分かりだろう。A君が手掛けた報告書記述言語など、ほとんどの機能はXHTMLのモジュールで足りたはずだ。それでも不足するほんの一部の機能だけ、新たにスキーマを記述すればよい。それも、既存の大きなスキーマファイルを書き換えるのではなく、小さな新しいファイルを作るだけでよい。これなら、XMLのベテランではないA君の手にも負えたわけだ。

 もちろん、このような方法で成功させたいなら、すでにあるものは可能な限り使うという姿勢で臨む必要がある。既存のモジュールが気に入らないからといって、その書き換えを始めたら手軽には終わらない。ちょっと気に入らなくてもニーズが満たせるならそれで十分と考え、本当に足りないものだけを補うようにしよう。そうすれば、言語の新規作成も、決して怖いものではないだろう。

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


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

注目のテーマ

HTML5+UX 記事ランキング

本日月間