XMLマスターポイントレッスン ~ ベーシック編 ~
第3回 DTDの基礎

インフォテリア認定教育センター 森田浩美 MORITA, Hiromi

前回は、XMLで最も基本的かつ重要である「要素」について解説しました。XML文書は、要素の内容に子要素を記述することで階層構造を実現できるため、さまざまなデータの保管/転送形式として使用されます。しかし、XML文書には、あらかじめ階層構造や要素名などを定義するための「設計図(スキーマ)」が必要となります。今回は、このXMLの設計図(スキーマ)について説明します。ベーシックV2試験のセクション3「DTD」を攻略する上での基礎知識ともなりますので、しっかりと学習しておきましょう。

XML文書の設計図の必要性

XML文書は、現在さまざまな用途で利用されています。発注書、請求書、見積書、出張報告書、議事録、会計書類、マニュアルなど、日常業務で利用するデータはもとより、最近では、日記、家計簿(お小遣い帳、娯楽の収支表)など個人ベースのデータにもXMLが利用され始めています。XMLは自由に要素名や階層構造を記述できるため、どんなデータもXML形式で作成できます。

ここでは、XML文書をただ単にXMLの文法に従って記述した場合(整形式XML文書)と、システム間のデータフォーマットとして、あるいは他社との共通データフォーマットとして使用することを考えて記述した場合について考えてみましょう。

例えば、「X株式会社のYさんに送付する発注書をXML文書で作成してください」と業務指示があったとき、みなさんならどのようなXML文書を作成しますか?図1に、ある3人が作成したXML文書の例があります。Aさんは要素名を日本語で記述しました。Bさんは要素名を英字で適当に記述し、Cさんは要素名を日本語で階層構造を利用して作成しました。

図1:3人が作成したXML文書の例

単純に「X株式会社のYさんに送付する発注書をXML文書で作成してください」という業務指示のみならば、いろいろなパターンのXML文書が考えられます。例で挙げた3人の考えたXML文書も間違いではありません。みなさんが考えたXML文書も、発注書として必要な情報が記載されていれば正しいXML文書と言えるでしょう。

しかし、Yさんの気持ちになって考えてみるとどうでしょうか。Yさんは、3人が作成したXML文書、そしてみなさんの作成したXML文書を受け取り、発注処理を行ないます。図1のように要素名や階層構造の異なるXML文書を受け取ったYさんは、すべてのXML文書をエディタで開いて、必要事項が記述されているかを確認して発注処理を行なう必要があります。これでは、発注処理はいつまで経っても自動化できず、毎回人手を介して発注処理を行なわなければいけません。

そこで、もし送付されたすべてのXML文書で要素名や階層構造が同じだった場合はどうでしょうか?要素名や階層構造が決まっていればそのXML文書を取り扱うシステム構築を行なうことで、Yさんは毎回受け取ったXML文書の内容をチェックする必要がなく、業務の自動化が実現できます。

このように、統一された要素名や階層構造のXML文書を受け取る、もしくは作成するために必要となるのが「設計図(スキーマ)」です。RDBの世界でもテーブルを設計する際、各項目(列)のデータ型やデータの大きさ、主キーの設定、他のテーブルとの関連付けなどのスキーマを定義することがありますよね。XML文書のスキーマでは、要素名や出現順序、出現回数を記述します。ある特定の用途でXMLを使用する場合はあらかじめスキーマを定義しておき、そのスキーマに沿ってXML文書を作成します。これにより、誰でも同じ要素名、階層構造
を持ったXML文書を作成できるようになるのです。

先ほどの業務指示をもう一度考えてみましょう。Yさんは、あらかじめXML文書を送付してくる相手(A、B、Cさん)へ発注書(XML文書)のスキーマ文書を送付します。そして、A、B、Cさんは受け取ったスキーマ文書を元にXML文書を作成してYさんへ送付します。3人が送付するXML文書の要素名と階層構造はまったく同じです。

一方のYさんは、送られてきたXML文書の記述をチェックするXMLパーサーで、スキーマどおりにXML文書が記述されているかをチェックできるため、いちいちファイルを開いて正しい要素名で記述されているか、階層構造が正しく記述されているかという

