XMLの枠組みでは、スキーマをつくることが言語を設計をすることです。 ここでは、スキーマを記述するための言語である XML Schema を取り上げ、 その記述方法を概観します。
XML Schema は、DTD に代わる新しいスキーマ記述言語です。 DTD と比較すると、以下の特徴があります。
以前、以下のXML文書を例に挙げました。
<?xml version="1.0" encoding="utf-8"?> <挨拶状> <宛先> <名前>電大牛男</名前> <所属>東京電機大学</所属> </宛先> <本文> <段落>ようこそ!</段落> </本文> </挨拶状>
このスキーマを記述することを考えます。 以下のようなルールを記述できればいいわけです。
これをスキーマ言語XML Schema で記述すると、次のようになります。
<?xml version="1.0" encoding="utf-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="挨拶状" type="手紙タイプ"/> <xsd:complexType name="手紙タイプ"> <xsd:sequence> <xsd:element name="宛先" type="アドレスタイプ"/> <xsd:element name="本文" type="文章タイプ"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="アドレスタイプ"> <xsd:sequence> <xsd:element name="名前" type="xsd:string"/> <xsd:element name="所属" type="xsd:string"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="文章タイプ"> <xsd:sequence> <xsd:element name="段落" minOccurs="1" maxOccurs="unbounded" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:schema>
これを先頭から見ていきましょう。 まず最初の行はXML宣言です。
<?xml version="1.0" encoding="utf-8"?>
XML Schema も XML によってつくられた言語ですから、 XML Schema で記述したスキーマの文書はXML文書となります。
次に、xsd:schema 要素があります。これがこのスキーマ文書のroot要素です。 xmlns:xsd=...は名前空間の指定で、この文書で使う XML Schema の要素には、 要素名の頭に xsd:をつけることを宣言しています。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
さて、次からがルールの記述になります。 xsd:element 要素が、検証するXML文書の要素に対応します。
<xsd:element name="挨拶状" type="手紙タイプ"/>
今回は、XML文書のroot要素は「挨拶状」でした。 xsd:element 要素のname属性に、この要素名「挨拶状」を指定します。 type属性に、この要素がどのような要素なのかを指定するのですが、 この要素は、1つの文字列といった単純なタイプ(simple type)ではなく、 子要素をとる複雑なタイプ(complex type)なので、 ここでは「手紙タイプ」という名前を指定しておいて、 「手紙タイプ」は次に記述することにします。
<xsd:complexType name="手紙タイプ"> <xsd:sequence> <xsd:element name="宛先" type="アドレスタイプ"/> <xsd:element name="本文" type="文章タイプ"/> </xsd:sequence> </xsd:complexType>
ここが、「手紙タイプ」の定義です。xsd:complexType 要素として定義しています。 その子要素として xsd:sequence があり、 さらにその子要素として、xsd:element が2つあります。 xsd:element は要素(element)を表していて、その name属性を見ると、 それぞれ「宛先」要素、「本文」要素を表していることがわかります。
xsd:sequence は、その子要素 xsd:element の表現している要素、 「宛先」要素と「本文」要素が、 xsd:element の順序通りに出現することを表しています。 つまり、「手紙タイプ」の要素は、 子要素として「宛先」要素と「本文」要素をこの順番で取らなくてはならない、 と記述されています。
「宛先」要素と「本文」要素がどんな要素かは、それぞれ「アドレスタイプ」、 「文章タイプ」と指定しています。 これは、「挨拶状」要素を「手紙タイプ」と指定しておいたのと同じで、 「アドレスタイプ」と「文章タイプ」はこの後に記述します。
<xsd:complexType name="アドレスタイプ"> <xsd:sequence> <xsd:element name="名前" type="xsd:string"/> <xsd:element name="所属" type="xsd:string"/> </xsd:sequence> </xsd:complexType>
その「アドレスタイプ」の定義が次にあります。 「手紙タイプ」と同様に xsd:complexType 要素として定義しています。 その子要素として xsd:sequence があり、 さらにその子要素として、xsd:element が2つあります。 これは name属性を見ると、それぞれ「名前」要素、 「所属」要素に対応していることがわかります。 つまり、「アドレスタイプ」の要素は、 子要素として「名前」要素と「所属」要素をこの順番で取らなくてはならない、 と記述されています。
「名前」要素と「所属」要素がどんな要素かは、それぞれの type 属性で指定します。 いままで現れた要素では、構造が複雑なのでここではタイプに名前をつけておいて、 あとでまた定義しました。 今回は「名前」要素も「所属」要素も単純な文字列なので、 文字列を表す xsd:string を直接 type 属性の属性値として指定しています。
<xsd:complexType name="文章タイプ"> <xsd:sequence> <xsd:element name="段落" minOccurs="1" maxOccurs="unbounded" type="xsd:string"/> </xsd:sequence> </xsd:complexType>
次に「文章タイプ」の定義があります。 「手紙タイプ」「アドレスタイプ」と同様に xsd:complexType 要素として定義しています。 子要素は「段落」要素1つですが、いままでと違うところは、 「段落」要素は1回だけとは限らず、1回以上何回でも出現することが可能です。 それを表現しているのが、xsd:element 要素の minOccurs 属性と maxOccurs 属性です。 minOccurs は最小出現回数、maxOccurs は最大出現回数です。 "unbounded" は無制限という意味です。 これにより、「段落」要素の出現回数は1回以上で上限なし、という指定になります。
これまでの要素では、minOccurs 属性も maxOccurs 属性も指定していませんでした。 この場合、1回だけ必ず出現するという意味になります。 つまり、minOccurs と maxOccurs のデフォルト値は1です。 省略可能な要素の場合には、minOccurs = 0 と指定することになります。
「段落」要素は、「名前」要素や「所属」要素と同じで単なる文字列です。 そこで、type 属性に xsd:string を指定し、文字列であることを表現しています。
「本文」要素に「言語」という属性を用意することにします。 「本文」要素のタイプは「文章タイプ」と指定していましたから、 この「文章タイプ」の定義を変更します。
<xsd:complexType name="文章タイプ"> <xsd:sequence> <xsd:element name="段落" minOccurs="1" maxOccurs="unbounded" type="xsd:string"/> <xsd:attribute name="言語" type="xsd:NMTOKEN"/> </xsd:sequence> </xsd:complexType>
子要素として xsd:attribute を追加しました。 xsd:element と同じように type 属性でタイプを指定しますが、 ここで属性は単純なタイプ(simple type)しか指定できません。 属性は子要素を持ったりできないからです。 ここでは、単純なタイプである NMTOKEN (空白の入らない文字列)を指定しています。
属性は、出現回数の制限が要素とは異なります。 属性は1回だけ出現するか、現われないかのどちらかです。 必ず現れなければならない必須の属性の場合には、use="required" と指定します。 そうでなければ use="optional"、つまり必須ではないと指定したのと同じになります。 value="日本語" と指定しておくと、「言語」属性は強制的に"日本語"となります。 「言語」属性を指定する場合には、その値は"日本語"でなくてはなりません。 default="日本語" と指定した場合、「言語」属性が指定されなかった場合には"日本語"を指定したのと同じことになります。「言語」属性が指定されていれば、その指定された値になります。
<xsd:complexType name="文章タイプ"> <xsd:sequence> <xsd:element name="段落" minOccurs="1" maxOccurs="unbounded" type="xsd:string"/> <xsd:attribute name="言語" type="xsd:NMTOKEN" use="optional" default="日本語"/> </xsd:sequence> </xsd:complexType>
ややこしいので、要素および属性の出現を制限するために要素宣言と属性宣言のなかで使われる属性の値の表を規格表から引用しておきます。 表にしたところでややこしいものはややこしいのですが…。
文字列、数値など、子要素や属性を持たないタイプを単純なタイプ(simple type)といいます。 「名前」や「所属」は文字列しかないので単純なタイプということになります。
テーブル 2. XML Schema に組み込まれた単純型 (引用) | ||
---|---|---|
単純型 | 例 (カンマで区切っている) | ノート |
string | Confirm this is electric | |
normalizedString | Confirm this is electric | (3) を参照せよ。 |
token | Confirm this is electric | (4) を参照せよ。 |
byte | -1, 126 | (2) を参照せよ。 |
unsignedByte | 0, 126 | (2) を参照せよ。 |
base64Binary | GpM7 | |
hexBinary | 0FB7 | |
integer | -126789, -1, 0, 1, 126789 | (2) を参照せよ。 |
positiveInteger | 1, 126789 | (2) を参照せよ。 |
negativeInteger | -126789, -1 | (2) を参照せよ。 |
nonNegativeInteger | 0, 1, 126789 | (2) を参照せよ。 |
nonPositiveInteger | -126789, -1, 0 | (2) を参照せよ。 |
int | -1, 126789675 | (2) を参照せよ。 |
unsignedInt | 0, 1267896754 | (2) を参照せよ。 |
long | -1, 12678967543233 | (2) を参照せよ。 |
unsignedLong | 0, 12678967543233 | (2) を参照せよ。 |
short | -1, 12678 | (2) を参照せよ。 |
unsignedShort | 0, 12678 | (2) を参照せよ。 |
decimal | -1.23, 0, 123.4, 1000.00 | (2) を参照せよ。 |
float | -INF, -1E4, -0, 0, 12.78E-2, 12, INF, NaN | 単精度 32 ビット浮動小数と等価、NaN は "数字以外 (not a number)" である。(2) を参照せよ。 |
double | -INF, -1E4, -0, 0, 12.78E-2, 12, INF, NaN | 倍精度 64 ビット浮動小数と等価、(2) を参照せよ。 |
boolean | true, false 1, 0 |
|
time | 13:20:00.000, 13:20:00.000-05:00 | (2) を参照せよ。 |
dateTime | 1999-05-31T13:20:00.000-05:00 | 協定世界時間 (Co-Ordinated Universal Time - UTC) から 5 時間遅れの米国東部時間の 1999 年 5 月 31 日午後 1 時 20 分。(2) を参照せよ。 |
duration | P1Y2M3DT10H30M12.3S | 1 年 2 ヶ月 3 日 10 時間 30 分 12.3 秒。 |
date | 1999-05-31 | (2) を参照せよ。 |
gMonth | --05-- | 5 月。(2) と (5) を参照せよ。 |
gYear | 1999 | 1999 年。(2) と (5) を参照せよ。 |
gYearMonth | 1999-02 | 日に関わりのない 1999 年 2 月。(2) と (5) を参照せよ。 |
gDay | ---31 | 31 日。(2) と (5) を参照せよ。 |
gMonthDay | --05-31 | すべての年の 5 月 31 日。(2) と (5) を参照せよ。 |
Name | shipTo | XML 1.0 Name 型。 |
QName | po:USAddress | XML Namespace QName (訳注: QName とは、qualified name の略で、修飾された名前といった意味)。 |
NCName | USAddress | XML Namespace NCName、つまり、接頭辞とコロンのない QName。 |
anyURI | http://www.example.com/, http://www.example.com/doc.html#ID5 | |
language | en-GB, en-US, fr | XML 1.0 で定義された xml:lang にとって妥当な値。 |
ID | XML 1.0 ID 属性型。(1) を参照せよ。 | |
IDREF | XML 1.0 IDREF 属性型。(1) を参照せよ。 | |
IDREFS | XML 1.0 IDREFS 属性型。(1) を参照せよ。 | |
ENTITY | XML 1.0 ENTITY 属性型。(1) を参照せよ。 | |
ENTITIES | XML 1.0 ENTITIES 属性型。(1) を参照せよ。 | |
NOTATION | XML 1.0 NOTATION 属性型。(1) を参照せよ。 | |
NMTOKEN | US, Brésil |
XML 1.0 NMTOKEN 属性型。(1) を参照せよ。 |
NMTOKENS | US UK, Brésil Canada Mexique |
XML 1.0 NMTOKENS 属性型。つまり、ホワイトスペースで区切られた NMTOKEN のリスト。(1) を参照せよ。 |
注意: (1) XML Schema と XML 1.0 DTD 間の互換性を保つため、単純型 ID, IDREF, IDREFS, ENTITY, ENTITIES, NOTATION, NMTOKEN, NMTOKENS は、属性内だけで使うべきである。(2) この型の値は、一つ以上のレキシカルフォーマット (lexical format) で表わすことができる。例えば、100 と 1.0E2 はいずれも、"100" を表わす妥当な浮動フォーマットである。しかし、規範的 (canonical) レキシカルフォーマットを定義するこの型のためのルールが確立されている。XML Schema Part 2 を参照せよ。(3) normalizedString 型における改行 (newline)、タブ、復帰 (carrige-return) 文字は、スキーマプロセッサーに渡される前に、一つのスペースに変換される。(4) normalizedString と同様、隣接するスペース文字は一つのスペース文字に切りつめられる。先頭と末尾にあるスペースは削除される。(5) 接頭辞 "g" は、グレゴリアンカレンダー (Gregorian calender) における期間を表わす。 |
単純なタイプに、さらに制限をかけたい場合があります。 例えば、数値というだけではなくて、値の範囲を指定したい場合です。 そのような場合、xsd:simpleType 要素を使って制限のきつい新たなタイプをつくることができます。 以下の例は、範囲 10000-99999 の myInteger を定義しています。
<xsd:simpleType name="myInteger"> <xsd:restriction base="xsd:integer"> <xsd:minInclusive value="10000"/> <xsd:maxInclusive value="99999"/> </xsd:restriction> </xsd:simpleType>
xsd:restriction 要素では、整数 xsd:integer をベースに制限をかけることを表しています。 これを、この myInteger は xsd:integer から派生している、といいます。 その子要素で値の範囲を指定しています。
文字列の制限方法も例を挙げておきます。 以下の例では、学籍番号を数字2桁+英字2文字+数字3桁に制限しています。
<xsd:simpleType name="学籍番号"> <xsd:restriction base="xsd:string"> <xsd:pattern value="\d{2}[A-Z]{2}\d{3}"/> </xsd:restriction> </xsd:simpleType>
xsd:restriction 要素で、文字列 xsd:string から派生させることを指定しています。 xsd:pattern 要素は value 属性には正規表現を記述することができます。 \d は数字1文字、[A-Z]は大文字英字1文字、{n}は前のものがn回繰り返すことを表現しています。
値を範囲で制限するのではなく、とりうる値を列挙することもできます。
<xsd:simpleType name="USState"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="AK"/> <xsd:enumeration value="AL"/> <xsd:enumeration value="AR"/> <!-- and so on ... --> </xsd:restriction> </xsd:simpleType>
すでに見た単純型は、単純型の中でも原始型(atomic type)と呼ばれるもので、 それ以上分割できないものでした。一方、原始型を組み合わせた単純型としてリスト型があります。
myInteger のリストは次のように定義できます。
<xsd:simpleType name="listOfMyIntType"> <xsd:list itemType="myInteger"/> </xsd:simpleType>
以下の要素はこれに適合します。
<listOfMyInt>20003 15037 95977 95945</listOfMyInt>
リストに対して制限を加えることもできます。
<xsd:simpleType name="USStateList"> <xsd:list itemType="USState"/> </xsd:simpleType> <xsd:simpleType name="SixUSStates"> <xsd:restriction base="USStateList"> <xsd:length value="6"/> </xsd:restriction> </xsd:simpleType>
この例では6項目、必ず列挙しなくてはなりません。
<sixStates>PA NY CA NY LA AK</sixStates>
ユニオン型は、複数の型をまとめた型です。
<xsd:simpleType name="zipUnion"> <xsd:union memberTypes="USState listOfMyIntType"/> </xsd:simpleType>
以下のどちらも許されます。
<zips>CA</zips>
<zips>95630 95977 95945</zips>
<zips>AK</zips>
これまで、xsd:element要素のtype属性で型の名前を指定し、 その名前の型を別で定義していました。 もし一度しか使われない型であるなら、 xsd:element の子要素として xsd:simpleType または xsd:complexType を用意すれば 名前を付ける必要がなくなります。
<xsd:complexType name="Items"> <xsd:sequence> <xsd:element name="item" minOccurs="0" maxOccurs="unbounded"> <xsd:complexType> <xsd:sequence> <xsd:element name="productName" type="xsd:string"/> <xsd:element name="quantity"> <xsd:simpleType> <xsd:restriction base="xsd:positiveInteger"> <xsd:maxExclusive value="100"/> </xsd:restriction> </xsd:simpleType> </xsd:element> <xsd:element name="USPrice" type="xsd:decimal"/> <xsd:element ref="comment" minOccurs="0"/> <xsd:element name="shipDate" type="xsd:date" minOccurs="0"/> </xsd:sequence> <xsd:attribute name="partNum" type="SKU" use="required"/> </xsd:complexType> </xsd:element> </xsd:sequence> </xsd:complexType>
数値xsd:decimalだけを持つ単純型に、「通貨」の属性をつけたい場合、 次のように単純型から派生させます。
<xsd:element name="価格"> <xsd:complexType> <xsd:simpleContent> <xsd:extension base="xsd:decimal"> <xsd:attribute name="通貨" type="xsd:string"/> </xsd:extension> </xsd:simpleContent> </xsd:complexType> </xsd:element>
これは型に名前を付けていないので、xsd:elementの子要素に xsd:complexType 要素がきています。 新しい型の内容モデルが文字データからなり要素を含まないことを指示するため、xsd:simpleContent 要素が使われています。
文字データと子要素を同時に持つ要素があります。 以下の例は顧客への手紙の一部です。
<手紙> <挨拶>Dear Mr.<名前>Robert Smith</名前>.</挨拶> Your order of <quantity>1</quantity> <productName>Baby Monitor</productName> shipped from our warehouse on <shipDate>1999-05-21</shipDate>. .... </手紙>
顧客への手紙のためのスキーマの一部は以下のようになります。
<xsd:element name="手紙"> <xsd:complexType mixed="true"> <xsd:sequence> <xsd:element name="挨拶"> <xsd:complexType mixed="true"> <xsd:sequence> <xsd:element name="名前" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="quantity" type="xsd:positiveInteger"/> <xsd:element name="productName" type="xsd:string"/> <xsd:element name="shipDate" type="xsd:date" minOccurs="0"/> <!-- etc. --> </xsd:sequence> </xsd:complexType> </xsd:element>
文字データが現れることを許容するため、 型定義の mixed 属性を true に設定しています。
属性だけで情報が十分な場合があります。
<価格 通貨="EUR" 金額="423.46"/>
<xsd:element name="価格"> <xsd:complexType> <xsd:complexContent> <xsd:restriction base="xsd:anyType"> <xsd:attribute name="通貨" type="xsd:string"/> <xsd:attribute name="金額" type="xsd:decimal"/> </xsd:restriction> </xsd:complexContent> </xsd:complexType> </xsd:element>
属性だけに制限して定義していることになります。 次のように略記することもできます。
<xsd:element name="価格"> <xsd:complexType> <xsd:attribute name="通貨" type="xsd:string"/> <xsd:attribute name="金額" type="xsd:decimal"/> </xsd:complexType> </xsd:element>
anyType は、何の制約もないことを表します。
<xsd:element name="anything" type="xsd:anyType"/>
こうすると、型がなんでもよい要素になります。 実は何も指定しないと anyType になるので、上記は次のようにも書けます。
<xsd:element name="anything"/>
グループ内の要素の出現順序を指定したり、択一としたりできます。
<xsd:all> <xsd:element name="犬" type="xsd:string"/> <xsd:element name="猫" type="xsd:string"/> </xsd:all> <xsd:sequence> <xsd:choice> <xsd:element name="左" type="xsd:string"/> <xsd:element name="右" type="xsd:string"/> </xsd:choice> <xsd:element name="目印" type="xsd:string"/> </xsd:sequence>
xsd:groupを使うことによって、さらに階層深く指定することもできます。
<xsd:choice> <xsd:element name="はし" type="xsd:string"/> <xsd:group> <xsd:sequence> <xsd:element name="フォーク" type="xsd:string"/> <xsd:element name="スプーン" type="xsd:string"/> </xsd:sequence> </xsd:group> </xsd:choice>
なお、sequence, choice, all, group の各要素自体も、 minOccurs 属性と maxOccurs 属性を持つことができます。 これにより、複雑なルールを表現することが可能です。
「.xsd」という拡張子をつけるのが通例です。
スキーマのファイルがletter.xsdだったとすると、 以下のようにroot要素の属性として指定します。
<?xml version="1.0" encoding="utf-8"?> <挨拶状 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="letter.xsd"> <宛先> <名前>電大牛男</名前> <所属>東京電機大学</所属> </宛先> <本文> <段落>ようこそ!</段落> </本文> </挨拶状>
なお、スキーマに名前空間の指定がある場合には、これとは異なります。