第8回 XML文書中の処理命令とCDATAセクション

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

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



処理命令とCDATAセクション
今回の主な内容
処理命令とCDATAセクション
Processing Instructions(PI=処理命令)
処理命令のEBNF表記による構文
処理命令は必ずアプリケーションプログラムに渡される
CDATA Sections (CDATAセクション)
CDATAセクション開始文字列のナゾ
CDATAセクションの構文を定義するEBNF
CDATAセクションの具体例

 前回「XMLで使ってもいい文字データとコメント」では、XML 1.0勧告の中で、「2 Documents(文書)」の中の「2.4 Character Data and Markup(文字データ及びマーク付け)」の途中から、「2.5 Comments(コメント)」までを解説した。

 今回はその続きとして、「2.6 Processing Instructions(PI=処理命令)」と「2.7 CDATA Sections (CDATAセクション)」を解説する。

 「2 Documents」は文書の基本的な構造について述べている章である。その中で、「2.6 Processing Instructions」は、XML文書の中でアプリケーションプログラムに対する命令を記述するために使用される。あまり利用頻度は多くないかもしれないが、スタイルシートをXML文書に関連付けるために使用する場合もあるので、無視して通ることはできない。

 「2.7 CDATA Sections」は、部分的にマーク付けを記述不可能にして、マーク付けに用いる特別な記号を普通の文字として記述可能にする。手動でXML文書を記述する場合に便利であるため、しばしば使用されるものである。XML文書を読むときには、知っておく必要がある構文である。

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

Processing Instructions(PI=処理命令)

 率直にいうと、処理命令は実際には利用頻度の低い機能である。通常、XML文書に記述される意味のある情報は、要素、属性、テキストなどの手段で記述される。それらは、スキーマ言語を用いて正しい並び順を厳密に定めることができ、通常はそれで十分である。そのうえで、それらに該当しない“処理命令”をあえて使う必要性はめったにない。まれに処理命令を使う状況として、スタイルシートの関連付けがある。W3C勧告のAssociating Style Sheets with XML documentsから実際に処理命令の例を1つ引用しておこう。

  <?xml-stylesheet href="mystyle.css" type="text/css"?>

 ご覧のとおり、“<?”で始まり、“?>”で終わるのが処理命令である。上記の例では、このXML文書のスタイルシートがCascading Style Sheets(text/css)で記述されており、所在は「mystyle.css」というURI参照によって示されている。ここでは属性のような構文が使われているが、これらは属性ではない。処理命令の内部に何を書くかはXML 1.0勧告では何も示されていない。理屈のうえではどんな構文でも使用することができる。それを以下の説明で確認してほしい。

 では、処理命令とはいったい何なのか。それがXML 1.0勧告の「2.6 Processing Instructions」の最初に書かれている、この1文で分かる。

[Definition: Processing instructions (PIs) allow documents to contain instructions for applications.]

 処理命令は、応用プログラムのための命令をXML文書に入れることを可能にする、という。つまり、処理命令は、XML文書によって表現されるべきデータそのものではなく、命令なのである。そして、その命令は、応用プログラムのためのものであって、XMLプロセッサのためのものではない、ということである。この言葉は、よく吟味する価値がある。

 例えば、XML 1.0勧告の一部ではないが、名前空間を指定する場合のことを考えてみよう。名前空間URIを接頭辞に関連付ける情報は属性として記述するが、これは処理命令で記述するようにした方がよかったという意見がある。しかし、名前空間はXMLプロセッサが処理するものであり、アプリケーションプログラムが処理するものではない。DOMを通して応用プログラムはXML文書の情報を得る段階で、すでに名前空間は解釈されている。いちいち、応用プログラムが属性を調べて解釈する必要はない。その意味で、名前空間を指定するために処理命令を使っていないのは、このような仕様に則して考えれば正しい選択であるといえる。

 とはいえ、それが本当に正しいのか、ということは分からない。というのは、いまの名前空間の仕様は、特権的な名前を持つ属性を使うため、属性の名前空間を指定するために属性を使うような状況になっているためだ。多数の属性が入り組むと、どれが名前空間を指定する属性で、どれが通常の属性なのか分かりにくいこともある。もしかしたら、名前空間URIと接頭辞を関連付ける処理命令を用意した方が、すっきり分かりやすかったという可能性も考えられる。しかし、あり得なかった架空の歴史のことを考えるのは、この連載の趣旨からいえば不毛である。

 もう1つ、この文章でさりげなく、Processing instructions (PIs)と書いてあるところに注目してみよう。処理命令(Processing instructions)の略称はPIs、単数形だとPIとなる。処理命令をPIと書くのは、問題ないだろう。

