第 7 回 XML

本日の内容


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

7-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 とそれに基づいてマークアップされた文書の組で 表現されます。 これは、「文脈自由文法」と「ルール」と「そのルールにしたがった文字列」 との間の関係と同じようなものです。

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

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

XML のファミリー

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

DOMは Document Object Model と呼ばれるもので、 XML のデータ に対してプログラムがアクセスできるように、オブジェクトとして組み立てら れたデータ構造です。 アプリケーションは XML ファイルを DOM のファクトリに渡し、オブジェクト を得ます。 そして、そのオブジェクトへ階層的に getter を与えることで様々なデータに アクセスします。 また、 setter を用いてデータを更新し、ファイルに書き出すこともできます。

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

XSLT(XSL Transformations)は XSL(Extensible Stylesheet Language) の構成要素の一つ。 XML 文書を他のドキュメント形式に変換するなどのための変換用のマークアッ プ言語です。

Java と XML

7-2. XML の記法

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

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

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

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

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

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

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

また、属性に関しては同じ属性は二度現れてはいけないという制限があります。 例えば、授業担当者が複数存在したり、実施日が週に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.1 は要素名にも上記の例のように日本語が使えるようになりまし た。 しかし、いくら様々な国の言語の文字をユニコードで指定できるとはいえ、要 素名を見るだけで他言語のフォントをインストールしなくては確認できないよ うな状況になるためか、 XML1.1 は現在普及していません。 本講義でも説明上の例以外は、要素名に英数字を使います。

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

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

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

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

文書構造

その他の構文

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 要素から構成されてい ます。

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 を使用して、ネッ トワークを利用して行います。

整形式

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

7-3. XML の定義

DTD の書き方

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

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

これを説明するのに、もう一度先に示した「講義情報」の例を XML 1.0 形式 に書き直したものを使って説明します。

例7-1

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

要素型定義

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

[45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>'
[46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children 

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

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

なお、 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 を指定するしかあ りません。 したがって、これをアプリケーション側で読み込む場合は数値のチェックなど をする必要があります。

実体定義

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

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

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

例7-2

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

<!Entity copyright "©Naoshi Sakamoto 2009">

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

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

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

[72]   	PEDecl	   ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
[74]   	PEDef	   ::= 	EntityValue | ExternalID 
[9]   	EntityValue::= '"' ([^%&"] | PEReference | Reference)* '"'
			|  "'" ([^%&'] | PEReference | Reference)* "'"
例7-3
<!Entity % daylist "(mon|tue|wed|thu|fri|sat|sun)">
<!ATTLIST time day %daylist; #REQUIED
             period CDATA #REQUIED >

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

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 に制限された integer), 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>

例7-4

前述した授業情報に関する 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>

プログラムによる検証

javax.xml.parsers パッケージで XML Schema 1.0 宣言により、 XML 文書を 検証することができます。 下記はその検証するプログラムの例です(エラーリカバリが非常に適当です)。


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 {
    public static void main(String[] arg) throws Exception {
	SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
	Schema schema = sf.newSchema(new File("class.xsd"));
	Validator validator = schema.newValidator();
	DocumentBuilderFactory factory
	    = DocumentBuilderFactory.newInstance();
	DocumentBuilder builder = factory.newDocumentBuilder();
	Document doc = builder.parse(System.in);
	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());
	}
    }
}

上記の XML Schema 宣言を class.xsd というファイルに納め、下記の XML 文 書を標準入力から与えると、検証の結果を表示します。


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

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