連載
» 2002年03月20日 00時00分 公開

技術者のためのXML再入門(5):XML文書のカタチを決めるDTD

XML文書には、XML文法に従ってさえいればどんなタグを利用していても構わない「整形式のXML文書」と、あらかじめ決めておいた文書型に従って書かれた「妥当なXML文書」の2種類がある。今回は、DTDがなぜ必要なのか、DTDで何を規定できるのかを紹介していく。

[吉田稔,日本ユニテック]

 今回は、XML文書の文書型を設定するスキーマ言語の例として、DTDを紹介していく。

DTDの必要性

 例えば、行政機関に提出する申請書類には必ず定型フォーマットがあり、別のフォーマットの書類を持っていっても受理されないことがある。申請者がそれぞれ異なるフォーマットの申請書類を持っていったら、行政機関は処理に困るからだろう。

 同様のことはXMLの場合にもいえる。電子政府の申請書類や電子商取引の受発注文書をXMLで記述する場合でも、当然、やりとりするXML文書のデータフォーマット、すなわちタグ付けのルールをあらかじめ決めておかなければならない。

 そのときどんな名前の要素や属性を使うのか、要素同士がどのような入れ子関係になるのか、などのタグ付けルールを、人間やシステムが理解できる形式で表現するための言語をスキーマ記述言語と呼ぶ。DTD(Document Type Definition:文書型定義)は、XML 1.0で規定されているXMLのためのスキーマ記述言語だ。

 W3Cは、DTDに代わるさらに高度な記述が可能なスキーマ記述言語としてXML Schemaをすでに標準化している。しかし、現時点では、XML関連の規格書や実際に動いているXMLシステムにおいて、スキーマの記述にDTDを使用しているケースが依然として多い。そのため、XML技術者であれば簡単なDTDを読めるくらいの知識は必要だろう。そこで、本連載第5回は、DTDの読み方の基本を解説することにする。

DTDの基本的な構成

 まずは、あるマニュアルのスキーマを定義したDTDから、エッセンスだけを抜き出したサンプルをご覧いただきたい。

リスト1 DTDの基本構成 リスト1 DTDの基本構成

 このリストから、DTDは、“<!DOCTYPE ルート要素の名前 [”から“]>”までの、囲まれた領域の中で、XML文書の本体を構成するさまざまなパーツを宣言している、ということにお気付きいただけるだろう(上記サンプルでは、manualがルート要素の名前だ)。

<!DOCTYPE ルート要素の名前 [
  ……詳細な定義の列挙……
]>

 リストをさらに詳しく見ていくと、DTDが以下の4種類の宣言で構成されていることが分かる。

  • 要素型宣言(element type declaration)
  • 属性リスト宣言(attribute-list declaration)
  • エンティティ宣言(entity declaration)
  • 記法宣言(notation declaration)
<!DOCTYPE ルート要素の名前 [
  要素型宣言
  属性リスト宣言
  エンティティ宣言
  記法宣言
]>

 なお、これら4つの宣言は必須ではない。また、出現順序は問わない。従って、実際のDTDではこれらの宣言すべて、またはそのいくつかが適当な順序で並んでいることになる。いずれせよ、それぞれの宣言によって要素や属性などのタグ付けのルールが厳密に定義される。では、4つの宣言を1つ1つ取り上げて、DTDの基本的な読み方を解説しよう。

要素型宣言

 サンプルコードの中で“<!ELEMENT”で始まる宣言を要素型宣言という。要素型宣言の基本形は、以下のとおりだ。

<!ELEMENT 要素名 内容モデル>

 ここで内容モデルとは、開始タグと終了タグで囲まれた部分がどのような内容になっているかを宣言するところだ。要素型宣言は、要素に関して次のことを宣言する。

(1)要素名
(2)要素の親子関係
(3)要素が出現する順序
(4)要素が出現する回数

 (1)から(4)の宣言をどのように行うかを述べよう。

(1)要素名
 前述したとおり、宣言する要素の名前を“<!ELEMENT”に続いて記述する。サンプルコードの該当する部分を1つ抜き出してみる。

