RELAX NG Compact Syntax

はじめに

書いてみるとRELAX NGってのは結構長い.XML形式ではないRELAX NG Compact Syntaxというのがあるので,そっちを使ってみることにする.RELAX NGでスキーマ記述でつくったRELAX NGのスキーマを書き換えることにしよう.

root要素

とりあえず,root要素と属性title,その直下にあるimg,content,p,ancherの各要素を書いてみる.参考までに,DTDも載せてみた.

表1.root要素
RELAX NG RELAX NG Compact Syntax DTD
<element name="root">
  <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>                  
                  
element root{
  attribute title{text},
  element img{},
  element content{}+,
  element p{},
  element ancher{}*
}
                  
<!ELEMENT root (img,content+,p,ancher*)>
<!ATTLIST root title CDATA #REQUIRED>
                  

キーワードelementは要素,attributeは属性を表している.キーワードの後ろに要素名,または属性名.要素名・属性名の後には{}をつけ,子要素やデータ型を記述する.

要素が順番に出現する場合は“,”で繋げる.“*”は0回以上の繰り返し,“+”は1回以上の繰り返しである.0もしくは1回は“?”となる.したがって,意味するところは以下の通りとなる.

  1. 要素rootには属性titleと子要素img,content,p,ancherがある.
  2. 属性titleは文字列.
  3. 子要素img,content,p,ancherはこの順番で記述する.
  4. contentは1回以上の繰返し.
  5. ancherは0回以上の繰返し.

img要素

img要素はwidth・height・src・alt属性を持つ空要素である.空要素なのでキーワードemptyをつける.

表2.img要素
RELAX NG RELAX NG Compact Syntax DTD
<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>
                  
element img{
  attribute width{text},
  attribute height{text},
  attribute src{text},
  attribute alt{text},
  empty
}
                  
<!ELEMENT img EMPTY>
<!ATTLIST img width  CDATA #REQUIRED
              height CDATA #REQUIRED
              src    CDATA #REQUIRED
              alt    CDATA #REQUIRED>
                  

content要素

content要素はテキストとul要素の混合型である.ulはあってもなくても良い.

表3.content要素
RELAX NG RELAX NG Compact Syntax DTD
<oneOrMore>
  <element name="content">
    <attribute name="href">
      <text/>
    </attribute>
    <attribute name="name">
      <text/>
    </attribute>
    <mixed>
      <optional>
        <element name="ul">
        </element>
      </optional>
    </mixed>
  </element>
</oneOrMore>
                  
element content{
  attribute href{text},
  attribute name{text},
  mixed{
    element ul{
    }?
  }
}+,
                  
<!ELEMENT content (#PCDATA|ul)*>
<!ATTLIST content href CDATA #REQUIRED
                  name CDATA #REQUIRED>
                  

ulの下位要素として一つ以上のli要素と混合型のa要素があるので,全部書くとこうなる.

表4.content要素 全部
RELAX NG RELAX NG Compact Syntax DTD
<oneOrMore>
  <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>
</oneOrMore>
                  
element content{
  attribute href{text},
  attribute name{text},
  mixed{
    element ul{
      element li{
        mixed{
          element a{
            attribute href{text},
            text
          }
        }
      }+
    }?
  }
}+,
                  
<!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>
                  

全体像

こうしてできあがったのがこれ.

表5.全体像
RELAX NG RELAX NG Compact Syntax DTD
<?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>
                  
element root{
  attribute title{text},
  element img{
    attribute width{text},
    attribute height{text},
    attribute src{text},
    attribute alt{text},
    empty
  },
  element content{
    attribute href{text},
    attribute name{text},
    mixed{
      element ui{
        element li{
          mixed{
            element a{
              attribute href{text},
              text
            }
          }
        }+
      }?
    }
  }+,
  element p{
    mixed{
      element a{
        attribute href{text},
        text
      }
    }
  },
  element ancher{
    attribute href{text},
    element banner{
      attribute src{text},
      attribute alt{text},
      empty
    }
  }*
}
                  
<!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>
                  

名前付きパターン

RELAX NG同様,RELAX NG Compact Syntaxでも名前付きパターンを使うことができる.DTDには名前付きパターンはない.

表6.名前付きパターン
RELAX NG RELAX NG Compact Syntax
<?xml version="1.0" encoding="utf-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
  <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>
                  
start=
  element root{
    attribute title{text},
    img, ←要素でも属性でもなく名前付きパターン
    content+, ←要素でも属性でもなく名前付きパターン
    p, ←要素でも属性でもなく名前付きパターン
    ancher* ←要素でも属性でもなく名前付きパターン
  }
img= ←名前付きパターンの定義
  element img{
    attribute width{text},
    attribute height{text},
    attribute src{text},
    attribute alt{text},
    empty
  }
content=
  element content{
    attribute href{text},
    attribute name{text},
    mixed{
      element ul{
        element li{
          mixed {a}
        }+
      }?
    }
  }
p=
  element p{
    mixed{a}
  }
ancher=
  element ancher{
    attribute href{text},
    element banner{
      attribute src{text},
      attribute alt{text},
      empty
    }
  }
a=
  element a{
    attribute href{text},
    text
  }
                  

Trangで変換

これまで,手で書いてきたが,Trangを使うとDTDとRELAX NGとRELAX NG Compact Syntaxの三つのスキーマを変換しあうことができる.RELAX NGからRELAX NG Compact Syntaxを作ってみる.trang.jarがカレントディレクトリにある前提の場合,このようなコマンドになる.

$ java -jar trang.jar index.rng index.rnc
        

第一引数が入力ファイル,第二引数が出力ファイル.これらのファイルの拡張子で,入力・出力のスキーマを判定する.拡張子.rngがRELAX NG,.rncがRELAX NG Compact Syntaxである.