これまで扱ってきたXML文書は、単一のスキーマに基づくものでした。 例えば注文書のXML文書では、 注文書のスキーマで定義されている要素だけが現れます。
実際の場面では、1つのXML文書内で、複数のスキーマの要素を混在させて使いたい場合があります。 例えば、品物を記述するためのスキーマを用意しておくと、 注文書や在庫管理票など、品物の情報の記述を必要とするさまざまな場面で使えそうです。
また、既存のものをベースに新たな文書を作りたい場合があります。 例えば、XHTMLの要素と独自の要素を組み合わせる、 XHTMLとMathMLを組み合わせる、などです。
ここでは、複数のスキーマに基づくXML文書の作成方法を紹介します。
いま、スキーマaとスキーマbの2つのスキーマに基づいたXML文書を考えます。 スキーマが2つあるということは、 それぞれのスキーマで定義されている要素が1つの文書内に混在するということです。
ここで、2つのスキーマの要素をそのまま使うことはできません。 どちらのスキーマの要素なのかを、なんらかの方法で明示しておく必要があります。 例えば、スキーマaでもスキーマbでもname要素があったとき、 そのままだと、どちらの要素なのか区別かつかなくなるからです。
名前空間(name space)は XML に限らない一般的な概念で、 名前の有効な範囲を表します。 例えば「1号館」という名前は、 東京電機大学の東京千住キャンパスという名前空間に属しています。 その名前空間の外では、「1号館」は別の建物を表すかもしれません。
プログラミングの際、 1つのメソッド(あるいは関数)の中で同じ変数名を宣言することはできません。 これは、1つの名前空間の中に同じ名前をつくることはできないからです。
XMLの世界では、1つのスキーマが1つの名前空間を持つようにすることで、 名前がかち合うことを避けています。 その際、個々の名前空間を区別するために、 名前空間の識別のための名前をつけています。
これまでつくってきた XML Schema によるスキーマを見てみましょう。
<?xml version="1.0" encoding="utf-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="挨拶状" type="手紙タイプ"/> ...
スキーマもXML文書ですから、名前空間を使うことができて、 実は名前空間の指定をしていました。 このスキーマでは、XML Schema の仕様で定められた要素を xsd:element などと xsd を前につけて使っていましたが、これが名前空間の指定です。
<?xml version="1.0" encoding="utf-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="挨拶状" type="手紙タイプ"/> ...
名前空間の指定は、root要素である xsd:schema 要素の xmlns 属性で行っています。 XML Schema の名前空間の名前は http://www.w3.org/2001/XMLSchema で、 これは長いので xsd という別名をつけています。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
名前空間の名前が http://www.w3.org/2001/XMLSchema だというのは奇異に感じることと思います。 これは、本当は他と違えばなんでもよいのですが、 URLは世界で唯一の場所を表す仕組みなのでこれが流用されています。 なお、大文字小文字の区別があることに注意しましょう。
xmlns: のあとの xsd が別名で、要素名の先頭に書くものです(接頭辞といいます)。 ここはユーザが好きに決めてよいので、例えば、xs とすることもできます。
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
こうすると、xs:element などと書くようになるわけです。 なお、root要素の schema 要素も xs:schema となります。 これは、xmlnsを指定した要素自身から名前空間が使えるという仕様になっているからです。
別名を指定しないと、xsd:...といった名前空間の指定をしない要素の名前空間を 定めたことになります。
<schema xmlns="http://www.w3.org/2001/XMLSchema">
こうすると、xsd:element などと書く必要はなくなり、 element とだけ書けばよくなります。
では逆に、スキーマを記述したときに、 そのスキーマの属する名前空間はどこで定めるのでしょうか。
スキーマの名前空間の指定は、スキーマのroot要素である xsd:schema 要素の targetNamespace 属性で行います。
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema xmlns="名前空間の名前"
targetNamespace="名前空間の名前"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="挨拶状" type="手紙タイプ"/>
...
ここでは簡単のため、このスキーマにおけるデフォルトの名前空間としても指定しています (xmlns=...)。
XML文書で xmlns 属性によって指定するのは、名前空間の名前でした。 その名前には URL が使われることが多いのですが、 この URL は他の名前空間との区別をするためのもので、 その名前空間に対応するスキーマのファイルがある場所ではありません。 スキーマのファイルの場所は別に指定する必要があります。
名前空間の名前が決まっている場合、 schemaLocation 属性でスキーマの場所を指定します。 schemaLocation 属性は名前空間 "http://www.w3.org/2001/XMLSchema-instance" に属しているので、これだけ他とは違う接頭辞をつけることになります。
<?xml version="1.0" encoding="utf-8"?>
<挨拶状
xmlns="letter"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="letter letter.xsd">
<宛先>
<名前>電大牛男</名前>
<所属>東京電機大学</所属>
</宛先>
<本文>
<段落>ようこそ!</段落>
</本文>
</挨拶状>
schemaLocation 属性の値は、名前空間の名前と、 その名前空間に対応するスキーマのファイル名の組です。 空白で区切って指定します。 複数の組を指定することもできます。区切りはすべて空白です。 上記の例では、letter という名前空間に対応するスキーマのファイル名が letter.xsd だということを表しています。
スキーマに名前空間の指定がない場合には、 名前空間と関係のないスキーマを使うことになるので、 noNamespaceSchemaLocation 属性を使うことになります。 属性の値は、スキーマのファイル名です(URL可)。 ファイルが複数の時には空白で区切ります。
<?xml version="1.0" encoding="utf-8"?>
<挨拶状
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="letter.xsd">
<宛先>
<名前>電大牛男</名前>
<所属>東京電機大学</所属>
</宛先>
<本文>
<段落>ようこそ!</段落>
</本文>
</挨拶状>
次のようなXML文書を考えます。 このXML文書は、cd と artist という2つの名前空間に属する要素を使っています。 名前空間 cd の接頭辞を「cd」、 名前空間artistの接頭辞を「artist」としていますから、 「cd:?」と「artist:?」の要素が混在しています。
<?xml version="1.0" encoding="utf-8"?>
<cd:disc xmlns:cd="cd"
xmlns:artist="artist"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="cd cd.xsd artist artist.xsd">
<cd:title>Pet Sounds</cd:title>
<cd:players>
<artist:artist>
<artist:name>Brian Wilson</artist:name>
<artist:type>Songwriter</artist:type>
</artist:artist>
</cd:players>
</cd:disc>
schemaLocation要素で、名前空間 cd のスキーマファイルは cd.xsd、 名前空間 artist のスキーマファイルは artist.xsd であると宣言しています。
名前空間 cd のスキーマ cd.xsd は次のようになります。 名前空間 cd に属する要素の子要素として名前空間 artist に属している要素を使うので、import要素により名前空間 artist をインポートしています。
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema xmlns="cd"
targetNamespace="cd"
xmlns:artist="artist"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import namespace="artist" schemaLocation="artist.xsd" />
<xsd:element name="disc" type="discType"/>
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="players" type="playersType"/>
<xsd:complexType name="discType">
<xsd:sequence>
<xsd:element ref="title"/>
<xsd:element ref="players"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="playersType">
<xsd:sequence>
<xsd:element maxOccurs="unbounded" ref="artist:artist"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
名前空間 artist に属している要素 artist:artist を ref 属性で参照しています。
なお、これまでと違い、complexType の内部の element でも ref 属性を使っています。 そして、ref 属性の参照先は schema 要素の子要素である element となっています。 これまで通りだと、以下のように書くところです。
<xsd:complexType name="discType"> <xsd:sequence> <xsd:element name="title" type="xsd:string"/> <xsd:element name="players" type="playersType"/> </xsd:sequence> </xsd:complexType>
実は、このような complexType の内部の要素はローカル要素という扱いになります。 XML Schema ではローカル要素には名前空間の接頭辞をつけないのが標準なのですが、 これは混乱の原因になります。 そこで、element 要素を schema 要素の子要素としてグローバル要素とすることにより、 常に接頭辞がつくことになります。
この場合、schema 要素の子要素に title と players を持っていくことにより、 これらが名前空間全体で有効なグローバル要素になり、 「cd:?」「artist:?」などという接頭辞をつけて使うようになります。
なお、ref 属性を使わず、いままで通り complexType の子孫要素に element を書きたい場合には、schema要素の属性に elementFormDefault="qualified" と書いておくとそのような効果が得られます。
名前空間 artist のスキーマ artist.xsd は次のようになります。 artist.xsd は組み込まれる側なので、独立しています。 他の名前空間をインポートすることはありません。
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema xmlns="artist"
targetNamespace="artist"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="artist" type="artistType"/>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="type" type="xsd:string"/>
<xsd:complexType name="artistType">
<xsd:sequence>
<xsd:element ref="name"/>
<xsd:element ref="type"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>