<!ELEMENT title (#PCDATA) >

 これは、titleという名前を持つ要素の宣言を行っているところだ。なお、内容モデルのところに“#PCDATA”というキーワードが出てくるが、これはtitle要素の内容がテキストであることを表している。

(2)要素の親子関係
 1つの要素型宣言は、1世代の要素に関して親子関係を表現する。例えば次のコードは、親要素であるmanual要素が、4つの子要素(title要素、 preface要素、 body要素、 index要素)を持つことを表現している。

<!ELEMENT manual (title, preface, body, index) >

 こうした要素型宣言を繰り返すことによって、次の階層の親子関係、さらにその次の階層の親子関係……を表現してゆく。

 開始タグと終了タグで囲まれた部分に、子要素もテキストデータもない空要素は、キーワード“EMPTY”を使って表現する。

<!ELEMENT image EMPTY >

 ある要素が、DTDで宣言されている任意の要素を子要素として持つ場合、キーワード“ANY”を使ってそのことを表現する。次の例では、body要素が任意の要素を子要素として持てることを表現している。

<!ELEMENT body ANY >

(3)出現順序
 ある要素が複数の子要素を持つ場合、子要素がどんな順序で出現するかを記述する必要がある。例えば、子要素が順番に出現する場合、子要素を“,”で区切って列挙する。

<!ELEMENT manual (title, preface, body, index) >

 この例は、manual要素の子要素がtitle要素、preface要素、body要素、index要素の順序に出現することを表現している。

<manual>
  <title> …… </title>
  <preface> …… </preface>
  <body> …… </body>
  <index> …… </index>)
</manual>

 宣言されている子要素のうち、いずれか1つだけが出現する場合、子要素を“|”で区切って表現する。

<!ELEMENT manual (title | preface) >

 この場合、次のいずれかの形を取る。

<manual>
  <title> …… <title>
</manual>

 または

<manual>
  <preface> …… </preface>
</manual>

(4)出現回数
 DTDは、子要素の出現回数も表現できる。manual要素が子要素titleを持つ場合、以下の4つのパターンを表現できる。

<!ELEMENT manual (title) >

 必ず1回出現するパターン

<!ELEMENT manual (title?) >

 0回または1回出現するパターン

<!ELEMENT manual (title+) >

 1回以上出現するパターン

<!ELEMENT manual (title*) >

 0回以上出現するパターン

 出現順序と出現回数の表現を組み合わせて表現することもできる。親要素がsectionで、子要素がpとfigureの場合、「p要素およびfigure要素が任意の順序で0回以上出現する」ことは以下のように記述する。

<!ELEMENT section (p | figure)* >

属性リスト宣言

  サンプルコードの中で“<!ATTLIST”で始まる宣言を属性リスト宣言という。属性リスト宣言の基本形は、以下のとおりだ。

<!ATTLIST 要素名 属性名 属性値の候補 "デフォルト値" >

 複数の属性を宣言したい場合、「属性名 属性値の候補 “デフォルト値”」を、空白で区切って繰り返す。

<!ATTLIST 要素名 属性名1 属性値の候補 "デフォルト値" 
属性名2 属性値の候補 "デフォルト値" 
属性名3 属性値の候補 "デフォルト値" >

 属性リスト宣言は、属性に関して次のことを宣言する。

(1)属性名
(2)属性値としてどんな値を持つか
(3)属性値のデフォルト値

(1)属性名
 以下に、サンプルコードにおける属性リスト宣言を抜き出した。

<!ATTLIST title id ID #REQUIRED >

 この宣言から、title要素がid属性を持つことが分かるだろう。

(2)属性値の候補
 属性として持てる値をデータ型で指定できる。指定に使用できるデータ型を表1に示す。

データ型 意味
CDATA 文字データ
ID 識別子
IDREF 識別子参照値。ID型の属性で指定された識別子への参照値
IDREFS 複数のIDREFの属性値
NMTOKEN 「名前トークン」というタイプの文字列
NMTOKENS 複数の「名前トークン」
ENTITY エンティティ参照値
ENTITIES 複数のエンティティ参照値
表1 属性値の候補として指定できるデータ型
(注) 「名前トークン」とは、開始文字が数字や記号でもよい文字列のこと。詳細は、XMLの仕様書を参照のこと。

 先ほどの属性リスト宣言では、属性idがID型の値を持つものとして宣言されている。

<!ATTLIST title id ID #REQUIRED >

 データ型ではなく、具体的な値を並べて属性値の候補を指定する列挙型の指定方法もある。以下の例では、report要素の属性securityは、“internal”または“public”という値を持つことが表現されている。なお、属性宣言の最後に出てくる“internal”は属性のデフォルト値だが、これは次項で説明する。

<!ATTLIST report security (internal|public) "internal" >

(3)デフォルト値の定義
 属性宣言の最後に属性値のデフォルト値を定義する。デフォルト値の定義の部分には、次の4種類のパターンを指定できる。

二重引用符あるいはアポストロフィーで囲んだ属性値
XMLの本体で属性が省略された場合に使用されるデフォルト値を書く。以下の例では、report要素のsecurity属性が省略されたときには、“internal”という文字データが指定されたと見なされる。

<!ATTLIST report security (internal|public) "internal" >

