第 8 回 XML

本日の内容


このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。

8-1. XML

マークアップ言語

マークアップとはもともとは文書の注釈で、組版などの仕上がり の指示を出すための記号のことでした。 そのような記号として「段落を分ける」「誤字を直す」など小学校でいくつか 習ったと思います。

マークアップ言語とはこれらの手法の集まりを言います。 XML 以前にも UNIX の roff システムや論文プロセッサの TeX など、テキス トファイルにマークアップを行い、さらに、組版(画面やプリンタでの仕上がりの指定) をコントロールしたものがいくつかありました。

1967 年にGCA(グラフィックコミュニケーション協会)の会長ウィリアム・タ ニクリフはカナダ政府印刷局の会合中に文書の構造情報と体裁情報の分離を提 唱しました。 これを受けて、1969 年に IBM の Ed Mosher, Ray Lorie, Charles F. Goldfarb が Generalized Markup Language というものを提案しました。 これがその後 SGML (Standard GML) に発展し、 1986 年に ISO8879 という国際規格になりました。

SGML はメタ言語です。つまり様々なマークアップ言語を定義するためのルー ルです。 特定の文書に対してそれをマークアップするための定義を記述し、実際にその 文書をマークアップします。 この、マークアップするための定義を文書型定義(Document Type Definition DTD)と言います。 つまり SGML 文書は、 DTD とそれに基づいてマークアップされた文書の組で 表現されます。 これは、「文脈自由文法」と「ルール」と「そのルールにしたがった文字列」 との間の関係と同じようなものです。

文書型定義(DTD)
マークアップされた文書

SGML 文書の構造

さて、WWW はもともと応用物理学者が論文を共有するために作られました。 そのため、論文をプレインテキストではなく、適当なマークアップが望まれま した。 そのため、 SGML を用いて WWW 用の DTD が作成されました。 この DTD で規定された WWW 用の文書のマークアップ法が HTML です。

1990 年代に HTML は大きな進歩を遂げましたが、その中で反省もありました。 それは、 SGML がもともと持つ強力な自由度がコンピュータ処理を複雑にして いるというものです。 そこで、 SGML とある程度の互換性を維持しつつ、拡張可能にし、さらにコンピュー タ処理を容易にしたものとして XML が登場しました。 XML が登場したのは 1998 年のことです(HTML4 は 1997年)。

そして、さらに HTML4 を XML 化する作業が行われました。 まず、HTML4 とほぼ完全互換を目指した XHTML1.0 が登場しました。 これは、 XML の文法に合わせるために HTML4 にいくつか制約を与えたような ものです。 そして、さらに、 XML の特徴である拡張可能性を使えるようにしたのが XHTML1.1です。 本講義資料では数式表現の拡張を使用するため、 XHTML1.1で記述しておりま す。

XML のファミリー

XML はただ単にデータを記述するためだけに作られたのではなく、それをコン ピュータで処理をすることが考えられています。 そのため、様々なデータ処理の手法や、コンピュータプログラムへの API な ども考えられています。

DOMは Document Object Model と呼ばれるもので、 XML のデータ に対してプログラムがアクセスできるように、木構造のオブジェクトとして組 み立てられたデータ構造です。 コンピュータプログラムはXML文書を DOMに変換したり、 DOM から XML文書に 変換することができます。 さらに、 DOM の構造の各部分を自由に編集することができます。

SAX(Simple API for XML)は XML 文書を頭から順番にデータ処理 を行うための API です。 DOM は全データをオンメモリで利用しますが、 SAX は順番に読むため、それ ほどメモリーを消費しません。 したがって、巨大なデータに対して単純な処理を行いたい場合等に適していま す。 SAX では XML を解釈する度にイベントが発生するようになっており、オブザー バデザインパターンでメソッドを登録します。

XPath は XML の構造内を検索するための問い合わせ言語です。 これは単純な検索に用いる他、 XSLT などで活用もできます。 さらに、リレーショナルデータベースに代わる XMLデータベースの問い合わせ 言語としても使用されます。また、この仕様をさらに拡張したものに XQuery があります。

XSLT(XSL Transformations)は XSL(Extensible Stylesheet Language) の構成要素の一つです。 XML 文書を他のドキュメント形式に変換するなどのための変換用の言語です。 つまり、 XML に対して様々な処理をするプログラミング言語とみなすことが できます。