処理命令のEBNF表記による構文

 続きを読もう。次は処理命令のEBNF表記である(EBNFの詳細については、第2回「XML勧告読解に必須のEBNF」を参照)。

Processing Instructions
[16]    PI    ::=    '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
[17]    PITarget    ::=    Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))

 [16]のPIは、処理命令そのものの構文を定義するEBNFである。[17]のPITargetは、アプリケーションプログラムを特定する名前の構文を定義するEBNFである。この中で、ちょっとややこしいものについて説明をしておこう。まず、[16]に含まれる以下の部分である。

  (Char* - (Char* '?>' Char*))

 まず、Char*はすぐ分かると思うが、CharつまりXMLで使用できる任意の文字のゼロ回以上の繰り返しである。では、(Char* '?>' Char*)とは何だろうか? これは、「Charのゼロ回以上の繰り返し」+「'?>'」+「Charのゼロ回以上の繰り返し」を意味する。つまり、以下の文字列はすべて当てはまる。

  • AB?>CD
  • ?>CD
  • AB?>
  • ?>
  • AB?>CD?>EF

 5番目は「え?」と思った人がいるかもしれないが、Charの定義に“?”や“>”が含まれるので、これも当てはまることになるのである。当然、Char*という定義に上の5つがすべて当てはまる。

 では全体として(Char* - (Char* '?>' Char*))は、どんな意味になるのだろうか。これは、あらゆるCharのゼロ回以上の繰り返しの中から、(Char* '?>' Char*)に当てはまるものを除外するという意味である。上記のリストにあるような例は、除かれるということだ。

 つまり、以下のような処理命令は間違いということになる。

  • <?sample AB?>CD?>
  • <?sample ?>CD?>
  • <?sample AB?>?>
  • <?sample ?>?>
  • <?sample AB?>CD?>CD?>

 [17]のEBNFも見てみよう。ポイントになるのは(('X' | 'x') ('M' | 'm') ('L' | 'l'))という部分だろう。('X' | 'x')はXとxのどちらか1つを意味する。つまり、最初の1文字は“X”または“x”、2文字目は“M”または“m”、3文字目は“L”または“l”である。これをつなげれば、XMLでもxmlでもXmlでもxMLでもよい、ということを表現している。大文字小文字を問わずXMLという文字列、といえる。ならば、そう一言でいえといいたくなるかもしれないが、XMLは大文字と小文字を区別する前提の仕様なので、たまに大文字と小文字を区別したくない状況があると、定義が回りくどくなってしまうわけである。

 これにより、[17]は、Nameから大文字小文字を問わずXMLという文字列を取り除いたもの、という定義になっているわけである。しかし、この定義から明らかなとおり、X、M、Lという3文字の並びを含む名前が排除されているわけではなく、XMLという名前だけが排除されている。例えば、(具体的にそれが何かは筆者には分からないが)Mサイズの手紙をFAXで送信する応用プログラムで使うために、FAXMLETTERという名前の処理命令を作った場合、文字列の中にXMLという文字列が含まれるが、これは問題とはならない。FAXMLETTERは(('X' | 'x') ('M' | 'm') ('L' | 'l'))に当てはまらないからである。

処理命令は必ずアプリケーションプログラムに渡される

 では、XML 1.0勧告の続きを読もう。EBNFで説明した構文に関する説明と、規定である。

