RELAX NGでスキーマ記述
元になるDTD
DTDの作成で作ったDTDをRELAX NGで書換えてみることにした.元になるDTDとXMLは以下の通りである.
---index.dtd--- <!ELEMENT root (img,content+,p,ancher*)> <!ATTLIST root title CDATA #REQUIRED> <!ELEMENT img EMPTY> <!ATTLIST img width CDATA #REQUIRED height CDATA #REQUIRED src CDATA #REQUIRED alt CDATA #REQUIRED> <!ELEMENT content (#PCDATA|ul)*> <!ATTLIST content href CDATA #REQUIRED name CDATA #REQUIRED> <!ELEMENT ul (li+)> <!ELEMENT li (#PCDATA|a)*> <!ELEMENT a (#PCDATA)> <!ATTLIST a href CDATA #REQUIRED> <!ELEMENT p (#PCDATA|a)*> <!ELEMENT ancher (banner)> <!ATTLIST ancher href CDATA #REQUIRED> <!ELEMENT banner EMPTY> <!ATTLIST banner src CDATA #REQUIRED alt CDATA #REQUIRED> ---end of index.dtd--- ---index.xml--- <?xml version="1.0" encoding="utf-8"?> <root title="眠る猫の頁"> <img width="333" height="250" src="cat.jpg" alt=""/> <content href="whatsnew/index.html" name="新着"> 2003.2.2 <ul> <li><a href="saijiki/index.html">歳時記</a>に2003.1.26から2003.2.1までを追加.</li> <li><a href="biboroku/index.html">備忘録</a>のUNIXに「文字列を数える」を追加.</li> </ul> </content> <content href="saijiki/index.html" name="歳時記"> 日々の出来事など. </content> <content href="ryokoki/index.html" name="旅行記"> 旅先での写真や駄文を. </content> <content href="hyoryuki/index.html" name="漂流記"> システム開発への私見です. </content> <content href="biboroku/index.html" name="備忘録"> 教えてもらったことやできたことを忘れないうちに. </content> <content href="zatsuroku/index.html" name="雑録"> その他雑多なものが色々. </content> <content href="links/index.html" name="リンク"> 友人・知合い,便利なソフトウェア,参考になるサイト等. </content> <p>「眠る猫の頁」にリンクを張りたい方は<a href="about.html">リンクについて</a>へ.</p> <ancher href="http://www.nakka.com/wwwc/"> <banner src="http://www.nakka.com/wwwc/wwwc_meta.png" alt="WWWC META Check"/> </ancher> <ancher href="http://validator.w3.org/check?uri=http://www.dab.hi-ho.ne.jp/sasa/"> <banner src="links/banner/valid-xhtml11.png" alt="Valid XHTML 1.1!"/> </ancher> <ancher href="http://jigsaw.w3.org/css-validator/validator?uri=http://www.dab.hi-ho.ne.jp/sasa/"> <banner src="links/banner/vcss.png" alt="Valid CSS!" /> </ancher> </root> ---end of index.xml---
Jingのインストール
validatorとしてJingをインストールする.JingにはJAR形式とWin32 executableの形式の2種類がある.どっちでもいい.今回はJAR形式をダウンロード・インストールした.インストールは以下の手順で行う.
- 圧縮ファイルをダウンロードして
- 適当なディレクトリに保管し
- 必要ならパスの設定orエイリアスの設定をする
cygwinを使っているので以下のaliasを.bashrc
に定義した.
alias jing='java -jar "${PROGRAMFILES}/trang/jing.jar"'
例えば,index.xmlをRELAX NGで書いたスキーマファイルindex.rngで検証する場合は,以下のようになる.$
はコマンドプロンプトで,何も出力がなければ検証終了である.
$ jing index.rng index.xml
なお,インストールディレクトリがjing
ではなくtrang
になっている理由とwin32-executableではなくJARを使った理由は後述する.
とりあえず書いてみた
わからない時は力技だ.DTDと見比べながら書いてみる.まずはルート要素から.
<!-- index.dtd --> <!ELEMENT root (img,content+,p,ancher*)> <!ATTLIST root title CDATA #REQUIRED> <!-- index.rng --> <?xml version="1.0" encoding="utf-8"?> <element name="root" xmlns="http://relaxng.org/ns/structure/1.0"> <attribute name="title"> <text/> </attribute> <element name="img"> </element> <oneOrMore> <element name="content"> </element> </oneOrMore> <element name="p"> </element> <zeroOrMore> <element name="ancher"> </element> </zeroOrMore> </element>
個々の要素を埋める.imgから.
<!-- index.dtd --> <!ELEMENT img EMPTY> <!ATTLIST img width CDATA #REQUIRED height CDATA #REQUIRED src CDATA #REQUIRED alt CDATA #REQUIRED> <!-- index.rng --> <element name="img"> <attribute name="width"> <text/> </attribute> <attribute name="height"> <text/> </attribute> <attribute name="src"> <text/> </attribute> <attribute name="alt"> <text/> </attribute> <empty/> </element>
content要素はul要素と文字列の混合だ.
<!-- index.dtd --> <!ELEMENT content (#PCDATA|ul)*> <!ATTLIST content href CDATA #REQUIRED name CDATA #REQUIRED> <!-- index.rng --> <element name="content"> <attribute name="href"> <text/> </attribute> <attribute name="name"> <text/> </attribute> <mixed> <optional> <element name="ul"> </element> </optional> </mixed> </element>
こうして,できあがったのがこれ.
<!-- index.rng --> <?xml version="1.0" encoding="utf-8"?> <element name="root" xmlns="http://relaxng.org/ns/structure/1.0"> <attribute name="title"> <text/> </attribute> <element name="img"> <attribute name="width"> <text/> </attribute> <attribute name="height"> <text/> </attribute> <attribute name="src"> <text/> </attribute> <attribute name="alt"> <text/> </attribute> <empty/> </element> <oneOrMore> <element name="content"> <attribute name="href"> <text/> </attribute> <attribute name="name"> <text/> </attribute> <mixed> <optional> <element name="ul"> <oneOrMore> <element name="li"> <mixed> <element name="a"> <attribute name="href"> <text/> </attribute> <text/> </element> </mixed> </element> </oneOrMore> </element> </optional> </mixed> </element> </oneOrMore> <element name="p"> <mixed> <element name="a"> <attribute name="href"> <text/> </attribute> <text/> </element> </mixed> </element> <zeroOrMore> <element name="ancher"> <attribute name="href"> <text/> </attribute> <element name="banner"> <attribute name="src"> <text/> </attribute> <attribute name="alt"> <text/> </attribute> <empty/> </element> </element> </zeroOrMore> </element> <!-- end of index.rng -->
名前付きパターン
力技は,投入する労力に比べ得るものは少ない.名前付きパターンを使って見通しを良くしよう.そのためにgrammar要素とstart要素を導入する.要するにルート要素にgrammar,grammar直下に一つのstart要素を用意すれば名前付きパターンが使えるわけだ.
<!-- index.rng --> <?xml version="1.0" encoding="utf-8"?> <grammar xmlns="http://relaxng.org/ns/structure/1.0"> <!-- ルート要素はgrammar --> <start> <!-- 直下にstart要素 --> <element name="root"> <attribute name="title"> <text/> </attribute> <ref name="img"/> <oneOrMore> <ref name="content"/> </oneOrMore> <ref name="p"/> <zeroOrMore> <ref name="ancher"/> </zeroOrMore> </element> </start> <define name="img"> <element name="img"> <attribute name="width"> <text/> </attribute> <attribute name="height"> <text/> </attribute> <attribute name="src"> <text/> </attribute> <attribute name="alt"> <text/> </attribute> <empty/> </element> </define> <define name="content"> <element name="content"> <attribute name="href"> <text/> </attribute> <attribute name="name"> <text/> </attribute> <mixed> <optional> <element name="ul"> <oneOrMore> <element name="li"> <mixed> <ref name="a"/> </mixed> </element> </oneOrMore> </element> </optional> </mixed> </element> </define> <define name="p"> <element name="p"> <mixed> <ref name="a"/> </mixed> </element> </define> <define name="ancher"> <element name="ancher"> <attribute name="href"> <text/> </attribute> <element name="banner"> <attribute name="src"> <text/> </attribute> <attribute name="alt"> <text/> </attribute> <empty/> </element> </element> </define> <define name="a"> <element name="a"> <attribute name="href"> <text/> </attribute> <text/> </element> </define> </grammar> <!-- end of index.rng -->
データ型の指定
dataLibrary
属性を使ってXML Schemaデータ型を使うことにする.imgタグのheight属性とwidth属性は正の整数にしてみる.
<?xml version="1.0" encoding="utf-8"?> <grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> <!-- XML Schemaのデータ型を使用 --> <start> <element name="root"> <attribute name="title"> <text/> </attribute> <ref name="img"/> <oneOrMore> <ref name="content"/> </oneOrMore> <ref name="p"/> <zeroOrMore> <ref name="ancher"/> </zeroOrMore> </element> </start> <define name="img"> <element name="img"> <attribute name="width"> <data type="positiveInteger"/> <!-- widthは正の整数 --> </attribute> <attribute name="height"> <data type="positiveInteger"/> <!-- heightは正の整数 --> </attribute> <attribute name="src"> <text/> </attribute> <attribute name="alt"> <text/> </attribute> <empty/> </element> </define> [省略] ---end of index.rng---
試しにimgタグに小数や負数を指定してjingで検証を行うと,このようになる.
---index.xml--- <?xml version="1.0" encoding="utf-8"?> <root title="眠る猫の頁"> <img width="333.5" height="-250" src="cat.jpg" alt=""/> <!-- widthに小数,heightに負数を指定 --> [省略] ---end of index.xml--- $ jing index.rng index.xml c:\xxx\index.xml:3: error: bad value for attribute "width" c:\xxx\index.xml:3: error: bad value for attribute "height"
trangで変換
Trangを使うと,DTDからRELAX NGへ変換ができる.インストールはJing同様,
- 圧縮ファイルをダウンロードして
- 適当なディレクトリに保管し
- 必要ならパスの設定orエイリアスの設定をする
となる.ただし,TrangはJingと同じディレクトリにないといけない.ちなみに,Trangの圧縮ファイルの中にJingも入っている.前述したJingのインストールディレクトリがTrangになっているのは,Trangをダウンロードして一緒に入ってたJingを使っているためである.Jing同様,.bashrcに以下のaliasを定義した.
alias trang='java -jar "${PROGRAMFILES}/trang/trang.jar"'
冒頭のDTDindex.dtdからRELAX NGスキーマを作ってみる.ファイル名はtrang.rngとした.
$ trang index.dtd trang.rng
出力が何もなければ完了である.こんなファイルができる.
<!-- trang.rng -->
<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
<define name="root">
<element name="root">
<ref name="attlist.root"/>
<ref name="img"/>
<oneOrMore>
<ref name="content"/>
</oneOrMore>
<ref name="p"/>
<zeroOrMore>
<ref name="ancher"/>
</zeroOrMore>
</element>
</define>
<define name="attlist.root" combine="interleave">
<attribute name="title"/>
</define>
<define name="img">
<element name="img">
<ref name="attlist.img"/>
<empty/>
</element>
</define>
<define name="attlist.img" combine="interleave">
<attribute name="width"/>
<attribute name="height"/>
<attribute name="src"/>
<attribute name="alt"/>
</define>
<define name="content">
<element name="content">
<ref name="attlist.content"/>
<zeroOrMore>
<choice>
<text/>
<ref name="ul"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="attlist.content" combine="interleave">
<attribute name="href"/>
<attribute name="name"/>
</define>
<define name="ul">
<element name="ul">
<ref name="attlist.ul"/>
<oneOrMore>
<ref name="li"/>
</oneOrMore>
</element>
</define>
<define name="attlist.ul" combine="interleave">
<empty/>
</define>
<define name="li">
<element name="li">
<ref name="attlist.li"/>
<zeroOrMore>
<choice>
<text/>
<ref name="a"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="attlist.li" combine="interleave">
<empty/>
</define>
<define name="a">
<element name="a">
<ref name="attlist.a"/>
<text/>
</element>
</define>
<define name="attlist.a" combine="interleave">
<attribute name="href"/>
</define>
<define name="p">
<element name="p">
<ref name="attlist.p"/>
<zeroOrMore>
<choice>
<text/>
<ref name="a"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="attlist.p" combine="interleave">
<empty/>
</define>
<define name="ancher">
<element name="ancher">
<ref name="attlist.ancher"/>
<ref name="banner"/>
</element>
</define>
<define name="attlist.ancher" combine="interleave">
<attribute name="href"/>
</define>
<define name="banner">
<element name="banner">
<ref name="attlist.banner"/>
<empty/>
</element>
</define>
<define name="attlist.banner" combine="interleave">
<attribute name="src"/>
<attribute name="alt"/>
</define>
<start>
<choice>
<ref name="root"/>
</choice>
</start>
</grammar>
作成したスキーマに出てくるdefine
要素のcombine
属性にはこんな意味がある.RELAX NGは,同名の名前付きパターンを一つに合成する.combine="interleave"
属性を指定した場合は順不同で合成する.したがって,以下の二つのスキーマは同じものになる.
<!-- define a -->
<define name="attlist.img" combine="interleave">
<attribute name="width"/>
<attribute name="height"/>
<attribute name="src"/>
<attribute name="alt"/>
</define>
<!-- define b -->
<define name="attlist.img" combine="interleave">
<attribute name="src"/>
<attribute name="alt"/>
</define>
<define name="attlist.img" combine="interleave">
<attribute name="width"/>
<attribute name="height"/>
</define>
今回例あげたtrang.rngの場合,同名の名前付きパターンはないので,combine="interleave"
はあんまり関係はない.