以上の XML に対するプログラミング上の規格は、基本的に Java と Javascript(ECMA Script) に対して正式に定義されます。 この他、 C# などでも実装されています。 本講義では、 Java によるこれらの操作について説明します。

8-2. XML の記法

XML の基本的な要素の記法は、次のような書式になります。

<要素名 属性1="値1" 属性2="値2">
内容
</要素名>

この始めの要素名の部分を「開きタグ」と言い、/(スラッシュ)の部分を「閉 じタグ」と言います。

なお、内容がない要素の記述法は次のようにします。

<要素名 属性1="値1" 属性2="値2" />

さて、データに対して、そのデータを含む抽象的な概念に関するデータをここで はメタデータと呼ぶことにしましょう。 例えば、「坂本直志, データ構造とアルゴリズム II, 火曜日 2 限」というデー タに対して「担当, 科目名, 日時」というデータがメタデータとして対応しま す。 さらにこれらはさらなる「授業」というメタデータに対応します。 このような関係に対して、XML では次のような表し方をします。

  1. <授業>
    <担当>坂本直志</担当>
    <科目名>データ構造とアルゴリズム II</科目名>
    <日時>火曜日 2 限</日時>
    </授業>
    
  2. <授業>
    <担当 name="坂本直志" />
    <科目名 name="データ構造とアルゴリズム II" />
    <日時 day="火曜日" period="2 限" />
    </授業>
    
  3. <授業 担当="坂本直志" 科目名="データ構造とアルゴリズム II"
       日時="火曜日 2 限" />
    

このようにデータを表す際に、要素を用いる方法と、属性を用いる方法があり ます。 これらの違いについて考えてみましょう。

XML では要素は属性とデータを含むことができます。 しかし、含む際に制約条件があります。 まず、データとして含めるデータ型は #PCDATA と呼ばれています。 これは様々なデータを含め、マークアップが可能です。 一方、属性では ""(ダブルクォーテーションマーク) 中にデータを書きますが、 ここに書けるデータは CDATA と呼ばれる文字列データです。 これにはマークアップはできません。 そのため、 CDATA 中には構造のあるデータは書くことができません。

<要素名 属性1="CDATA" 属性2="CDATA">
(属性値には<などは入れられるがマークアップできない)
#PCDATA(内容には< など直接書いてはいけない文字がある一方、
        他の要素を含むことができる)
</要素名>

また、属性に関しては同じ属性は二度現れてはいけないという制限があります。 例えば、授業担当者が複数存在したり、実施日が週に2回以上あるような場合 が想定される場合、それは属性としては表現できません。 また、一人の授業担当者の名前が一つしかないとか、授業科目の名前も一つし かないなど、必ず唯一性が成立していなければならないような状況の場合、属 性としてデータを持たせると、それを処理させるプログラム側で唯一性のチェッ クが不要になります。 このようなことを踏まえると、上記の例は次のように定義すると良いことがわ かります。

<授業 科目名="データ構造とアルゴリズム II" >
<担当 name="坂本直志" />
<日時 day="火曜日" period="2 限" />
</授業>
このようにすると次のような記述もできます。
<授業 科目名="情報通信基礎実験" >
<担当 name="金田先生" />
<担当 name="坂本直志" />
<日時 day="月曜日" period="3 限" />
<日時 day="月曜日" period="4 限" />
</授業>

このようにすると複数の指定が可能な部分とそうでない部分で切り分け、XML のルールから制約を設定できます。

XML のデータ型

それでは、 XML に関して細かいルールを見ていきましょう。

文字

XML は基本的には Unicode で記述します。 特に UTF-8 で記述すれば文字コードの宣言自体を省略できます。 しかし通常は XML 文書の冒頭の XML 宣言で文字コードの宣言をします。

<?xml version="1.0" encoding="utf-8"?>

utf-8 以外で記述しても構いませんが、 Shift_JIS など宣言を書く必要があ ります。

XML 1.0(1998) では要素名や属性名は英字などしか使用できませんでした。し かし、XML 1.1(2004) は要素名にも今まで示した例のように日本語が使えるよ うになりました。 しかし、いくら様々な国の言語の文字をユニコードで指定できるというのは長 所でもありますが、一方で欠点にもなり得ます。 つまり XML 文書を英語以外の言語だけで完結できる一方、現状のパソコンの ように「英語+一言語」という仕様では、指定された外国語が利用可能でない と、 XML 文書その物が他言語のフォントをインストールしなくては確認できないよ うな状況になりえます。 そのためか、 XML1.1 は現在普及していません。 本講義でもたとえ話のような説明以外は、 XML 1.0 を使用し、要素名に英数 字を使います。

