第11回 XMLの外部参照と非依存文書宣言

XML 1.0は、1998年にW3Cから勧告として公開された。当然中身は英語で、しかもEBNFと呼ばれる式によって重要な部分が記述してある。この連載では、XML 1.0を深く理解するために、そのXML 1.0勧告の最新版「Extensible Markup Language (XML) 1.0 (Second Edition)」をだれでも分かるように、やさしく読み解きながら解説していくことを目指している。(編集局)

川俣 晶
株式会社ピーデー
2003/7/2



XMLを限界まで使いこなす
今回の主な内容
XMLを限界まで使いこなす
外部サブセットの条件
外部サブセットのEBNF
外部サブセットと内部サブセットの違い
文書型宣言の2つの例
DTD使いこなしのテクニック
Standalone Document Declaration(非依存文書宣言)
非依存文書宣言のEBNF
ネットワーク配信には非依存文書を
非依存文書の例

 前々回前回と続けて、XML 1.0勧告の中の「2 Documents」(2 文書)の中の「2.8 Prolog and Document Type Declaration」(2.8 前書き及び文書型宣言)を解説してきたが、まだ解説していない部分が残っている。今回もその続きである。

 「2 Documents」は、XML文書の基本的な構造について述べている部分である。その中で、「2.8 Prolog and Document Type Declaration」(前書き及び文書型宣言)は、XML宣言や文書型宣言を扱っている部分である。

 今回は2.8 Prolog and Document Type Declarationの中でExternal Subset(外部サブセット)について解説する。第9回「XML宣言と文書型宣言」で述べたとおり、外部サブセットとは文書型宣言の外部の部分集合である。

 今回はそれに続いて、「2.9 Standalone Document Declaration」(2.9 非依存文書宣言)を説明する。これは、XMLを学ぶ者ならだれでも最初に知ることになるXML宣言の中に記述するものだが、実は相当XMLについて勉強してからでないと、正しい意味を理解できない難物である。実際、間違った解説を見掛ける場合もあるし、筆者も過去に間違えた経験がある。XMLを限界まで使いこなすために、しっかりと理解しておこう。

編集注:この連載では、XML 1.0勧告であるW3Cの「Extensible Markup Language (XML) 1.0 (Second Edition)」(英語)を参照し、その日本語訳として、日本工業規格 JIS X 4159:2002(リンク先は該当規格の原案ですが、最終版とほぼ同等の内容です)を参照しています。本文中のピンクの地の部分は、XML 1.0勧告の原文を示しています。

外部サブセットの条件

 前々回、前回の続きである、2.8 Prolog and Document Type Declaration(前書き及び文書型宣言)についての解説を続けよう。前回は、パラメタ実体についての話で終わったが、ここからは文書型宣言の外部の部分集合である外部サブセットについての話である。

Like the internal subset, the external subset and any external parameter entities referenced in a DeclSep must consist of a series of complete markup declarations of the types allowed by the non-terminal symbol markupdecl, interspersed with white space or parameter-entity references. However, portions of the contents of the external subset or of these external parameter entities may conditionally be ignored by using the conditional section construct; this is not allowed in the internal subset.

 「内部サブセットのときと同様に、外部サブセットと、DeclSepにおいて参照する任意の外部パラメタ実体とは、非終端記号markupdeclによって許される型の完全なマーク付け宣言をいくつか並べたものでなければならない。マーク付け宣言の間には、空白またはパラメタ実体参照を置いてもよい。」

 この文章のポイントは、DeclSepとmarkupdeclの指し示す意味である。これらは前回解説したが、もう一度詳しく見てみよう。

 DeclSepは、パラメタ実体参照(PEReference)または空白(S)を示す(前回参照)。これはおおむね、(空白を無視すれば)文書型宣言からパラメタ実体参照を行うものだと思えば間違いないだろう。つまり、「DeclSepにおいて参照する任意の外部パラメタ実体」という言葉は、間接的に「文書型宣言から行う外部パラメタ実体参照」という意味に相当する。

 一方のmarkupdeclはマーク付け宣言(前回参照)であり、それは要素型宣言や、属性リスト宣言などの総称である。つまり、「非終端記号markupdeclによって許される型の完全なマーク付け宣言」とは、「要素型宣言や、属性リスト宣言などが完全な形で含まれている宣言」という意味に相当する。

 これらを総合すれば、文章の意味が見えてくるだろう。それは、文書型宣言から行う外部パラメタ実体参照は、要素型宣言や、属性リスト宣言などが完全な形で含まれている宣言をいくつか並べたものでなければならない、というわけである。この規定はおそらく、整形式のXMLプロセッサを開発する際に意味があるのだろう。もし、内部サブセットしか処理しないXMLプロセッサを開発する際、外部サブセットを無視して処理を続けても構文解析が失敗しない保証になる。