PIs are not part of the document's character data, but must be passed through to the application. The PI begins with a target (PITarget) used to identify the application to which the instruction is directed. The target names "XML", "xml", and so on are reserved for standardization in this or future versions of this specification. The XML Notation mechanism may be used for formal declaration of PI targets. Parameter entity references are not recognized within processing instructions.

 まず、処理命令の必要性だ。PIはXML文書の文字データの一部ではないが、アプリケーションプログラムに渡されなければならないとしている。コメントは渡さなくてもよかったが、処理命令は必ずアプリケーションプログラムに渡さねばならない。もちろん、アプリケーションプログラムが扱う命令が処理命令なのだから当たり前のことである。そういう当たり前のことも書くのが、こういう仕様書に要求された条件の1つである。

 次にいこう。PIは、命令が渡される応用プログラムを特定するために使用するターゲット(PITarget)で始まると記述されている。ターゲットはもちろん名前であるから、応用プログラムはターゲットを調べることで、それが自分が処理すべき命令かどうかを判断できる。しかし、たまたま同じ名前のターゲットが使われたらどうするのかという問題が生じる可能性がある。ならば、名前空間の仕様を併用すれば問題ないだろう、と思うかもしれないが、実は名前空間はターゲットには適用できない。ターゲットの名前にはコロンを含む名前を記述することができないのである。その点からも、処理命令の実用度には疑問が残る。おそらく、複数名前空間の要素の混在ができれば、特定の応用ソフト向けの命令であっても、固有の名前空間を持つ要素としてXML文書内に入れれば処理命令など使わなくても問題ない、ということなのだろう。

 さてその次である。ターゲット名“XML”、“xml”などは、この仕様の現在の版または将来の版の標準化のために予約する、としている。これは大文字小文字を問わずXMLという名前は予約されていることを意味する。将来の版はともかく、現在の版についても言及されているのは、少し奇異に思えるかもしれない。というのは、XMLに類する名前の処理命令についての定義は、この版のXML勧告には含まれていないためだ。しかし、処理命令によく似たXML宣言が存在していることを覚えているだろうか。この規定が存在するおかげで、xmlという名前の処理命令を記述することはできなくなる。これにより、“<?xml”で始まる記述はXML宣言しかありえないことになり、処理命令かもしれないという可能性を考えなくて済む。

 その次には、XMLの記法(notation)をPIのターゲットを宣言するために使用してもよいと書かれている。だが、記法はあまり使う機会がないので、あまり気にしなくてもよいだろう。XMLプロセッサを開発する場合は多少なりとも意識する必要があるかもしれないが、いずれにせよ単に名前をアプリケーションプログラムに渡すだけでよいと思われるので、それほど面倒な話にはならないだろう。少なくとも、XMLプロセッサが記法の意味を解釈する必要はないはずである。

 最後の1文である。パラメタ実体参照は、処理命令の内部では認識されない、と記述されている。EBNFを読むと、処理命令はDTDの内部でも記述できることが分かる。しかし、DTDの内部に記述された処理命令であっても、パラメタ実体参照は展開されないということを記述しているのである。

CDATA Sections (CDATAセクション)

 さて、処理命令の次は「2.7 CDATA Sections (CDATAセクション)」である。CDATAセクションは特別な必須機能というわけではない。実際、CDATAセクションを使用したXML文書を、ほとんど等価なCDATAセクションを使用していないXML文書に書き換えることができる。しかし、必ずしも逆は真ではなく、普通のテキストをCDATAセクションで書き直せない場合もある。例えば、UTF-8にはあるがシフトJISには含まれていない文字が記述されたテキストを、シフトJISで記述されたXML文書内のCDATAセクションに含めることはできない。

 そのような制約にかかわらずCDATAセクションという機能があるのは、以下のような状況を考えれば分かるだろう。例えば、テキストエディタでXML文書を作成中に、以下のような文字列をマーク付けではなく文字として含めたくなったとしよう。

  <a><b><c><d><e><f>g</f></e></d></c></b></a>

 もし、CDATAセクションを使わないなら、XML文書内で下記のように記述しなければならない。

 

&lt;a>&lt;b>&lt;c>&lt;d>&lt;e>&lt;f>g&lt;/f>&lt;/e>&lt;/d>&lt;/c>
&lt;/b>&lt;/a>

 だがCDATAセクションを使えば、こう書ける。

  <![CDATA[<a><b><c><d><e><f>g</f></e></d></c></b></a>]]>

 要するに、特別な文字列で前後をサンドイッチしてやれば、定義済み実体の参照を使用する ことなく記述可能となるわけで、これは便利である。しかし、XML 1.0勧告には、こう使うと便利だという話は書いていない。こう使うと便利だから入れた機能だ、という話が必ずしも含まれないところが、標準仕様の1つの盲点といえるかもしれない。それを補うためにいろいろな手があるが、JIS規格などには「解説」と呼ばれるものが「規格」に付随して書かれている。そこには、経緯や知っておくと役に立つ情報などが含まれることがある。

 W3Cの仕様書はJISほど厳格ではなく、解説的な文章が含まれていたりする。しかし、それにも限度というものがある。仕様書にはその背景にさまざまな文化や蓄積があるもので、書かれた文章だけで理解できない場合もあるだろう。

CDATAセクション開始文字列のナゾ

 それはさておき、XML 1.0勧告の続きを読んでいくことにしよう。まず、CDATA sections (CDATAセクション)という言葉の定義から始まる。

