XMLのスキーマ定義の仕方(3)
- 第1回 http://d.hatena.ne.jp/daisuke-m/20091104/1257355231
- 第2回 http://d.hatena.ne.jp/daisuke-m/20091105/1257387380
っつーことで第3回いきます。今回が最終回っす。前回までで、profiles要素の定義を整備しました。次に進みましょう。
profile要素
<xs:element name="profile"> <xs:complexType> <xs:sequence> <xs:element ref="name" /> <xs:element ref="email" minOccurs="0" /> <xs:element ref="skype" minOccurs="0" /> <xs:element ref="webs" minOccurs="0" /> </xs:sequence> <xs:attribute name="id" type="xs:NMTOKEN" use="required" /> </xs:complexType> </xs:element>
- profileには必ずid属性が必要で、値は1以上の整数でなければならない
- profileの子要素は、name(必須), email(省略可), skype(省略可), webs(省略可)が順番に出現
さて、良く見れば、子要素の設定はほぼOK。4つの要素をsequenceで表現し、省略可能なものは minOccurs="0" で表現する。websも省略可能という仕様なので、こいつにも minOccurs を設定して完了です。
次に。xs:attribute で、この要素の属性を設定してます。idっていう属性がrequiredで、その型は xs:NMTOKEN ですよ、ということになっている。
参考 http://www.atmarkit.co.jp/fxml/tecs/021xsd/21.html
内容は整数でなければならんので、型だけ修正すれば良いですね。型は、独自で定義する以外に、XML Schemaで元々定義済みのビルトイン型が豊富に用意されています。simpleTypeだけですけどね。
XML Schema Part 2: Datatypes Second Edition
xs:string や xs:boolean, xs:integer あたりは頻出なので覚えておくと良いですね。xs:NMTOKEN というのは、XMLで「名前」として使えるトークン*1です。
ここでは「1以上の整数」なので、xs:positiveInteger を使いましょう。ちなみに「0以上の整数」だったらxs:nonNegativeIntegerになる。
この状態で、テストケースが通るハズです。そして試しに id に 0 や -1 を設定してみて、テストが失敗することも確認してみてください。
また、10以上20未満なんてのも指定可能です。参考 http://www.atmarkit.co.jp/fxml/tecs/034xsd/34.html
(余談)ユニーク制約
仕様漏れ…というか、わざとなんですがw profile要素のid属性の制約は「1以上の整数」であって、一意制約はつけていません。idとして使うならば、現実問題、この値は一意であるべきです。
そういった場合は、xs:unique という要素で制約をかけられるハズです。
具体的には、profiles要素の子要素に、こんな風に書けば良いハズです。
<xs:element name="profiles"> ... <xs:unique name="uniqueId"> <xs:selector xpath="profile" /> <xs:field xpath="@id"/> </xs:unique> </xs:element>
profiles要素をカレントノードとして、そこからの相対XPathで profile を選択します。その中で、選択要素それぞれのXPath、"@id" が一意であれ、という記述のつもりなんです。
なんで余談なのかって、これ上手くうごかねーんすよww 多分名前空間絡みのミスがあるんじゃないかなぁ、と思ってるんですが、独力で解決できず…。無念なり。
だれか分かったら教えて下さい。超ヘルプ。
name要素とemail要素
自動で吐いてくれたのは、こんな定義になっている。何でだか分からんけども。
<xs:element name="name"> <xs:complexType mixed="true" /> </xs:element>
また余談だが、mixedってのは混在内容っつーのを表している。HTMLのように、「これは
まぁ、nameの型は xs:string で良いだろうから、単純に書き直してしまいます。email要素も同様で。
<xs:element name="name" type="xs:string"/>
skype要素
さて、コイツには正規表現による制約がついていました。
しかしまぁ、子要素も属性もないので、まずはsimpleTypeであることは確定。で、正規表現による制約がついていなければ、xs:string で良いですよね。つまり、xs:stringを基底(base)として、ちょいとパターン(pattern)の制約(restriction)がついたsimpleTypeを定義して適用しましょう、というのが以下。
<xs:element name="skype"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:pattern value="[A-Za-z0-9]+" /> </xs:restriction> </xs:simpleType> </xs:element>
なんて簡単なんだ。そんなん「覚えておかなきゃ書けない!」と思うかもしれないけど、XML SchemaのXML Schemaがあることを思い出して下さい。補完でどうにかなります。俺も覚えてなかったけど、補完で書けたw
さて、テストケースは通っていますか?
webs要素
<xs:element name="webs"> <xs:complexType> <xs:sequence> <xs:element ref="web" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> </xs:element>
- webs要素の直下には、web要素が0個以上存在する
一点だけ直せばいいですね。簡単だと思いますので、敢えて解答を書かないで次いきますw
web要素
<xs:element name="web"> <xs:complexType> <xs:sequence> <xs:element ref="title" minOccurs="0" /> <xs:element ref="url" /> </xs:sequence> <xs:attribute name="type" type="xs:NMTOKEN" use="required" /> </xs:complexType> </xs:element>
まず、今までの知識で解決できる、後者の仕様はどうでしょうか。これも解答は省略。考えてみてください。
では前者。今までの知識で何とかしようと思ったら、
<xs:element name="web"> <xs:complexType> ... <xs:attribute name="type" use="required"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:pattern value="(blog|twitter|wassr)"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> </xs:element>
こんな所でしょうか。ちょっと複雑ですが。web要素の宣言→web要素の型定義→type属性の宣言→type属性の型定義 が、連鎖しています。一応、分解してみるとこんな感じ。
<xs:element name="web" type="webType"/> <xs:complexType name="webType"> <xs:sequence> <xs:element ref="title" minOccurs="0" /> <xs:element ref="url" /> </xs:sequence> <xs:attribute name="type" type="typeType" use="required"/> </xs:complexType> <xs:simpleType name="typeType"> <xs:restriction base="xs:token"> <xs:pattern value="(blog|twitter|wassr)"/> </xs:restriction> </xs:simpleType>
前者のように、まとめて書いてしまうやり方は、Javaの「無名クラス」のような感覚でしょうか。まさに「名前をつけない」訳ですし。名前をつけて、別に定義しておけば、他の所からも同じ定義を参照して再利用できる、という点も似てます。無名クラスは大きくなりすぎると見づらい、というのも一致していますね。どちらの記法を採用するかは、この辺りと同じ判断基準で良い気がします。
さて、ひとまずtype属性の値は正規表現で制約してみましたが。もっと良い方法があります。Javaで言う enum、つまり列挙型です。記述方法は以下の通り。基底となる型はxs:tokenとしてみました。
<xs:simpleType name="typeType"> <xs:restriction base="xs:token"> <xs:enumeration value="blog" /> <xs:enumeration value="twitter" /> <xs:enumeration value="wassr" /> </xs:restriction> </xs:simpleType>
url要素
さて、title要素を飛ばして、url要素を考えてみましょう。まぁ、
<xs:element name="url" type="xs:string" />
これでもいいんですが…。せっかくなので、こんな型を使ってみましょう。
<xs:element name="url" type="xs:anyURI" />
title要素
というわけで、最後。コイツも、問題ないでしょう。nameやemailと同じです…
<xs:element name="title" type="xs:string" />
…じゃあつまらないので、一つ無茶振りな仕様変更の宿題。xml:lang 属性を「つけてもよい」というスキーマに書き換えてみてください。
つまり、以下のXMLがvalidになるように修正です。
<?xml version="1.0" encoding="UTF-8"?> <profiles xmlns="http://xet.jp/xml/ns/sample/profiles" xmlns:fooNS="http://xet.jp/xml/ns/sample/foo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xet.jp/xml/ns/sample/profiles ../../main/resources/profiles.xsd"> <profile id="1"> <name>都元 ダイスケ</name> <email>dai.0304_at_gmail.com</email> <skype>cuervo1800</skype> <webs> <web type="blog"> <title>都元ダイスケ IT-PRESS</title> <url>http://d.hatena.ne.jp/daisuke-m/</url> </web> <web type="twitter"> <url>http://twitter.com/daisuke_m/</url> </web> <web type="wassr"> <url>http://wassr.jp/user/daisukem/</url> </web> </webs> </profile> <profile id="1"> <name>じゅんいち☆かとう</name> <webs> <web type="blog"> <title xml:lang="ja">じゅんいち☆かとうの技術日誌</title> <url>http://d.hatena.ne.jp/j5ik2o/</url> </web> <web type="twitter"> <url>http://twitter.com/j5ik2o/</url> </web> </webs> </profile> <fooNS:foo> <fooNS:bar/> </fooNS:foo> <fooNS:bar/> </profiles>
ちなみに、解説していないテクニックを結構使います。ぶっちゃけかなり難しいです。以下にヒントを書いておくので、一個ずつ開いてみて、ゴールに近づいてみてください。
- xml名前空間の値は "http://www.w3.org/XML/1998/namespace"
- しかし、xml名前空間は、ルートタグに定義しなくても暗黙的に定義済みです。
- でも、スキーマは必要です。どこかを参照しなければ…?
- インポートすればいいんじゃね?
- xs:importを、xs:schemaの第一子要素に書こう。参考 http://www.atmarkit.co.jp/fxml/tecs/030xsd/30.html
- xs:simpleContentとかいうのを使うらしいよ
- xs:restriction(制約)の反対は xs:extension(拡張)らしいよ
- http://www6.airnet.ne.jp/manyo/xml/schema/step45-2.html
がんばれーー。
では、XML Schema講座は以上。もっと細かいことは下記のサイトで学べたりします。参考にどうぞ。