外部サブセットのEBNF

 次は外部サブセットに関するEBNFの定義である(EBNFの詳細については、第2回「XML勧告読解に必須のEBNF」を参照)。

External Subset
[30]    extSubset    ::=    TextDecl? extSubsetDecl
[31]    extSubsetDecl    ::=    ( markupdecl | conditionalSect | DeclSep)* /* */

 まず、[30]のextSubsetである。これは、まさにExternal Subsetつまり外部実体の定義を示すものである。例えばファイルに保存する場合は、そのファイルそのものが、このextSubsetという文法に沿って最初から最後まで記述されている必要がある。逆にいえば、外部サブセットはXML文書の本体の一部とはならないわけである。XML文書本体の一部となるサブセットは内部サブセットである。

 extSubsetの内容は簡単である。まず、TextDeclがあり、次にextSubsetDeclがあるというだけのシンプルなものである。そして、TextDeclには後ろに“?”が付いているため、省略可能であることが分かる。

 では、TextDeclとextSubsetDeclとはいったい何だろうか? TextDeclの方は、Text Declaration(テキスト宣言)と呼ばれるもので、「4.3.1 The Text Declaration」(4.3.1 テキスト宣言)にて定義されているものである。これは、XML宣言と似た構文のもので、それが外部解析対象実体であることを示す印である。付けることがお約束であるもの、と思っても問題ないので、そういうものが付くのだということで話を先に進めよう。

 もう1つのextSubsetDeclは、その次に書かれた[31]の定義そのものである。

 [31]のextSubsetDeclは、外部サブセットの本体の内容そのものを定義している。その内容はというと、markupdeclまたはconditionalSectまたはDeclSepを0回以上繰り返したもの、となっている。いずれもすでに出てきたものである。markupdeclはマーク付け宣言である。conditionalSectは条件付きセクション、DeclSepはパラメタ実体参照(PEReference)または空白(S)を意味する。余談だが、markupdeclを要素型宣言や属性リスト宣言のようなものと思っていると、「外部サブセットにコメントは書けないのか?」と思ってしまうかもしれないが、コメントはmarkupdeclに含まれているので、このEBNF定義に沿って記述する場合には、コメントも書けることになる。

外部サブセットと内部サブセットの違い

 次は、EBNFに続く解説文である。

The external subset and external parameter entities also differ from the internal subset in that in them, parameter-entity references are permitted within markup declarations, not only between markup declarations.

 ここでは「外部サブセットおよび外部パラメタ実体の中では、パラメタ実体参照がマーク付け宣言のだけでなくマーク付け宣言の中でも認められる(編注:斜体は英文に対応)」としている。さらにこの点でも、「外部サブセットおよび外部パラメタ実体は内部サブセットと異なる」としている。これはどういうことだろうか。内部サブセットと外部サブセットでは、マーク付け宣言の中でパラメタ実体参照が認められる、認められない、という違いがあると説明しているが、それはどういうインパクトがあるのだろうか?

 例えば、以下はMathML2.0のDTDから、適当に引用してきたものである。

<!ATTLIST %mrow.qname;
      %MATHML.Common.attrib;
