第15回 文章系に必須の混合内容と属性リスト宣言
XML 1.0は、1998年にW3Cから勧告として公開された。当然中身は英語で、しかもEBNFと呼ばれる式によって重要な部分が記述してある。この連載では、XML 1.0を深く理解するために、そのXML 1.0勧告の最新版「Extensible Markup Language (XML) 1.0 (Second Edition)」をだれでも分かるように、やさしく読み解きながら解説していくことを目指している。(編集局)
川俣 晶
株式会社ピーデー
2003/11/11
■混合内容と属性リスト宣言を読む
今回の主な内容 混合内容と属性リスト宣言を読む 混合内容は子要素と文字データが混在する 混合内容の定義 混合内容宣言のEBNF定義 混合内容の例 独自の世界を持つ属性リスト宣言の役割 要素と属性が異なる役割を持つ例 属性宣言リストの内容 属性リスト宣言という言葉の定義 属性リスト宣言のEBNF定義 1つの要素型に複数の属性リスト宣言を記述 |
前回は、XML 1.0勧告の中で「3.2 Element Type Declarations(3.2 要素型宣言)」から「3.2.1 Element Content(3.2.1 要素内容)」までを読んだ。DTDに含まれる概念の多くは、RELAX NGやXML Schemaといった新しいスキーマ言語でもそのまま役立つものなので、これを学ぶことは意義があると述べたが、そのことは今回にも当てはまる。今回は「3.2.2 Mixed Content(3.2.2 混合内容)」から「3.3 Attribute-List Declarations(3.3 属性リスト宣言)」まで(「3.3.1 Attribute Types(3.3.1 属性の型)」の手前まで)を読む。
混合内容は、前回読んだ要素内容と対をなすものである。混合内容は、XMLをRDBMSの交換言語でしかないと思っている読者には、どうしてこんな機能があるかピンとこないかもしれないが、文章系のXML利用においては、混合内容は必須の重要な機能である。その辺りを、実例を交えて解説しているので、じっくり読んでいただけると幸いである。
属性リスト宣言は、これまでの要素の型を宣言する要素型宣言から離れて、属性に関する宣言に入る。DTDでは、要素と属性は扱いがかなり異なるので、その辺りを詳しく説明するように構成してみた。要素と属性の相違を知ることは、XMLで両者をどう使い分けるかを考えるうえでも価値があることだといえるだろう。
編集注:この連載では、XML 1.0勧告であるW3Cの「Extensible Markup Language (XML) 1.0 (Second Edition)」(英語)を参照し、その日本語訳として、日本工業規格 JIS X 4159:2002(リンク先は該当規格の原案ですが、最終版とほぼ同等の内容です)を参照しています。本文中のピンクの地の部分は、XML 1.0勧告の原文を示しています。 |
さて、「3.2.2 Mixed Content(3.2.2 混合内容)」から読み始めよう。混合内容は、前回解説した要素内容と対をなすものである。まずは、mixed content(混合内容)という言葉の定義から始まる。
[Definition: An element type has mixed content when elements of that type may contain character data, optionally interspersed with child elements.] In this case, the types of the child elements may be constrained, but not their order or their number of occurrences: |
[定義:ある要素型の要素が文字データを含んでもよく、文字データに子要素が混在しても構わないとき、その要素型は、混合内容を持つという。]この場合、子要素の型についての制約が存在してもよいが、子要素の順序または出現回数についての制約は存在しない: |
最初の文章は、mixed content(混合内容)の定義である。これによって、要素内容との違いは明らかだろう。要素内容は、必ず子要素だけを含み、文字データを含まない。例えば、要素aが混合内容を持つとき、文字データはどこに出現してもよいので、以下のような内容はすべて有効である。
<a>Text<b /><b /><b /></a>
<a><b />Text<b /><b /></a>
<a><b /><b />Text<b /></a>
<a><b /><b /><b />Text</a>
文字データの出現が許される場所は、特に1カ所に限定されないので、以下のように書いてもよい。
<a>Text<b />Text<b />Text<b />Text</a>
文字データを書かずに以下のように記述することも有効である。
<a><b /><b /><b /></a>
このような、どこにでも文字データが出現してよい表記は、XMLをRDBMSのデータ交換フォーマットと考えている場合には、混合内容の用途が分かりにくいかもしれない。しかし、(X)HTMLで、以下のように記述する場合、要素pは混合内容である必要がある。
<p>ここは<strong>強調</strong>する。</p>
次の文章は、mixed contentの定義の範囲から外れる。「子要素の型についての制約が存在してもよい」とは、p要素の子としてstrong要素は記述してもよいが、html要素を書いてはならない、という制約を課すことができる、という意味である。そして、「子要素の順序または出現回数についての制約は存在しない」とは、p要素の子として最初にem要素が出現し、次にstrong要素を出現させる、といった順番の制約や、strong要素を必ず1回だけ出現させる、といった回数の制約を課すことはできないことを意味する。これらの制約は、要素内容の場合は容易に課すことができるのだが、混合内容ではできない。
次は、混合内容宣言のEBNF定義である(EBNFの詳細については、「第2回 XML勧告読解に必須のEBNF」を参照)。
Mixed-content Declaration
|
混合内容宣言は、混合内容を宣言するもので、生成規則の51番、Mixedに対応する。これには、2つのVC(妥当性制約)が付いている。
まず、生成規則Mixedの内容だが、2つの表現のどちらかを選べることが分かる。最初の方は、開きカッコがあり、空白を記述することができ、'#PCDATA'を記述し、空白を記述することができ、省略可能な空白+'|'+省略可能な空白+Nameを0回以上繰り返すことができ、空白を記述することができ、閉じカッコと*記号を記述する。もう1つは、開きカッコがあり、空白を記述することができ、'#PCDATA'を記述し、空白を記述することができ、閉じカッコを記述する。生成規則Nameは、要素の型(名前)を示す。
この生成規則により、例えば、(#PCDATA|strong)*と記述することができ、これは混合内容であり、strong要素を子要素として記述することができる、という意図を表現している。ここでは#PCDATAという特別な名前を使っているが、もしこれが普通の要素の名前だとすれば、(名前|strong)*となって、要素内容のようにも見える。もし、これを要素内容として見ると、前の文章に出てきた「子要素の順序または出現回数についての制約は存在しない」という意図は、“|”と“*”という記号で表現されていると見ることもできる。しかし、ここでは、“|”ではなく“,”を使ったり、“*”ではなく“+”を使ったりすることは許されていない。混合内容は、必ず“|”と“*”を使って書かねばならない。その点で、混合内容は、要素内容とははっきり異なるものであり、別の生成規則が与えられているわけである。
さて、もう1つの選択は、'(' S? '#PCDATA' S? ')'という規則に従って、混合内容を記述する方法である。これは、要素内容に文字データだけを記述可能にするものである。つまり、子要素の記述は許さない。前の文章に、「子要素の型についての制約が存在してもよい」と書かれていたが、これは「すべての型の子要素に、記述してはならないと制約を課した」と考えることができる。例えば、「商品名」要素の内容には、文字データのみの記述を許し、子要素の記述を許さない場合、
<!ELEMENT 商品名 (#PCDATA)>
と記述することになる。
where the Names give the types of elements that may appear as children. The keyword #PCDATA derives historically from the term "parsed character data.". |
ここで、Nameは子として出現してもよい要素の型を示す。キーワード#PCDATAは、歴史的には「解析対象文字データ(parsed character data)」に由来する。 |
さて、生成規則の後の文章を見ていこう。最初の文は問題なく理解できるだろう。次は、XML勧告を読む読者にとって首をひねる文章かもしれない。#PCDATAというのは、かなり唐突に出てくる感じのある名前である。例えば、ここは#記号1文字でも問題なく意図を表現できそうな感じもある。しかし、SGMLとの互換性を維持するためには、SGMLで使われていた名前を変更するわけにはいかない。XML利用者は、意味を考えないで、こう書くものだと丸暗記しても何ら問題ないだろう。ちなみに、parsed character data(解析対象文字データ)とは、XML文書中の通常の文字データを意味する。これと逆の意味を持つ文字データは、解析対象外実体(unparsed entity)に含まれる文字データだろう。解析対象外実体については、「4 Physical Structures(物理構造)」に定義があるので、ここでは深く追求しないこととして、先に進もう。
さて、2つある妥当性制約のうち、Proper Group/PE Nesting(グループおよびパラメタ実体が厳密な入れ子をなしていること)は、「3.2.1 Element Content(3.2.1 要素内容)」に説明があるので、ここに説明はない。No Duplicate Types(要素型の重複の禁止)の説明は以下に続く。
The same name must not appear more than once in a single mixed-content declaration. |
妥当性制約:要素型の重複の禁止 |
これは、妥当性制約、No Duplicate Types(要素型の重複の禁止)の定義である。例えば、(#PCDATA|a|a)*というように、aという要素名を2回書くと誤りである、ということである。そもそも、このように記述することは無意味である。混合内容では、順番や回数を指定することはできないので、仮に(#PCDATA|a|a)*と記述することができたとしても、結果は(#PCDATA|a)*とまったく同じことになり、意味がない。
次は混合内容の例である。
Examples of mixed content declarations:
|
最初の例は、HTMLを意識していると思われるものだが、p要素の内容に、順番や回数を問わず、文字データ、a要素、ul要素、b要素、i要素、em要素を記述することを許している。とはいえ、これは実際のHTMLの定義とは少し異なっている。HTML4.01の場合、UL要素はブロック要素であって、P要素の子要素になることはない。とはいえ、HTMLと明示して書かれた例ではないので、間違いとはいえない。
2番目の例は、パラメタ実体を使って混合内容宣言を記述した例である。もちろん、混合内容宣言でパラメタ実体を使っても構わない。ただし、展開された結果が、混合内容宣言の正しい構文に一致している必要はある。拡張性などを考えれば、パラメタ実体を活用して混合内容宣言を記述することにはメリットがある。
最後の例は、文字データのみを内容に持つ混合内容宣言の例である。この場合は、最後に*記号が付かないことを確認しておこう。
次は、要素型宣言を離れて、「3.3 Attribute-List Declarations(3.3 属性リスト宣言)」に入っていく。つまり、要素に関する宣言から、属性に関する宣言に進むということである。
さて、要素と属性は、共通点もあるが、相違点もある。しかし、ある情報を要素の内容で表現するか、属性を使って表現するか悩むことは多いだろう。例えば、メンバーの名前とID番号をXML文書に書き込む際に、ID番号を属性として表現するように設計することもできる。以下のような感じである。
<メンバー id="N00001">山田太郎</メンバー>
属性ではなく、要素として表現することもできる。以下のような感じである。
<メンバー><id>N00001</id><名前>山田太郎</名前></メンバー>
これらは、どちらが正しいということではなく、どちらもあり得る書き方である。このことから考えると、要素と属性の相違は、それほど大きいものではないといえる。例えば、要素に関する定義を属性に関する定義に書き換える場合、キーワードelementをキーワードattributeに置き換える程度で、実現できてもよいと考えても筋が通っている。実際、新しい世代のXMLのスキーマ言語では、要素も属性も同じように扱える方向に進んでいる。例えば、RELAX NGでは、(とても大ざっぱで不完全ないい方をすれば)element要素をattribute要素に置き換えるだけで、要素として表現されていたものを、属性で表現するように変更することができる。
しかし、DTDは、そのような新しいスキーマ言語の世界とは隔絶した古い世界の存在である。DTDにおいて、要素と属性の間の距離は、驚くほど大きい。かつては、要素と属性の役割分担は確かにあって、どちらを使ってもよい、という状況はなかったのである。一例を挙げるならば、タグを取り去っても文章として読める、という原則があった。
以下の例を基に、いかに要素と属性の役割に違いがあったのか、その一部を紹介しよう。以下は、HTMLで記述された文章の一部を想定している。
<p>筆者の<strong>素晴ら</strong>しい与太話は<a href="http://mag.autumn.org">オータムマガジン</a>で読めます。</p>
さて、ここでstrong要素は強調を意味するが、どうして強調するテキストを属性ではなく、内容に書いているのだろうか。そして、a要素のhref属性は、どうして要素の内容ではなく、属性に書かれているのだろうか。それは、タグを取り去って文章として読める、という原則からすれば当然の選択であることが分かる。この例からすべてのタグを取り去ると、以下のようになる。
筆者の素晴らしい与太話はオータムマガジンで読めます。
では、もしstrong属性が強調するテキストを属性で表現し、a要素はリンク先を要素の内容として表現するとしたらどうなるだろうか。おそらく、以下のような感じになるだろう。
<p>筆者の<strong text="素晴ら" />しい与太話は<a><href>http://mag.autumn.org</href>オータムマガジン</a>で読めます。</p>
この文書からタグを取り去ると以下のようになって、日本語として自然に読むことができなくなる。
筆者のしい与太話はhttp://mag.autumn.orgオータムマガジンで読めます。
このような原則は、いまとなっては過去のものである。実際に、このような原則に沿っていてはうまく言語を設計できないこともあるし、このような原則に沿っていない実例も多くある。また文章ではない情報を記述したXML文書は、タグを取っても文章として自然に読めることを期待するのは無理である。そのほか、ルビなどの情報を要素で記述する場合、属性には要素を入れられないので、属性に書かれた文字列にルビを付けることができない、という問題も生じる。
そのような現在の動向はあるが、XMLのDTDは古いSGMLとの互換性の必要性の上に成り立っているため、そのような動向と無関係に、遠い過去の姿を現代に伝える姿を示している。
遠い過去の姿を保っているということは、要素と属性の在り方が、まったく異なっているということである。実際、要素は「3.2 Element Type Declarations(3.2 要素型宣言)」によって宣言されるが、属性は「3.3 Attribute-List Declarations(3.3 属性リスト宣言)」によって宣言される。要素は「型」の宣言であるのに、属性は「リスト」の宣言なのである。では属性に型はないのかというと、それは存在している。「3.3.1 Attribute Types(3.3.1 属性の型)」が、属性の型を定義しているが、それは要素の型とはまったく無縁の別の世界である。要素の型は要素の名前であるから、名前の数だけ要素型は生まれることになる。しかし、属性の型は、文字列型、トークン化型、列挙型と分けられ、名前によって決まる要素型とは違う世界なのである。ここから先は、要素型宣言に関する知識では理解できない別世界であることを理解してから先に進んでほしい。
では、そのような前置きはともかくとして、本文を読み進めよう。
Attributes are
used to associate name-value pairs with elements.
Attribute specifications may appear only within start-tags and empty-element
tags;
thus, the productions used to recognize them appear in 3.1
Start-Tags, End-Tags, and Empty-Element Tags. Attribute-list declarations
may be used:
|
属性は、名前および値の対を要素に関連付けるために用いる。「3.1 開始タグ、終了タグおよび空要素タグ」に示されている属性指定を認識するための生成規則に従って、属性指定は、開始タグまたは空要素タグ内でだけ可能とする。属性リスト宣言は、次の目的で用いる。
|
冒頭の文章は、XMLを知る者なら同然のことといえる。属性には名前と値がある。例えば、href="http://www.atmarkit.co.jp/"という属性があれば、hrefは属性の名前であり、http://www.atmarkit.co.jp/は属性の値である。この2つは常にペア(対)で現れる。属性が名前だけ、値だけで出現することはない。SGMLでは、名前だけで出現する属性があるのだが、XMLにはそういうものはない。そして、属性は要素に関連付けられるという。要素と関係ない属性はあり得ず、属性は必ずいずれかの要素に付いていることになる。実際、属性は開始タグまたは空要素タグの中に記述されるわけで、要素抜きで属性を記述することはできないことが次に書かれている。そして属性リスト宣言の目的としてリストがあり、3つの文章が並んでいる。
最初の文章は、属性リスト宣言が単独では存在できないことを意味する。必ず要素型があって、それに対して適用可能な属性を示すために使われるというわけである。もう1つ、集合(set)という言葉に注目しよう。属性リスト宣言は、複数の属性の集まりを宣言する。複数の属性がリストされているので、属性「リスト」宣言と呼ばれるわけである。この点で、常に1つの要素型を宣言する要素型宣言とは性格が異なっていることが分かるだろう。
2番目の文章にでてくる型(type)は、要素型とは関係のない属性の型である。詳しくは後で説明が出てくる。
最後の文章は、XML文書中に宣言された属性が記述されなかった場合に指定されたと見なされる属性値を指定可能とすることを意味する。余談だが、XML界における貴族とボヘミアンの対立で話題になるPSVI問題と関連するものである。
次は、属性リスト宣言という言葉の定義である。
[Definition: Attribute-list declarations specify the name, data type, and default value (if any) of each attribute associated with a given element type:] |
[定義:属性リスト宣言は、ある要素型と関連付けられた各属性に対し、名前、データ型および(存在すれば)デフォルト値を規定する:] |
類似の話が続くが、属性リスト宣言は、この文章に書かれた内容以上でも以下でもない、と定義されていることが分かるだろう。繰り返しになるが、ここでは「each attribute(各属性)」という部分に「each(各)」という文字が付いていることに注意しておこう。属性はリストとして記述され、1つの属性リスト宣言に複数あり得るのである。
次は、属性リスト宣言のEBNF定義である。
Attribute-list Declaration
|
The Name in the AttlistDecl rule is the type of an element. At user option, an XML processor may issue a warning if attributes are declared for an element type not itself declared, but this is not an error. The Name in the AttDef rule is the name of the attribute. |
AttlistDecl規則に含まれるNameは、要素型の名前とする。ユーザーのオプション指定によっては、宣言していない要素型に対して属性を宣言したならば、XMLプロセサは、警告を出してもよいが、これはエラーとはしない。AttDef規則におけるNameは、属性の名前とする。 |
生成規則52、AttlistDeclは属性リスト宣言そのものを示している。生成規則53、AttDefは、生成規則AttlistDeclで参照されている生成規則である。生成規則AttlistDeclが独立して別途書かれているのは、0回以上繰り返す範囲を分離して分かりやすくするためだろう。
まず、生成規則AttlistDeclから見ていこう。これは、'<!ATTLIST'
で始まり、空白を置き、要素の名前を書き、生成規則AttDefに適合する文字列を0回以上繰り返し、省略可能な空白を置き、'>'
で終わる。
生成規則AttDefは、空白を置き、属性の名前を書き、空白を置き、生成規則AttTypeに適合する文字列を書き、空白を置き、生成規則DefaultDeclに適合する文字列を書き、終わる。生成規則AttTypeは属性の型を記述する生成規則で、この後に出てくる。生成規則DefaultDeclは、属性のデフォルト値を指定する生成規則である。これは、生成規則AttTypeのさらに後に出てくる。
本当ならここで実際の属性リスト宣言の例をお見せして、それと生成規則を比較しながら理解していただきたいところなのだが、生成規則AttTypeと生成規則DefaultDeclを知らずに読み取ることは難しいので、もっと後に実例が出てくるまで、属性リスト宣言の例は待っていただきたい。
さて、その後に文章が続いている。要素型の名前とは要素の名前と理解してもよい。つまり、属性リスト宣言は、要素型の名前によって、要素型宣言に関連付けられるわけである。DTD内で、属性リスト宣言と要素型宣言は必ずしも並べて書く必要はなく、要素型の名前によって、離れた場所に書かれていても関連付けられる。
次は、おそらく高度なDTDカスタマイズを想定している規定だろう。例えば、オプショナルな一群の属性リスト宣言をDTDに追加して使う場合、その中に要素型宣言が存在しない要素型に対する属性リスト宣言が含まれているかもしれない。しかし、それをXML文書から使うことがなければ、そのような不完全な宣言が存在することは何ら問題ではなく、それは許容される、ということだろう。
この後に、もう1つ文章があるが、これは書かれたとおりの意味である。
次は、1つの要素型に対して、複数の属性リスト宣言を記述した場合についての規定である。
When more than one AttlistDecl is provided for a given element type, the contents of all those provided are merged. When more than one definition is provided for the same attribute of a given element type, the first declaration is binding and later declarations are ignored. For interoperability, writers of DTDs may choose to provide at most one attribute-list declaration for a given element type, at most one attribute definition for a given attribute name in an attribute-list declaration, and at least one attribute definition in each attribute-list declaration. For interoperability, an XML processor may at user option issue a warning when more than one attribute-list declaration is provided for a given element type, or more than one attribute definition is provided for a given attribute, but this is not an error. |
ある要素に対して、複数のAttlistDeclを与える場合、これらすべての内容はマージする。ある要素型の同じ属性に、複数の定義を与える場合には、最初の宣言を有効とし、ほかの宣言は無視する。相互運用性のためには、DTDの作成者は、ある要素型にはたかだか1つの属性リスト宣言しか与えない、ある属性名にはたかだか1つの属性定義しか与えない、およびすべての属性リスト宣言には少なくとも1つの属性定義を与える、という選択をしてもよい。相互運用性のためには、XMLプロセサは、ユーザーのオプション指定によっては、ある要素型に複数の属性リスト宣言を与えたり、ある属性に複数の属性定義を与えたりしたときに、警告を出してもよいが、これは、エラーとはしない。 |
まず、AttlistDeclとは属性リスト宣言そのものの生成規則であるから、ある要素に対する属性リスト宣言は複数あってよく、それらは1つにまとめられると述べている。例えば、要素Aにa、b、cという3つの属性があるという属性リスト宣言と、要素Aにd、eという2つの属性があるという属性リスト宣言がある場合、これらは合成され、要素Aにa、b、c、d、eという5つの属性があると見なされるわけである。
このように、複数の属性リスト宣言を許すことは、DTDをカスタマイズして使う場合に有効である。例えば、外部サブセットとして使用している既存のDTDに存在しない属性を使えるようにするために、DTDを書き換えずに、内部サブセットに属性リスト宣言を付け加えることができる。
次の文章は、同じ属性に関する規定である。例えば、要素Aにaという属性がある属性リスト宣言と、同じく要素Aにaという属性があるという属性リスト宣言がある場合、有効になるのは最初の宣言ということになる。これだけ読むと、同じ属性の宣言なら、どちらが有効でもよいではないかと思うかもしれないが、属性には属性固有の型やデフォルト値があり、同じ名前の宣言ならすべて同じというわけではないのである。属性の型やデフォルト値の話は後から出てくる。
次の「相互運用性のためには」とは、「拘束力は持たない推奨事項。ISO 8879へのWebSGML適用附属書以前から存在するSGMLプロセッサが、XML文書を処理できる可能性を高めるために取り入れるもの」というものであった。現実の問題として、あまり意識する必要のある話ではないと思うが、このような配慮をして属性リスト宣言を記述すると、古いSGML処理系でも処理できる可能性が高まるということである。ここで書かれている3つの条件は、
- ある要素型には1つの属性リスト宣言しか与えない、つまり複数の属性リスト宣言を記述しないこと
- ある属性名には1つの属性定義しか与えない、つまり、属性名の重複がないこと
- すべての属性リスト宣言には少なくとも1つの属性定義を与える、つまり、属性リスト宣言に1つも属性に関する宣言が存在しない状況を避ける
ということである。
次も上の文章と同じく、「相互運用性のため」である。ある要素型に複数の属性リスト宣言を与えた場合や、ある属性に複数の属性定義を与えた場合に、警告を出すオプションを指定してもよい、ということである。しかし、これをエラーとはしない、というのがXML勧告の選択である。
◇
さて、今回はこれで終わりである。次回は、いよいよ属性リスト宣言の詳細に入っていく。最初は、「3.3.1 Attribute Types(3.3.1 属性の型)」である。属性の型が、いかに要素の型と違うか、じっくりと取り組んでいきたいと思う。このような要素と属性の間の型の相違は、DTDに特徴的なものであるが、それ故に学ぶ必要はないと思うのは早計である。これらの型の多くは、新しい世代のスキーマ言語にも継承されているのである。ぜひ、じっくりと学んでいこう。
連載 やさしく読む「XML 1.0勧告」 |
- QAフレームワーク:仕様ガイドラインが勧告に昇格 (2005/10/21)
データベースの急速なXML対応に後押しされてか、9月に入って「XQuery」や「XPath」に関係したドラフトが一気に11本も更新された - XML勧告を記述するXMLspecとは何か (2005/10/12)
「XML 1.0勧告」はXMLspec DTDで記述され、XSLTによって生成されている。これはXMLが本当に役立っている具体的な証である - 文字符号化方式にまつわるジレンマ (2005/9/13)
文字符号化方式(UTF-8、シフトJISなど)を自動検出するには、ニワトリと卵の関係にあるジレンマを解消する仕組みが必要となる - XMLキー管理仕様(XKMS 2.0)が勧告に昇格 (2005/8/16)
セキュリティ関連のXML仕様に進展あり。また、日本発の新しいXMLソフトウェアアーキテクチャ「xfy technology」の詳細も紹介する
|
|