有修飾名(Qualified Name) の取扱い

はじめに

一つの名前空間を有するXML文書(属性無し)

宣言すべき名前空間が一つだけの場合、スキーマの記述は2通りあります。以下にその例を紹介します。

ns 属性を用い、デフォルトの名前空間を用いる
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   ns="http://cafe.co.jp/">
<start>
 <element name="喫茶">
  <text/>
 </element>
</start>
</grammar>
xmlns:xx 属性を用いる方法
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   xmlns:cafe="http://cafe.co.jp/">
<start>
 <element name="cafe:喫茶">
  <text/>
 </element>
</start>
</grammar>

いずれの場合も働きは同一で、以下の文書はいずれも正当なものと見倣されます。

デフォルトの名前空間を利用
<?xml version="1.0" encoding="utf-8"?>
<喫茶 xmlns="http://cafe.co.jp/">
 ジャズCafe レナ
</喫茶>
有修飾名を利用
<?xml version="1.0" encoding="utf-8"?>
<店:喫茶 xmlns:店="http://cafe.co.jp/">
 ジャズCafe レナ
</喫茶>

先程述べた通り、名前空間の本質は URI にあるので、接頭辞自体はスキーマと同一である必要はありません。

複数の名前空間を有するXML文書(属性無し)

単一のスキーマで済ませる方法

一つの言語(の名前空間)をデフォルトにする方法と、全ての要素宣言で有修飾名を使う方法と、二通りあります。以下の例では、二つの名前空間を有する文書を検証するためのスキーマを紹介します。

一つをデフォルトにする方法
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   ns="http://cafe.co.jp/" xmlns:data="http://data.org/">
<start>
 <element name="喫茶">
  <element name="data:住所"><text/></element>
 </element>
</start>
</grammar>
全ての要素名を有修飾名とする方法1(ルート要素で宣言)
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   xmlns:cafe="http://cafe.co.jp/" xmlns:data="http://data.org/">
<start>
 <element name="cafe:喫茶">
  <element name="data:住所"><text/></element>
 </element>
</start>
</grammar>

また、限られた要素内でのみ違う名前空間の要素を用いたい場合は、局所的に宣言することも可能です。以下に、上記の二例と同じ働きをするスキーマの一例を示します。

<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   xmlns:cafe="http://cafe.co.jp/">
<start>
 <element name="cafe:喫茶">
  <element name="data:住所" xmlns:data="http://data.org/"><text/></element>
 </element>
</start>
</grammar>

複数のファイルを用いる場合

実際には、一つのファイルで複数の名前空間を用いることは少なく、大抵は異る名前空間を有する複数のファイル(モジュール)を組み合わせることが多いかと思われます。そのような場合、マクロを扱う define, ref 各要素, 外部スキーマを取り込む include 要素を用いて、以下のような記述が可能となります。

いずれもデフォルトの名前空間を使う場合
主スキーマ main.rng
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   ns="http://cafe.co.jp/">
<start>
 <element name="喫茶">
  <ref name="cafe-content"/> <!-- 従スキーマ内のマクロを指定 -->
 </element>
</start>

<include href="sub.rng"/> <!-- 従スキーマの取り込み -->

</grammar>
従スキーマ sub.rng
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   ns="http://data.org/">
<define name="cafe-content"> <!-- 従スキーマ内のマクロ -->
 <element name="住所"><text/></element>
</define>
</grammar>
主スキーマがデフォルト、従スキーマは接頭辞附きの場合
主スキーマ main.rng
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   ns="http://cafe.co.jp/">
<start>
 <element name="喫茶">
  <ref name="cafe-content"/> <!-- 従スキーマ内のマクロを指定 -->
 </element>
</start>

<include href="sub.rng"/> <!-- 従スキーマの取り込み -->

</grammar>
従スキーマ sub.rng
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   xmlns:data="http://data.org/">
<define name="cafe-content"> <!-- 従スキーマ内のマクロ -->
 <element name="data:住所"><text/></element>