>

 これは1つの属性リスト宣言だが、2つのパラメタ実体参照を含んでいる。%mrow.qname;は、属性が付く要素名をパラメタ実体参照で定義していることを示している。なぜ、要素名を直接書かないのかというと、要素名が変更の対象になる可能性があるためだ。要素名をパラメタ実体参照で記述しておけば、パラメタ実体の宣言を1個所修正するだけで、要素型宣言とパラメタ実体宣言の両方が正しく同時に変更されることになる。また、DTDをカスタマイズして使う、という高等技を使う場合にも、パラメタ実体経由で定義してあると便利である。

 次に、%MATHML.Common.attrib;と記述されたパラメタ実体参照は、属性リストそのものをパラメタ実体経由で参照していることを意味する。この定義は名前から推測できるように、MathMLの多くの要素で共通(Common)に使われる属性の定義をパラメタ実体で参照しているものである。このような共通の定義は、何回も繰り返し書くことに意味はなく、逆に変更があったときにすべて適切に書き直すのは骨が折れ、間違いも入り込みやすくなる。これを回避するには、この例のように、パラメタ実体を使うと便利なのである。

 このようなテクニックが使えるのは外部サブセット内での話である。内部サブセットでは、マーク付け宣言の中でパラメタ実体参照を認めない。つまり、外部サブセットに記述された宣言をXML文書の内部にコピーして使えない可能性がある。

文書型宣言の2つの例

 さて、次は文書型宣言の例である。

An example of an XML document with a document type declaration:

<?xml version="1.0"?> 
<!DOCTYPE greeting SYSTEM "hello.dtd">
<greeting>Hello, world!</greeting>

The system identifier "hello.dtd" gives the address (a URI reference) of a DTD for the document.

 水色の地の部分が例で、その説明として「システム識別子“hello.dtd”が文書のDTDのアドレス(統一資源識別子(URI)参照)である」とある。要するに、この文書の文書型宣言が示すDTDは、hello.dtdというURIで示される、ということをいっているわけである。

 次に、もう1つ例が掲載されている。