#REQUIRED
その属性を持つ要素が出現した場合、開始タグの中に必ず属性指定を書かなければならないことを表す。従って、デフォルト値はない。

<!ATTLIST report security CDATA #REQUIRED>

#IMPLIED
その属性指定が省略された場合、アプリケーションに何の属性値も渡されないことを意味する。

<!ATTLIST header shortname CDATA #IMPLIED>

#FIXED+二重引用符あるいはアポストロフィーでくくった属性値
属性値は固定され、そのデフォルト値以外の値を書くとエラーになる。何らかの理由で意図的に固定的な属性指定を行わせたい場合に使用する。

<!ATTLIST form method CDATA #FIXED "post">

エンティティ宣言

 XML文書の本体で同じ文字列が何度も出てくる場合、それをエンティティ(entity:実体)として宣言しておき、置換文字列として参照する方法がある。また、外部ファイルをエンティティとして宣言し、それを参照する方法もある。いずれにせよ、エンティティとして事前に定義することが必要だ。それをエンティティ宣言と呼ぶ。文字列をエンティティとして宣言する場合、以下のように記述する。

<!ENTITY エンティティ名 "置換される文字列">

 例えば、“World Wide Web Consortium”という文字列をW3Cというエンティティ名で宣言する場合、次のように記述する。

<!ENTITY W3C "World Wide Web Consortium" >

 一方、ファイル全体をエンティティ宣言する場合、次のように記述する。

<!ENTITY エンティティ名 SYSTEM "ファイル名">

 例えば、chapter1.xmlというファイルを、ch1というエンティティ名で宣言する場合、次のように記述する。

<!ENTITY ch1 SYSTEM "chapter1.xml">

 XML文書の本体の中でのエンティティ参照は、“&エンティティ名;”と記述する。

<document>
  <title>XML再入門</title>
  &ch1;
  &ch2;
</document>

 ここまでで述べたエンティティは、DTDでエンティティ宣言したものをXML文書の本体で利用するものだった。これら以外に、パラメータエンティティ(parameter entity:パラメータ実体)といって、DTDの中で参照するための特別なエンティティがある。

 DTDの要素宣言の中で、頻繁に“(#PCDATA | em | sup | sub | br)*”という文字列が出てくる場合を考えよう。同じ文字列を毎回、記述するよりもそれらを置換文字列にした方がはるかによい。この場合、その文字列を次のようにパラメータエンティティとして宣言しておく。

<!ENTITY % para.mix "(#PCDATA | em | sup | sub | br)*">

 パラメータエンティティ宣言では、“%”の後に空白を入れること、しかしこれをDTDの中で参照する場合は“%エンティティ名;”と記述して、“%”の後に空白を入れてはならないことに注意していただきたい。例えば、pという要素の要素型宣言で、上記のパラメータエンティティを参照するときには、以下のように記述する。

<!ELEMENT p (%para.mix;)>

 パラメータエンティティ宣言と異なり、パラメータエンティティ参照では“%”の後に空白を入れない。

記法宣言

 イメージデータや音声データなど、XML形式以外の外部ファイルを利用することもあるだろう。その場合、アプリケーションにそのファイルがどんな形式のデータかを通知する必要がある。データ形式を通知するために使用される名前を記法(notation)と呼び、記法は、記法宣言によって宣言する。記法宣言は、以下のように記述する。

<!NOTATION 記法名 SYSTEM "記法を特定するデータのURL">

 または

<!NOTATION 記法名 PUBLIC "公開識別子" "記法を特定するデータのURL">

 ただし、XML文法は、「記法を特定するデータのURL」や「公開識別子」で表されるのが何かは定めていない。アプリケーションとの約束によって解釈と利用法が決まる。

 サンプルコードで記法宣言をしている部分を以下に示す。CGM形式で記述されたイメージデータをCGMという記法で表現する場合、このような宣言になるだろう。

<!NOTATION CGM PUBLIC "ISO 8632/4//NOTATION clear text encoding//EN" "http://www.utj.co.jp/……">

 外部ファイルとして存在するDTDを利用する方法やDTDの内容を切り替えるための方法など、ここで述べた機能以外にも、DTDに関連した機能は多い。さらに詳しく知りたい場合は、XMLの仕様書や参考書をご覧いただきたい。

次回に扱うこと

 同じXML文書を複数のアプリケーションが処理する場合のように、複数のタグセットを混在させたいときがしばしば生じる。しかし、異なるタグセットがたまたま同じ要素名を使っていたら、混乱が生じてしまうだろう。それを回避する実に巧妙な方法が名前空間だ。次回は、名前空間の意味や記述方法、利用方法を説明しよう。



Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。