さて、要素名ですが、 SGML では大文字小文字を区別しませんでしたが、 XML では区別することになりました。 これでパーサを書くのは楽になりました。 例えば、HTML では要素名を大文字でも小文字でも記述できました。 しかし、 XHTML では要素名は小文字のみになっています。 そのため、 HTML からXHTML への変換を考えると、少なくとも要素名が小文字 になっているかをチェックする必要があります。

また、文字列を表す時ですが、 CDATA ではタグなどを入れてもそのまま文字 列として解釈されますが、 #PCDATA ではタグはすべて意味があるものとして 解釈されてしまいます。 そのため、内容に < などのタグの一部になる文字が含まれるものを書 く場合は注意が必要です。 例えば、 script 要素の内容にはプログラムを書きます。 つまり、 script の内容に対してマークアップは不要ですが、大小比較のため に、大量の< などの記号が含まれるような場合は、 <![CDATA[]]> の間にプログラムなどを入れるという表現があります。 一方、単純に一文字だけ < などを入れるには実体参照という 表現を使います。 実体参照にはこの他キーボードから直接入力しづらいような文字も指定します。 下記がよく使われる実体参照です。

実体参照表示される文字
&amp;&
&lt;<
&gt;>
&quot;"
&apos;'
&copy;©
&trade;
&alpha;α
&spades;
&euro;

また、 &#文字コード(10進数);&#x文字コード(16進数); で指定した文字コードの指す文字を表示できます。

文書構造

ここでは XML の定 義 Extensible Markup Language (XML) 1.0 (Fifth Edition) http://www.w3.org/TR/2008/REC-xml-20081126/ を参考に XML の文法を見ていきます。 これは文脈自由文法で XML の文法を厳密に与えています。 以下、文脈自由文法のルールには番号が表示されていますが、これは原文のそ のままの引用で、原文の式番号です。

その他の構文

XML 文書の構造の説明に入る前に、 XML 文書のどこにでも配置できる構文を 説明します。

White Space