The declarations can also be given locally, as in this example:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE greeting [
  <!ELEMENT greeting (#PCDATA)>
]>
<greeting>Hello, world!</greeting>

 「次の例のとおり、宣言を局所的に与えることもできる」と説明されている。局所的(locally)とは、文書の内部という意味である。

DTD使いこなしのテクニック

 次の段落はちょっと違う話題になる。さりげなく書かれているが、DTDの使いこなしのうえで重要な意味を持つ。

If both the external and internal subsets are used, the internal subset is considered to occur before the external subset. This has the effect that entity and attribute-list declarations in the internal subset take precedence over those in the external subset.

 まず、「外部サブセットおよび内部サブセットの両方を使用するときは、内部サブセットが外部サブセットより先に出現したと見なす」とある。続いて、「これは内部サブセットの実体および属性リスト宣言が、外部サブセットの実体および属性リスト宣言に優先するという効果をもたらす」という。これはどういうことだろうか。

 外部サブセットと内部サブセットは同時に使える。このとき、外部サブセットと内部サブセットの両方に同じ対象に対する宣言が含まれていてもよく、しかもそれは異なる内容でも構わない。そして、重複した場合は、内部サブセットの宣言が優先するというのである。

 もしかしたら今回の記事で、最も重要なトピックがこれかもしれない。これは、DTDを使いこなすうえで重要な意味を持っている。標準的なDTDが配布されているときに、それを少しだけカスタマイズして使いたい場合がある。そのような場合には、そのDTDを外部サブセットとして参照し、変更したい定義を内部サブセットとして書いておけば実現できることになる。

 このような使い方を前提にして、多くの定義をパラメタ実体参照にしているDTDも珍しくない。そういう事例を体験するために、パラメタ実体参照を大量に使っているベテランの作成したDTDをじっくり読んでみるのもよい経験になると思う。もっとも、パラメタ実体こそ問題を引き起こす諸悪の根元であるという批判も存在する。強力だが危険、という一面があるのも事実だろう。

Standalone Document Declaration(非依存文書宣言)

 XMLの入門で最初に出合うのが、XML宣言である。そして、多くの書籍では、XML宣言には、versionとencodingとstandaloneというキーワードを含めることができることを記述している。ところが、versionとencodingは初心者にもすぐ分かるものだが、standaloneは簡潔で分かりやすい言葉で説明した例を見たことがない。それもそのはず、これは相当XMLについて理解していないと分からない概念を扱うものなのである。処理を高度に最適化するための仕組みであるといえるが、高度であるが故に、初心者にはハードルが高いものとなっている。しかし、初心者を卒業して次のステップに進むなら、正しい意味を理解する必要がある。

 それでは、非依存文書宣言についての文章を読み始めよう。

Markup declarations can affect the content of the document, as passed from an XML processor to an application; examples are attribute defaults and entity declarations. The standalone document declaration, which may appear as a component of the XML declaration, signals whether or not there are such declarations which appear external to the document entity or in parameter entities. [Definition: An external markup declaration is defined as a markup declaration occurring in the external subset or in a parameter entity (external or internal, the latter being included because non-validating processors are not required to read them).]

 最初は一般的な話題から入っている。「XMLプロセサは、応用プログラムに文書の内容を渡すが、マーク付け宣言はこの内容に影響を与えることがある」とある。その一例が「属性のデフォルト値および実体宣言は影響を与える」だ。例えばDTDで、要素eに付く属性aのデフォルト値がdであると記述されているとする。そこで、このDTDを用いて<e />と記述したXML文書を処理すると、応用プログラムは属性aの値はdであるという情報を受け取る。XML文書には属性aに関する情報は何も記述されていないが、応用プログラムは書かれていない属性に関する情報を受け取るわけである。

 これは、貴族とボヘミアンの対立におけるPSVI(post-schema-validation infosets)とも関係する話題である。XML文書に書かれていない内容が応用プログラムに渡ることは好ましくないという意見もあるが、ここでは本筋から外れるので説明を割愛する。ここに現在のDTDやスキーマに対する論点の1つがあると頭の片隅に置いてもらえればいい(貴族とボヘミアンの対立については、「XMLにおける「ボヘミアンと貴族の階級闘争」を参照)。

 続いて、「非依存文書宣言はXML宣言の一部分として出現することができ、影響を与えるマーク付け宣言が文書実体の外部またはパラメタ実体の中に出現するかどうかを示す」としている。この文章は、非依存文書宣言の出現場所と役割について記述している。

 非依存文書宣言がXML宣言の一部分として出現することができる(may)ということは、詳しく説明する必要もないだろう。問題になるのは役割の方である。これは、「影響を与えるマーク付け宣言」が、「外部またはパラメタ実体の中に出現するかどうか」を示すとしている。「影響を与えるマーク付け宣言」というのは、XML文書に書かれていない内容が応用プログラムに渡ることに関係するマーク付け宣言という意味である。例えば、前述した属性のデフォルト値を記述した属性リスト宣言などがこれに当たる。そして、「外部またはパラメタ実体の中に出現するかどうか」というのは、XMLプロセッサが解析しないで読み飛ばす可能性のある部分を示している。

 つまり、非依存文書宣言は、すべてを処理するXMLプロセッサと、一部を読み飛ばすXMLプロセッサを使用した場合に、応用プログラムが同じ結果を受け取るかどうかを示す宣言であるといえる。もし、非依存文書宣言によって、外部サブセットやパラメタ実体によって応用プログラムが受け取る情報に変化がない、と示すことができれば、それらの処理を省略して高速化することができる。

 次は、external markup declaration(外部マーク付け宣言)という言葉の定義である。「外部マーク付け宣言は外部サブセットまたはパラメタ実体の中に出現するマーク付け宣言として定義される」としている。これは、説明のために定義された言葉であって、何か新しい宣言が増えたわけではない。要は、マーク付け宣言のことであるが、出現場所がこの文章で書かれた範囲内のものを、外部マーク付け宣言と呼ぶ、ということである。

 そのあと、括弧でくくって、以下の補足説明が続く。「パラメタ実体は、外部も内部も含むとしている。妥当性を検証しないプロセサが内部パラメタ実体を読むことを義務付けないために,内部パラメタ実体も含める」としている。ここに書かれているとおり、妥当性を検証しないプロセッサは内部パラメタ実体を読み飛ばす可能性があるので、すべての宣言が内部にある場合でも、非依存文書であると宣言できない場合があり得る、ということである。つまり、外部の情報を参照しているかどうか、ということと、非依存文書であるかどうか、という条件には直接的に関連性がない。外部の情報を参照していても非依存文書である場合があるし、外部の情報を参照していない文書が非依存文書ではない場合もある。このあたりが、非依存文書宣言を分かりにくくしている理由だろうか。

非依存文書宣言のEBNF

 次は非依存文書宣言のEBNFである。

Standalone Document Declaration
[32]    SDDecl    ::=    S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) [VC: Standalone Document Declaration]

 すでに多くのEBNFを読んできた読者には難しいことは何もないだろう。standalone='yes'やstandalone="no"といった内容を記述することができる。指定する値はyesかnoだけである。後で例が出てくるが、<?xml version="1.0" standalone='yes'?>というように、XML宣言に含める。

 次はより詳しい説明が続く。