チェック作業を行なう必要はありません。したがって、Yさんの業務削減が行なえる、すなわちYさんが一番「得」をするわけですね。

XML文書のスキーマの種類

XML文書のスキーマにはいろいろな種類があります。図2のような文章形式で記述した設計図も1つのスキーマと言えますが、文章形式では人によって解釈の違いが出てくる可能性があります。したがって、XML文書のスキーマでは、一般的にスキーマ定義言語を使用して記述します。スキーマ定義言語は、スキーマを記述する専用の定義言語で、人による解釈の違いがありません。

図2:文書形式のスキーマ

発注書XMLのスキーマ
[1] ルート要素は「orderform」とする
[2] 「orderform」の内容は「customer」「goods」要素がこの順番で出現する。
「customer」は1回、「goods」は0回以上何回出現してもよい
[3] 「customer」の内容は「name」「address」「tel」要素の順番で1回出現する
[4] 「name」「address」の内容は文字列とする
[5] 「tel」の内容は「portable」「home」要素のどちらかが出現する
[6] 「portable」「home」の内容は文字列とする
[7] 「goods」の内容は「goods_name」「num」要素の順番で1回出現する
[8] 「goods_name」の内容は文字列とする
[9] 「num」の内容は数値とする

スキーマ定義言語は1つではなく複数存在します。XML 1.0の仕様で定義されているスキーマ定義言語は「DTD(Document Type Definition)」ですが、このDTDよりさらに厳密に定義できるスキーマ定義言語として、W3Cが定めている「XML Schema」があります。このほかにも、ベンダからさまざまなスキーマ定義言語が定義されています。

DTDにおけるスキーマ定義

DTDでは、XML文書を構成する主要項目の宣言を記述します。宣言は次の4つに分類されます。

●要素型宣言
●属性リスト宣言
●実体宣言
●記法宣言

ここでは、一番重要となる「要素型宣言」について説明します。

要素型宣言は、XML文書に含まれる要素の宣言を行ないます。要素型宣言の構文は図3のとおりです。要素名には要素の名前を記述し、内容モデルには宣言する要素の内容の構造を定義します。

図3:要素型宣言の構文

要素型宣言で重要なのが内容モデルです。要素の内容が文字列や数値(文字データ)なのか、または子要素のみが出現するのか(要素内容)などを定義します。要素型宣言を記述するときの注意点は、XML文書に出現するすべての要素について宣言を行なう必要があり、1つの要素について1つの要素型宣言を記述します。また、要素名が同じで内容モデルが異なる要素型宣言の記述はエラーとなります。

内容が文字列や数値の場合(文字データの場合)

要素の内容が文字列や数値の場合、内容モデルは#PCDATAを指定し、「(#PCDATA)」と記述します。データベースでは、テーブル設計時に列ごとに数値型、文字列型と指定できますが、DTDではこれらの指定ができません。

例えば、図2の「[8] 「goods_name」の内容は文字列とする」の要素型宣言を記述する場合は、次のとおりです。