[Definition: CDATA sections may occur anywhere character data may occur; they are used to escape blocks of text containing characters which would otherwise be recognized as markup. CDATA sections begin with the string "<![CDATA[" and end with the string "]]>":]

 まず、最初の文は2つのことについて書かれている。CDATAセクションがどこに出現できるのか、ということと、CDATAセクションは何のために使用するのか、ということである。最初の方は、文字データが出現するところであれば、CDATAセクションはどこに出現してもよい、としている。しかし、これは少し冗長な説明である。CDATAセクションが出現してよい個所はEBNFで明記されているからだ。そして、EBNFの方が、より厳密であいまいさが少ない。さて、もう1つは、CDATAセクションの目的として、CDATAセクションで囲まなければマーク付けとして認識されてしまう文字を含むテキストを別扱いするのに使用する、と記述している。

 続いて、CDATAセクションは、文字列“<![CDATA[”で始まり、文字列“]]>”で終わる、と記述している。これも、後でEBNFによって同じことが繰り返し記述されているという意味で冗長である。

 余談だが、CDATAセクションの開始文字列“<![CDATA[”は、非常に唐突な文字列に見えるのではないだろうか。どうして“!”記号が付いているのだろうか、どうして“[”記号が2回も出てくるのだろうか。これらはSGMLとの互換性のため、というのが最も簡単な説明だろう。ほかの記述方法でCDATAセクションを実現することも不可能ではないが、SGMLとの互換性を維持するにはこうなる必然性があったということである。

 しかし、XMLの枠内でも、もうちょっと分かりやすく理解する手段はある。“<!”に続いてキーワードを書くのはDTDなどでよく見られる構文であり、それらと共通のルールであると考える説だ。例えば要素型宣言なら“<!ELEMENT”で書き始めるが、それと同じようなものと考えられる。しかし、CDATAセクションは“<![CDATA”で書き始めるので、“<!”とキーワードの間の“[”記号の存在が相違となる。

 実は“<!”とキーワードの間の“[”記号を記述する構文は、ほかに存在する。それがConditional Sections(条件付きセクション)である。これらは、CDATAではなくIGNOREやINCLUDEというキーワードを使用して記述するが、確かに“<!”とキーワードの間に“[”記号を記述する。つまり、CDATAセクションはこれら条件付きセクションと合わせて、1つの文法上のグループを成していると考えれば、少しは分かりやすくなるだろう。しかし、それは「そう思うこともできる」というだけで、同じと見なしてよいわけではない。CDATAセクションと条件付きセクションのEBNFをじっと比較して見つめると、空白文字を入れてよい場所に相違があることが分かる。そのため、やはりSGMLとの互換性のためにCDATAセクションの開始文字が決められている、と割り切って考えるのがいいかもしれない。

CDATAセクションの構文を定義するEBNF

 次はCDATAセクションのEBNFである。

CDATA Sections
[18]    CDSect    ::=    CDStart CData CDEnd
[19]    CDStart    ::=    '<![CDATA['
[20]    CData    ::=    (Char* - (Char* ']]>' Char*))
[21]    CDEnd    ::=    ']]>'

 ここはすでに説明した知識で容易に読み取れると思うので、詳しい説明は割愛する。そして、CDATAセクションについての説明が続く。

Within a CDATA section, only the CDEnd string is recognized as markup, so that left angle brackets and ampersands may occur in their literal form; they need not (and cannot) be escaped using "&lt;" and "&amp;". CDATA sections cannot nest.

 上記では、CDATAセクション内では文字列CDEndだけをマーク付けとして認識するので、不等号(より小)とアンパサンドは、そのままの形で出現してよいと記述してある。文字列CDEndとは、“CDEnd”という文字列ではなく、CDEndという名前のEBNF定義を意味する。つまり、“]]>”である。これだけが、マーク付けとして認識され、不等号(より小)とアンパサンドは、その機能を持った文字とは認識されないわけである。

 次に、“&lt;”と“&amp;”などを使用した特定の文字の特別扱いは必要ない(できない)と記述している。CDATAセクション内で認識されるマーク付けは“]]>”だけであるから、“&”で始まる定義済み実体の参照も書けず、“&lt;”や“&amp;”と書いて“<”と“&”を示すことはできない。続いて簡潔に、CDATAセクションは入れ子にはできない、と記述されている。これも当然のことといえる。というのも、“<”で始まるマーク付けであるCDATAセクションも、CDATAセクション内には記述できない。必然的にネストはできないことになる。

CDATAセクションの具体例

 次は、CDATAセクションを使った具体的な例が載っている。

An example of a CDATA section, in which "<greeting>" and "</greeting>" are recognized as character data, not markup:

<![CDATA[<greeting>Hello, world!</greeting>]]> 

 ここでは、“<greeting>”と“</greeting>”を、マーク付けではなく文字データとして認識するためのCDATAセクションの例であると記述されている。見てのとおりの例なので、詳しい説明は不要だろう。

 さて今回はここまでである。次回は、XML 1.0勧告の「2.8 Prolog and Document Type Declaration(前書き及び文書型宣言)」に説明を進める予定だ。

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


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

注目のテーマ

HTML5+UX 記事ランキング

本日月間