In a standalone document declaration, the value "yes" indicates that there are no external markup declarations which affect the information passed from the XML processor to the application. The value "no" indicates that there are or may be such external markup declarations. Note that the standalone document declaration only denotes the presence of external declarations; the presence, in a document, of references to external entities, when those entities are internally declared, does not change its standalone status.

 「非依存文書宣言では,、値“yes”はXMLプロセサから応用プログラムへと渡される情報に影響する外部マーク付け宣言が存在しないことを意味する」としている。“yes”が非依存文書であるということを示す。「値“no”は、影響する外部マーク付け宣言が存在するか、または存在する可能性があることを意味する」としている。

 ここで、「存在するか、または存在する可能性がある」という表現に注意していただきたい。値“no”は「存在する」という断言ではない。非依存文書宣言は、処理を最適化できるかどうか判断するために使われるものであって、最適化できるかどうか分からないあいまいな状態は“yes”と断言できないので、“no”となるのだ。

 次に、注意書きがある。「非依存文書宣言は、外部宣言が存在するかどうかだけを示すことに注意すること。外部実体への参照が文書内に存在していても、その実体が内部で宣言されているときは文書の非依存の値には影響を与えない」としている。つまり、外部実体の参照の有無は、非依存文書宣言には影響を与えないということである。影響を与えるのは、外部宣言である。宣言が外部にあるかどうかは非依存文書宣言に影響を与えるが、宣言が指し示す対象が外部にあっても、それは影響を与えないということである。つまり、非依存文書宣言が“yes”であれば、一切の文書外のリソース抜きで処理可能、というわけではないのである。

ネットワーク配信には非依存文書を

 さらに非依存文書の説明が続く。

If there are no external markup declarations, the standalone document declaration has no meaning. If there are external markup declarations but there is no standalone document declaration, the value "no" is assumed.

 ここでは、「外部マーク付け宣言が存在しなければ非依存文書宣言は意味をもたない」としている。これは当然のことといえる。続けて、「外部マーク付け宣言は存在するが非依存文書宣言が存在しない場合は、“no”の値が設定されているものとする」とある。すでに述べたとおり、非依存文書であるかどうか分からない場合は“no”であるとすれば、外部にマーク付け宣言が存在する場合のデフォルト値が“no”であることは当然の結果といえる。

Any XML document for which standalone="no" holds can be converted algorithmically to a standalone document, which may be desirable for some network delivery applications.

 ここでは、「XML文書で standalone="no" が設定されているものは、あるアルゴリズムでstandalone="yes"の文書に変換できる」とある。要するに、外部マーク付け宣言を内部に取り込んでしまえばよいのである。パラメタ実体参照は参照される内容に置き換えてしまえばよい。属性のデフォルト値は、直接要素の属性として書き込むこともできるだろう。そういう操作によって、非依存ではない文書は、非依存文書に変換できる

 それに続けて「変換後の文書の方がネットワークによる配信には望ましい」としている。ネットワークで送る場合に参照される外部の宣言が必ずアクセス可能とは限らないし、可能であってもムダなトラフィックを増やすだけである。その点で、変換後の文書を送る方が望ましいのは、当然予想されることであろう。