<!ELEMENT goods_name (#PCDATA)>

この要素型宣言に従った正しい要素の記述は、<goods_name>television</goods_name>です。<goods_name><abc/></goods_name>のような子要素を記述した場合はエラーとなります。

また、図2の「[9] 「num」の内容は数値とする」の要素型宣言を記述する場合は、次のようになります。

<!ELEMENT num (#PCDATA)>

この定義に対して正しい要素の記述は、<num>10</num>です。先述したとおり、DTDでは要素の内容が文字列でも数値でも#PCDATAと指定するため、<num>yamada</num>も正しい記述です。要素の内容が数値であるかどうかのチェックはアプリケーション側で行なう必要があります。

内容が子要素の場合

要素の内容に子要素が出現する場合、内容モデルには出現する子要素の要素名を指定します。しかし、そのほかに子要素の出現順序や出現回数を定義する必要あります。

出現順序の定義方法

子要素が複数ある場合は、出現順序を指定します。出現順序の記述方法は2つあります。子要素名と子要素名の間に「,」を指定すると「記述された順序で出現する」という定義に、「|」を指定すると「いずれかが出現する」という定義になります(表1)。

表1:出現順序の指定とその定義

, 記述された順序で出現
| いずれかが出現

例えば、図2の「[7] 「goods」の内容は「goods_name」「num」要素の順番で1回出現する」の要素型宣言の記述は、次のようになります。

<!ELEMENT goods (goods_name,num)>

そして、この定義に対して正しい要素の記述は、次のとおりです。

<goods>
  <goods_name>television</goods_name>
  <num>10</num>
</goods>

「,」は「記述された順序で出現する」という定義なので、図4の記述例1~3はエラーです。

また、図2「[5] 「tel」の内容は「portable」「home」要素のどちらかが出現する」の要素型宣言の記述は、次のとおりです。

<!ELEMENT tel (portable|home)>

なお、portable要素とhome要素を両方記述した場合はエラーとなります(図4の記述例4)。

図4:誤った要素の記述法

出現回数の定義方法

内容モデルには、子要素名と出現順序のほかに出現回数を定義します。出現回数は、3種類の記号「*」「+」「?」を指定します。*は「0回以上無制限に出現可能」、+は「1回以上無制限に出現可能」、?は「0回か1回の出現可能」を意味します。

先ほどの要素型宣言の記述例(<!ELEMENT goods ( goods_name,num)>)では、出現回数の記号を指定していないけど……。と不思議に思われた方もいらっしゃいますよね。このように、出現回数の記号を指定していない場合は、「必ず1回出現する」という指定になります(表2)。また、DTDでは「出現回数を3回にしたい」「出現
回数を2回から5回にしたい」といった厳密な出現回数の指定を行なうことができません。

表2:出現回数の指定と定義

0回以上無制限に出現可能
1回以上無制限に出現可能
0回または1回
指定なし 1回

例えば、図2の「[2] 「orderform」の内容に「customer」「goods」要素の順番で出現する。「customer」は1回、「goods」は0回以上何回出現してもよい」の要素型宣言の記述は、次のようになります。

<!ELEMENT orderform (customer,goods*)>

いかがでしたでしょうか? ここでは、要素型宣言の一番重要となる項目に的を絞って説明しました。今までの記述例を参考に、すべての要素について記述してみましょう。実際に作成する場合は、拡張子を「dtd」としてください。図2の項目をDTDで記述したスキーマ文書を図5に示します。また、スキーマ文書に従ったXML文書もぜひ作成してみてください(LIST1)。

図5:図2の発注書のスキーマ文書完成例

LIST1:DTDに従った正しいXML文書

orderform.xml
<!DOCTYPE orderform SYSTEM "order.dtd">
<orderform>
 <customer>
  <name>yamada</name>
  <address>Tokyo</address>
  <tel>
   <portable>**********</portable>
  </tel>
 </customer>
 <goods>
  <goods_name>washing machine</goods_name>
    <num>1</num>
 </goods>
 <goods>
  <goods_name>television</goods_name>
  <num>2</num>
 </goods>
</orderform>

XML文書とスキーマ文書を関連付ける宣言

LIST1の先頭に記述されている<!DOCTYPE・・・>は「文書型宣言」と呼ばれるもので、XML文書の構造を定義するDTDを指定します。記述方法は2種類あり、文書型宣言内に要素型宣言や各種宣言を記述する「内部サブセット」と、今回のように外部ファイルに要素型宣言や各種宣言を記述する「外部サブセット」があります。今回は、外部サブセットの記述方法を説明します。

文書型宣言の記述場所には決まりがあり、ルート要素の開始タグの上に記述します。文書型宣言の構文は図6のように記述し、ルート要素名とファイル名を指定します。

図6:文書型宣言の構文

XML文書の検証

スキーマ文書とXML文書が作成できたら、作成したXML文書がスキーマ文書どおりに作成されているかを検証してみましょう。この検証は人間が目で見て確認したり、検証用プログラムを作成したりといった必要がなく、XMLパーサーで行なうことができます。

前回、XML文書が正しく記述されているかどうかをInternet Explorer(以下、IE)で確認する方法を説明しましたが、IEに組み込まれているXMLパーサーはXML文書がスキーマ文書に従って記述されているかどうかの検証を行ないませんので、検証用ツールを使用します。なお、検証用ツールのインストールや操作方法については、コラム「検証用ツールのインストール」を参照してください。

では、作成したXML文書とスキーマ文書で検証してみましょう。

まず、作成したXML文書をIEで開きます。正しくXML文書が表示された場合は、XMLの文法に従った正しいXML文書であると判断できます。もしエラーが表示された場合は、XML文書を確認しましょう。ただし、この時点のエラーはスキーマ文書と関係あるものではありません。

正しく表示できたら、次にブラウザ上で右クリックして表示されるメニューから「ValidateXML」をクリックしてみましょう。以下のダイアログボックスが表示された場合は、スキーマ文書に従ったXML文書である(XML仕様では「妥当なXML文書」と言います)と判断できます。

図:ダイアログボックス(スキーマ文書である)

次に、LIST2のXML文書を作成し、同じように操作してみましょう。どのようなダイアログボックスが表示されましたか?もし、以下のようなダイアログボックスが表示された場合は、スキーマ文書に従ったXML文書ではありません。

図:ダイアログボックス(スキーマ文書でない)

なぜ、このようなエラーが発生するのかと言うと、LIST2のXML文書は、スキーマ文書(図2)では「customer」の内容は「name」「address」「tel」要素の順番で1回出現する、と定義されているのに対し、XML文書でtel要素が出現していないためです。ダイアログボックスのエラーメッセージを手がかりに前後の記述を確認して、修正しましょう。

LIST2:DTDに従っていないXML文書

LIST2:DTDに従っていないXML文書

効率よくXML 文書を作成するための宣言

前回、要素の内容には「<」や「&」をそのまま記述できないため、定義済み実体参照を使用しましょうと説明しました。<calculation>a1<b2</calculation>の記述はエラーとなるため、<calculation>a1&lt;b2</calculation>と記述するのでしたね。このような定義済み実体参照は、XML 1.0仕様では5種類定義されています(表3)。

DTDの実体宣言を使用すると、この5種類以外に自分で実体参照を定義できます。

表3:定義済み実体参照

実体 実体名 表記方法
lt &lt;
gt &gt;
& amp &amp;
" quot &quot;
' apos &apos;

実体宣言を利用するメリット

実体宣言を使用すると、以下の2つのことを実現できます。

置換文字列定義によるXML文書作成/変更の作業効率が向上

1つは、定義済み実体参照のような置換文字列を自分で定義できることです(XML 1.0仕様では「内部実体」と言います)。例えば、長い文字データがXML文書の中で何度も出現する場合は、実体宣言を定義しておき、定義した実体名を使用することで記述量を削減したり、タイプミスを防ぐことができます。また、文字データに変更が発生する場合も、すべての箇所の変更や確認などの作業を行なう必要がなく、実体宣言の定義のみを変更することで参照している部分は変更されます。

外部ファイル取り込みによる作業分散化

もう1つは、実体宣言を使用して外部ファイルを取り込めることです(XML 1.0仕様では「外部実体」と言います)。例えば、複数人で大きなXML文書を作成する場合があります。このような場合、作業の際に1つのファイルを奪い合うことが予想されます。ファイルが奪われている間は、作業が中断してしまうからです。

しかし、実体宣言を使用すると各々が担当部分を別ファイルとしてXML文書を作成し、元になるXML文書に取り込むことができます。取り込むと言うと、通常はコピー/貼り付けをイメージしますが、実体宣言の場合はコピー/貼り付けといった操作は必要ありません。

今回は、置換文字列を定義する方法を説明します。実体宣言の構文は図7のとおりです。XML文書の中で使用する場合は、定義済み実体参照と同じく「&実体名;」と指定します。

図7:実体宣言の構文

実体宣言の定義とXML文書での使用例を以下に示します。

●goods_name.dtd
<!ELEMENT goods_name (#PCDATA)>
<!ENTITY TV "television">

●goods.xml
<!DOCTYPE goods_name SYSTEM "goods_name.dtd">
<goods_name>&TV;</goods_name>

作成できたら、IEで開いてみましょう。XMLパーサーにより「&TV;」が置換され、ブラウザには次のように表示されます。

<goods_name>television</goods_name>

今月の確認問題(セクション3:DTD)

スキーマ定義で重要となるDTDの要素型宣言と実体宣言は理解していただけましたでしょうか?ここで、今回解説した内容が理解できたかどうか、確認問題で試してみましょう。

問題1

「血液型」要素の子要素は、「A」「B」「AB」「O」要素のいずれかを出現させたい。正しい要素型宣言を選択してください。

  1. <!ELEMENT 血液型(A|B|AB|O)>
  2. <!ELEMENT 血液型(A?B?AB?O)>
  3. <!ELEMENT 血液型(A,B,AB,O)>
  4. <!ELEMENT 血液型(A + B + AB + O)>

解説

子要素の出現において、「いずれかの出現」を指定するには「|」を使用します。「,」は「記述された順序で出現」を表わし、「?」「+」は出現回数を表わします。したがって、Aが正解です。

問題2

要素型宣言について正しい説明文を選択してください。

  1. <!ELEMENT sample(data1|data2)>
    sample要素の子要素は、data1とdata2の
    両方出現する可能性がある
  2. <!ELEMENT sample(data1,data2)>
    sample要素の子要素は、data1とdata2が
    必ず出現するが、出現順序は問わない
  3. <!ELEMENT sample(data1|data2*)>
    sample要素の子要素は、data1とdata2は
    0回以上複数回出現する可能性がある
  4. <!ELEMENT sample(data1, data2*)>
    sample要素の子要素は、data1が最初に必ず
    1回出現し、その後data2が0回以上出現する

解説

Aの「|」は「いずれか出現」の指定です。両方出現する可能性という記述が誤りです。Bの「,」は「記述された順序で出現」の指定です。出現順序は問わないという記述が誤りです。Cのdata1には出現回数を表わす記号が付いていないため、出現回数は1回です。したがって、data1とdata2は0回以上複数回出現するという記述が誤りです。Dの「,」は「記述された順序で出現」、「*」は0回以上の出現回数の指定ですので、説明文は正しいです。したがって、Dが正解です。

問題3

以下のDTDとXML文書から、ブラウザ上で表示される結果として正しい記述を選択してください。

●DTD
<!ELEMENT DB(#PCDATA)>
<!ENTITY DB "DB Magazine">

●XML
<DB>&DB;</DB>

  1. 実体宣言では、外部ファイルを取り込むことしかできない。置換文字列は定義済み実体参照の5つのみであり、独自には定義できない
  2. 要素型宣言のDBと実体宣言のDBの名前が重複しているためエラーとなる
  3. <DB>DB Magazine</DB>
  4. <DB>&DB;</DB>

解説

実体宣言では、独自に置換文字列が定義できます。したがってAは誤りです。また、要素型宣言と実体宣言では宣言の種類が異なるため、エラーにはなりません。したがってBは誤りです。ブラウザで表示した場合には、実体参照は展開されます。したがってCが正解です。

* * *

今回は、DTDの中から要素型宣言と実体宣言について解説しました。このほかにも、DTDには属性を定義するための「属性リスト宣言」や画像などのXMLパーサーでは処理できないデータを実体として利用するための「記法宣言」があります。今回は説明しませんでしたが、これらについてもしっかりと押さえておきましょう。

次回は、第2回と第3回のまとめとして、整形式XML 文書と妥当なXML文書の違いについて説明します。


森田浩美(もりたひろみ)

株式会社日立システムアンドサービス人財教育部に勤務。現在、インフォテリア認定トレーナーとしてXMLの講師や社内でJavaの講師を担当。本厄突入!ということでお払いに行こうと思っていたら、A課長に「厄を落とすと周りに影響する」と言われ、どっちが良いのか真剣に迷い中。


<掲載> P.212-217 DB Magazine 2006 April

ページトップへ▲

XMLマスターポイントレッスン ~ ベーシック編 ~ 記事一覧へ戻る

HOMEへ戻る