</define>
</grammar>
いずれも接頭辞を附属させる場合
主スキーマ main.rng
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   xmlns:cafe="http://cafe.co.jp/">
<start>
 <element name="cafe:喫茶">
  <ref name="cafe-content"/> <!-- 従スキーマ内のマクロを指定 -->
 </element>
</start>

<include href="sub.rng"/> <!-- 従スキーマの取り込み -->

</grammar>
従スキーマ sub.rng
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   xmlns:data="http://data.org/">
<define name="cafe-content"> <!-- 従スキーマ内のマクロ -->
 <element name="data:住所"><text/></element>
</define>
</grammar>

属性が入らない場合は三例いずれの記述でも問題ありませんが、属性を併せて定義する場合は、いくつか注意すべき点があります。それを次の項で述べます。

属性宣言が入り込む場合

誤った例

属性の定義が入り込む場合、とりわけモジュールの作成には注意しなければならないことがあります。例えば、以下の二つのモジュールを取り込む場合を考えてみます。

主モジュール main.rng
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   ns="http://cafe.co.jp/">
<start>
 <element name="喫茶">
  <ref name="cafe-attribute"/>
  <zeroOrMore>
   <ref name="cafe-content"/>
  </zeroOrMore>
 </element>
</start>

<define name="cafe-attribute" combine="interleave">
 <attribute name="店名"><text/></attribute>
</define>

<define name="cafe-content" combine="choice">
 <element name="メニュー"><text/></attribute>
</define>

<include href="sub.rng"/>

</grammar>
従モジュール sub.rng
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   ns="http://garden.co.jp/">

<define name="cafe-attribute" combine="interleave">
 <attribute name="庭園"><text/></attribute>
</define>

<define name="cafe-content" combine="choice">
 <element name="植物"><text/></element>
</define>

</grammar>
検証する文書
<?xml version="1.0"?>
<喫茶 xmlns="http://cafe.co.jp/" xmlns:庭園="http://garden.co.jp/"
  店名="モダン喫茶レナ" 庭園:庭園="ローズガーデン">
 <メニュー>フルーツパフェ</メニュー>
 <庭園:植物>薔薇</庭園:植物>
</喫茶>

ローカルな属性、グローバルな属性

グローバルな属性

接頭辞が附属した属性は「グローバルな」属性です。いかなる名前空間の属するいかなる要素に記述されても、その属性は「属性特有の名前空間を有する属性」です。

ローカルな属性

要素の開始タグにて、接頭辞無しの属性が記された場合、その属性は「ローカルな」属性になります。具体的には、「とある名前空間に属する、その要素に属する属性」となります。同じ名前空間に属する要素であっても違う要素に附属する場合、その属性の所属は異ることになります。

二つの違いの具体例

例えば、以下の例を考えてみます。

<?xml version="1.0">
<喫茶 xmlns="http://cafe.co.jp/" xmlns:cafe="http://cafe.co.jp/">
 <cafe:メニュー cafe:名前="フルーツパフェ"/>
 <cafe:メニュー 名前="コーヒー"/>
</喫茶>

上の文にある二つの 名前cafe:名前 は同じように見えますが、所属が異ります。前者の cafe:名前 属性はグローバル属性で、「名前空間 http://cafe.co.jp/ に属する 名前 属性」ですが、後者はローカル属性で、「名前空間 http://cafe.co.jp/ に属する メニュー 要素に属する、 名前 属性」となります。

具体的にどうすべきか

ローカルな属性のみを入れる場合

グローバルな属性を取り込む場合

先程の失敗例を修正する

前述の例において、庭園:庭園 属性を異る名前空間に属する 喫茶 要素で用いる場合、従スキーマの sub.rng を以下のように修正する必要があります。

<grammar xmlns="http://relaxng.org/ns/structure/1.0"
   xmlns:庭園="http://garden.co.jp/">

<define name="cafe-attribute" combine="interleave">
 <attribute name="庭園:庭園"><text/></attribute>
</define>

<define name="cafe-content" combine="choice">
 <element name="庭園:植物"><text/></element>
</define>

</grammar>

これで 庭園:庭園 属性はグローバル属性となり、適切に文書を検証出来るようになります。