非依存文書の例

 次は、EBNFの[32]に付けられた「妥当性制約:非依存文書宣言」についての説明である。

Validity constraint: Standalone Document Declaration

The standalone document declaration must have the value "no" if any external markup declarations contain declarations of:

  • attributes with default values, if elements to which these attributes apply appear in the document without specifications of values for these attributes, or

  • entities (other than amp, lt, gt, apos, quot), if references to those entities appear in the document, or

  • attributes with values subject to normalization, where the attribute appears in the document with a value which will change as a result of normalization, or

  • element types with element content, if white space occurs directly within any instance of those types.

 まず、「非依存文書宣言は、何らかの外部マーク付け宣言が次のいずれかを宣言しているときは、値“no”を取らなければならない」とある。これは、値が“no”でなければならない条件を厳密に定めたものだ。いうまでもなく、これらの条件を満たしていない場合でも非依存文書であるか明示されない場合は“no”として処理することになる。

 さて、具体的な条件は4つある。英語版では特に順序は付いていないが、JIS版では、aからdのアルファベットが振られている。

 最初のものは、 「デフォルト値付きの属性であって、この属性が適用される要素が属性値を指定せずに文書内に現れるもの」だ。すでに説明したデフォルト値付きの属性についての記述である。ここで注意すべきことは、デフォルト値付きの属性が存在する場合でも、属性値を指定せずに文書内に現れなければ、この条件に該当しないということである。すべての属性は、デフォルト値を頼らず、明示的に記述するという方針を取れば、この条件に該当することはなくなる。

 2番目は、「amp(&)、lt(<)、gt(>)、apos(')、quot(")以外の実体であって、その実体に対する参照が文書内に出現するもの」だ。amp、lt、gt、apos、quotは定義済み実体であり定義なしに使用できるので例外である。それ以外の実体は処理されない場合があるので、それらを使用すると非依存文書とは呼べなくなる。

 3番目は、「トークン化された型を持つ属性であって、文書中で出現するときの属性値は,この値から正規化が生成する値と、宣言がないとしたときに正規化が生成する値とが異なるもの」だ。“トークン化された型”という言葉が分かりにくいかもしれないが、これは属性の型に関係するものである。NMTOKENのような型と関係してくる。これらは、属性の型についての説明に進んだときに詳しく説明を行う。もう1つ属性値の正規化、というのもまだ説明していないややこしい話である。この先に、属性値の正規化というトピックがあるのだが、これを処理する際に宣言の有無によって結果が変わる可能性のあるケースがある。それについて言及しているものである。属性値の正規化の話題を読んだ後でここに戻ってきて読み直すと理解できるだろう。

 最後は、「要素内容を持つ要素型であって、空白がその要素型のいずれかのインスタンス内に直接現れるもの」としている。これは、内容に要素を持つ要素についての話題である。混合内容ではない要素であるといえる。要素内容は空白文字を内部に含んでもよいのだが、その空白が出現する場合は非依存文書にならない。

 そして例が1つ掲載されている。

An example XML declaration with a standalone document declaration:

<?xml version="1.0" standalone='yes'?>

 ここでは「非依存文書宣言付きのXML宣言の例を次に示す」として、1つの例が記述されている。

 さて「2.9 Standalone Document Declaration」(2.9 非依存文書宣言)はこれで終わりである。次回は、「2.10 White Space Handling」(2.10 空白の取扱い)を解説する予定である。空白文字は、だれでもXML文書を記述するときに使うもので、難解な非依存文書宣言に比べれば親しみやすいのではないかと思う。しかし、知らないと困る落とし穴もあるので、ぜひ読んでいただきたい。

連載 やさしく読む「XML 1.0勧告」


XML & SOA フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

HTML5+UX 記事ランキング

本日月間