まず、White Space ですが、通常の空白記号 (&#x20;) の他に、TAB 記号 と、 CR と LF を含みます。 これらの 1 個以上連続を white space と呼びます。 XML のドキュメントでは次のように文脈自由文法で定義されています。


[3] S ::= (#x20 | #x9 | #xD | #xA)+
コメント

コメントは <!-- で始まって、 --> で終わり、中になにを書いても構わないの が基本ですが、もともと SGML では定義文とコメントを --(ハイフン二つ) を -- 区切りにして交互に使っていました。

SGML 時代のコメントの使われ方
<! 定義文 -- コメント --
      定義文 -- コメント --
      定義文 -- コメント --
>

それを廃止する際に、コメント中にこのハイフン二つを書かないように規定さ れました。 そのため、XML のコメントの定義も下記のように文脈自由文法で定義されてい ます。


[15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'

なお、Char は文書で使用する文字のことです。

PI

PI とは Processing Instructions のことです。 これは、文書の文字データではなく、アプリケーションに直接渡される記述で す。 例えば、 XHTML 中に PHP のプログラムを書く場合、次のように記述します。


<p>
<?php echo 'Hello World'; ?>
</p>

これは XML の定義書では次のように定義されています。


[16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
[17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))

なお、 Name は要素名などを定義するための「名前」を表すことのできる文字 列です(英文字+英数字の 0 文字以上の連続と思ってよい)。

XML の構造

XML 文書は次のように定義されています。


[1] document ::= prolog element Misc*
[22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)?
[27] Misc ::= Comment | PI | S

まず、 prolog ではすべての部分が省略可能であることに注意してください。 上の定義を解釈すると XML 文書とは次のような構造を持ちます。

  1. XMLDecl(XML 宣言) (あってもなくても良い)
  2. doctypedecl(文書型宣言) (あってもなくても良い)
  3. 一つだけの要素
  4. 以上に加え、 XML宣言の前を除き、コメント、PI、 WhiteSpace はどこで も配置可能

この「一つだけの要素」とは root element とか呼ばれます。 XML 文書では基本的には一つの要素について記述します。 但し、その要素の内容には他の要素が含まれていても構いません。 例えば、 XHTML ドキュメントでは、一つだけの html 要素から構成されてい ますが、内容には head 要素と body 要素が含まれます。

XML 宣言

XML 宣言は既に上の文字の定義の際に、文字コードの指定をするために紹介し ました。 XML 宣言では文字コードの指定の他に version と standalone というオプショ ンを指定します。 standalone オプションは外部参照をするか否かを指します。 この条件は複雑なので、ここでは詳しく説明しません。 standalone オプションは省略可能であり、省略しても問題ありません。 また、 XML のバージョンは "1.0" を常に指定することにします。

<?xml version="1.0" encoding="utf-8"?>
文書型宣言

文書型宣言は、XML 文書にどのような要素が含まれるかを記述します。

[28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S?
                          (' [' intSubset ']' S?)? '>'

文書型宣言を行うには外部サブセット宣言と、内部サブセット宣言の二通りの 方法があります。 外部サブセット宣言は以下のように ExternalID を指定するもの、内部サブセット宣言は intSubset を指定するものです。 内部サブセット宣言は章を改めて説明します。

外部サブセット宣言

外部サブセット宣言は次の二つの宣言があります。

[75] ExternalID ::= 'SYSTEM' S SystemLiteral 
                  | 'PUBLIC' S PubidLiteral S SystemLiteral

PUBLIC は文書宣言が公式なものとして既に広まっていて、利用側で文書宣言 を改めて取得しなくてよいようなものを言います。 よく使う有名な例は XHTML の宣言などです。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

PubidLiteral は DTD を表すような識別子になります。 ここで、 SystemLiteral は上のように実際に文書型定義が dtd ファイルとし て存在する URI を指定します。

一方、SYSTEM の宣言は、文書型定義を取得するための URI を指定します。 複数の XML 文書で DTD を共有する場合などは、この SYSTEM を使用して、ネッ トワークを利用して行います。

<!DOCTYPE class SYSTEM "class.dtd" >
整形式

XML ではパーサを作り易いように SGML のオプション機能(タグの一部を省略 する機能)が取り除かれました。 そして、構文解析をする際、誤っている構文を発見したら通知する義務があり ます。 従来の HTML 文書では多少の構文誤りに関しては、エラーとせず、なるべく文 書を表示するようになっていました。 しかし、XHTML のような XML 文書は構文誤りを発見した途端、表示を止め文 書のエラーを通知します。

これは HTML5 の定義で再確認されました。 つまり、 HTML 5 の規格では HTML5 という定義と XHTML5 という XML に準拠 した定義の二つが定義されています。 そして、HTML5 の文書では、構文エラーなどに対して、なるべく表示をするよ うに指示されていますが、一方で、 XHTML5 では整形式のチェックでエラーに なった場合、エラーの通知を行うように明記されています ( HTML5 Draft 1.6 HTML vs XHTML)。

8-3. XML の DTD

内部サブセットに関する定義は次のようになっています。

[28a] DeclSep ::= PEReference | S
[28b] intSubset ::= (markupdecl | DeclSep)*
[29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl
                  | NotationDecl | PI | Comment

この記述が DTD と呼ばれる部分になります。 これを説明するのに、もう一度先に示した「講義情報」の例を XML 1.0 形式 に書き直したものを使って説明します。

例8-1

<class name="データ構造とアルゴリズム II" >
<teacher name="坂本直志" />
<time day="tue" period="2" />
</class>

要素型定義

要素型定義 elementdecl の定義形式は次の通りです。

[45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>'

[46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children 
[47] children    ::= (choice | seq) ('?' | '*' | '+')?
[48] cp          ::= (Name | choice | seq) ('?' | '*' | '+')?
[49] choice      ::= '(' S? cp ( S? '|' S? cp )+ S? ')'	
[50] seq         ::= '(' S? cp ( S? ',' S? cp )* S? ')'
[51] Mixed	 ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*'
			| '(' S? '#PCDATA' S? ')' 

これは要素が内容として何を含むかを記述するものです。 何も含まないか、子要素含むか、それとも EntityDecl で定義した実体を含む かなどを指定します。 例えば上記の例だと class は teacher を 1 つ以上、 time も一つ以上含みます。 一方、 teacher も time も要素を含みません。 これを表すと、次のようになります。

<!ELEMENT class (teacher+ , time+) >
<!ELEMENT teacher EMPTY >
<!ELEMENT time EMPTY >

class の宣言では children を使用し、さらに seq を使用しています。

なお、 Mixed では文字列で実際に記述されたデータか、他の要素を含むこと ができます。 このデータ型は #PCDATA になります。

属性型定義

属性型定義 AttlistDecl は次のように定義されています。

[52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'

まず、 ATTLIST 宣言で要素名を指定した後、属性の定義を連続させます。

[53] AttDef ::= S Name S AttType S DefaultDecl

属性の定義は、属性名と型とデフォルト値の指定です。

[54] AttType ::= StringType | TokenizedType | EnumeratedType
[55] StringType ::= 'CDATA'
[56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' 
                     | 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS'
[57] EnumeratedType ::= NotationType | Enumeration
[58] NotationType ::= 'NOTATION' S '(' S? Name 
                                    (S? '|' S? Name)* S? ')'
[59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
[7] Nmtoken ::= (NameChar)+

型には文字列型(CDATA)、トークン化型、列挙型があります。 トークン型は ID などあらかじめ定められている識別子を値に取るものです。 また、列挙型には NotationType と Enumeration が定義されていますが、こ こでは Enumeration について説明します。

Enumeration はカッコの中に特定の名前を表すトークンを縦棒で区切って列挙 するものです。 例えば、曜日を表したいときは次のようにします。


(mon|tue|wed|thu|fri|sat|sun)

なお、XML はこの場合、大文字と小文字を区別します。 そのため、曜日などを大文字小文字関係なく入れさせる場合は、可能な入力を全 て記述しなければなりません。

次に、属性の型指定の後に付加するものとして、属性を指定する制限を指定をします。

[60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue
[10] AttValue ::= '"' ([^<&"] | Reference)* '"'
                  |"'" ([^<&'] | Reference)* "'"
#REQUIERD
必ず指定しなければならない
#IMPLIED
任意
#FIXED
属性値を指定するときは必ず次に指定した値にする

上記の例に対しては次のように定義します。

<!ATTLIST class name CDATA #REQUIRED >
<!ATTLIST teacher name CDATA #REQUIRD >
<!ATTLIST time day (mon|tue|wed|thu|fri|sat|sun) #REQUIED
             period CDATA #REQUIED >

なおここで、時限を指定するのに実際は数値型が便利です。 しかし、 XML にはこのように属性に対しては列挙か CDATA を指定するしかあ りません。 したがって、これをアプリケーション側で読み込む場合は DTD によるチェッ クをしても、さらに数値のチェックなどをする必要があります。

実体定義

XML にも非終端記号や変数のような、特定の概念をまとめて名前をつける方法 があります。 これが Entity と呼ばれる指定です。

[70]   	EntityDecl	   ::=   	 GEDecl  | PEDecl
[71]   	GEDecl	   ::=   	'<!ENTITY' S Name S EntityDef S? '>'
[73]   	EntityDef	   ::=   	EntityValue | (ExternalID NDataDecl?)

GEDecl は汎用実体と呼ばれ、文書中などで指定した内容を名前で呼び出すも のです。

例8-2

下記のような定義をします。

<!Entity copyright "©Naoshi Sakamoto 2009">

すると文書中で &copyright; と書くと上記のように ©Naoshi Sakamoto 2009 と展開されます。

また、定義に外部の dtd ファイルを呼びたいときは、 SYSTEM キーワードま たは PUBLIC キーワードを使って URI を指定します。

<! Entity %romanI SYSTEM "roman.dtd">

一方 PEDecl はパラメータ実体と呼ばれています。 これは、 他の DTD の中だけで使用します。

[72]   	PEDecl	   ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
[74]   	PEDef	   ::= 	EntityValue | ExternalID 
[9]   	EntityValue::= '"' ([^%&"] | PEReference | Reference)* '"'
			|  "'" ([^%&'] | PEReference | Reference)* "'"

例8-3

<!ENTITY % daylist "(mon|tue|wed|thu|fri|sat|sun)">
<!ATTLIST time day %daylist; #REQUIED
             period CDATA #REQUIED >

なお、これも SYSTEM キーワードを使って外部 dtd ファイルも使用できます。

DTD 記法のまとめ

一つの要素だけの文書は XML 文書になり得ます。 しかし、 DOCTYPE 宣言を文書の前に置くことで、文書内の要素や属性の構成 を規定することができます。 DOCTYPE 宣言では、含むべき要素名とそれに関する DTD 宣言を指定します。 DTD 宣言を指定するには、 [](角カッコ)の中に直接記述するか、別ファイル に記述した後、 SYSTEM キーワードで URI を指定する方法があります。

DTD 宣言では、要素間の関係を示す ELEMENT 宣言と、属性を定義する ATTLIST 宣言の他、 実体宣言である ENTITY 宣言があります(このほか NOTATION 宣言があります が省略します)。

例8-4

<?xml version="1.0" encoding="Shift_JIS" standalone="yes" ?>
<!DOCTYPE class [ 
<!ELEMENT class (teacher+,time+)>
<!ATTLIST class name CDATA #REQUIRED>
<!ELEMENT teacher EMPTY>
<!ATTLIST teacher name CDATA #REQUIRED>
<!ELEMENT time EMPTY>
<!ENTITY % daylist "(mon|tue|wed|thu|fri|sat|sun)">
<!ATTLIST time day %daylist; #REQUIRED
  period CDATA #REQUIRED>
]>
<class name="データ構造とアルゴリズム II" >
<teacher name="坂本直志" />
<time day="tue" period="2" />
</class>

例8-5

class.dtd
<!ELEMENT class (teacher+,time+)>
<!ATTLIST class name CDATA #REQUIRED>
<!ELEMENT teacher EMPTY>
<!ATTLIST teacher name CDATA #REQUIRED>
<!ELEMENT time EMPTY>
<!ENTITY % daylist "(mon|tue|wed|thu|fri|sat|sun)">
<!ATTLIST time day %daylist; #REQUIRED
  period CDATA #REQUIRED>
XML文書
<?xml version="1.0" encoding="shift_jis" standalone="no" ?>
<!DOCTYPE class SYSTEM "class.dtd" >
<class name="データ構造とアルゴリズム II" >
<teacher name="坂本直志" />
<time day="tue" period="2" />
</class>

なお、DTD によるチェックを行うには次のプログラムが使用できます。 但し、最低限度のプログラムなので、正常な XML 文書に対しては何も出力し ません。 詳しくは SAX の章で解説します。


import java.io.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.validation.*;
class TestHandler extends DefaultHandler {
  public TestHandler(){}
  @Override
  public  void error(SAXParseException e) throws SAXException {
    System.out.println(e);
  }
}
class Rei {
  private static SAXParser getSAXParserWDTD() throws Exception {
    final SAXParserFactory parserFactory = SAXParserFactory.newInstance();
    parserFactory.setValidating(true);
    parserFactory.setNamespaceAware(true);
    return parserFactory.newSAXParser();
  }
  public static void main(String[] arg) throws Exception {
    final SAXParser parser = getSAXParserWDTD();
    parser.parse(new FileInputStream("sample1.xml",new TestHandler());
  }
}

8-4. XML Schema

XML Schema とは

DTD 宣言は SGML から来ているもので、独特の記述法になっています。 これらは DTD 独特の文法を使うため、習得するのにさらなる知識が必要です。 また、値のチェックをする際に数値などの指定はできず、属性値は常に CDATA か列挙型になります。

XML Schema は XML 文書の定義を XML 自体で定義するものです。 さらに、数値型など様々な型が定義されていて、チェックをするのに十分な機 能を持っています。 その上、 javax.xml.validation パッケージはこの XML Schema に対応してい ます。 そのため、 DTD ではできなかった、 XML 文書の定義のチェックが可能になり ます。

全体の構成

XML Schema の定義は次のような書式で行います。


<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:annotation>
  </xsd:annotation>
  <xsd:element name="要素名" type="型名" />
  <xsd:complexType name="型名">
...
  </xsd:complexType>
...
  <xsd:simpleType name="型名">
...
  </xsd:simpleType>
</xsd:schema>

親要素の宣言が xsd:schema の直下の xsd:element で行われます。 型の定義ですが、内部に他の要素を含む場合は complexType を、単なる文字 列などの場合は simpleType を使用します。 但し、 simpleType に関しても、値の上限下限など様々な制約条件を付加する ことができます。

xsd:element 要素

上記に示したように、 xsd:element は要素名と型名を宣言します。 型は改めて指定します。

xsd:simpleType 要素

数値型など他の要素を含まない型に関しては simpleType 宣言で指定します。 XML Schema では string, token, integer, int(32bit に制限された整数), double, dateTime(1999-05-31T13:20:00.000-05:00), date(1999-05-31); time(13:20:00.000, 13:20:00.000-05:00), Name, ID,NMTOKEN など様々な型が使えます。

また、さらに列挙型も作ることができます。 下記のような指定によります。


  <xsd:simpleType name="dayType">
    <xsd:restriction base="xsd:string">
      <xsd:enumeration value="mon"/>
      <xsd:enumeration value="tue"/>
      <xsd:enumeration value="wed"/>
      <xsd:enumeration value="thu"/>
      <xsd:enumeration value="fri"/>
      <xsd:enumeration value="sat"/>
      <xsd:enumeration value="sun"/>
    </xsd:restriction>
  </xsd:simpleType>

xsd:complexType 要素

complexType による指定では、内部に xsd:element や xsd:attribute 要素を 持ちます。 xsd:element に関しては xsd:sequence 要素で括ります。


  <xsd:complexType name="classType">
    <xsd:sequence>
      <xsd:element name="teacher" type="teacherType" />
      <xsd:element name="time" type="timeType" />
    </xsd:sequence>
    <xsd:attribute name="name" type="xsd:string"/>
  </xsd:complexType>

例8-6

前述した授業情報に関する XML Schema による定義は下記のようになります。


<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:annotation>
  </xsd:annotation>
  <xsd:element name="class" type="classType" />

  <xsd:complexType name="classType">
    <xsd:sequence>
      <xsd:element name="teacher" type="teacherType" />
      <xsd:element name="time" type="timeType" />
    </xsd:sequence>
    <xsd:attribute name="name" type="xsd:string"/>
  </xsd:complexType>

  <xsd:complexType name="teacherType">
    <xsd:attribute name="name" type="xsd:string"/>
  </xsd:complexType>

  <xsd:complexType name="timeType">
    <xsd:attribute name="day" type="dayType"/>
    <xsd:attribute name="period" type="xsd:int"/>
  </xsd:complexType>

  <xsd:simpleType name="dayType">
    <xsd:restriction base="xsd:string">
      <xsd:enumeration value="mon"/>
      <xsd:enumeration value="tue"/>
      <xsd:enumeration value="wed"/>
      <xsd:enumeration value="thu"/>
      <xsd:enumeration value="fri"/>
      <xsd:enumeration value="sat"/>
      <xsd:enumeration value="sun"/>
    </xsd:restriction>
  </xsd:simpleType>
</xsd:schema>

プログラムによる検証

XML Schema 1.0 による定義により XML 文書を検証するには javax.xml.parsers パッケージを使用します。 下記はその検証するプログラムの例です(エラーリカバリはおざなりです)。


import java.io.*;
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.transform.dom.*;
import org.w3c.dom.*;
import javax.xml.validation.*;
class Rei {
    private final static String xsdfile="class.xsd";
    private final static String xmlfile="sample.xml";
    private static Validator getValidator(String xsd) throws Exception {
	final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
	final Schema schema = sf.newSchema(new File(xsd));
	return schema.newValidator();
    }
    private static Document getDocument(InputStream is) throws Exception {
	final DocumentBuilderFactory factory
	    = DocumentBuilderFactory.newInstance();
	final DocumentBuilder builder = factory.newDocumentBuilder();
	return builder.parse(is);
    }
    public static void main(String[] arg) throws Exception {
	final Validator validator = getValidator(xsdfile);
	final Document doc = getDocument(new FileInputStream(xmlfile));
	try {
	    validator.validate(new DOMSource(doc));
	    System.out.println("Document validates fine.");
	} catch (org.xml.sax.SAXException e) {
	    System.out.println("Validation error: " + e.getMessage());
	}
    }
}

例8-6の XML Schema 宣言を class.xsd というファイルに納め、下記の XML 文 書を sample.xml というファイルに納めてから上記のプログラムを走らせると、 検証の結果を表示します。


<?xml version="1.0" encoding="shift_jis"?>
<class name="データ構造とアルゴリズム II" >
<teacher name="坂本直志" />
<time  day="tue" period="2" />
</class>

坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科