Standaloneと究極の外部非依存文書:XMLを学ぼう(9)
ある文書がXML文書であることを示すのがXML宣言だ。このXML宣言の中には、通常あまり解説されないキーワードが含まれている。それがStandaloneだ。今回はこのStandaloneが意味するところは何なのか、ということを解説する。やさしいけれど奥が深い、そんなXMLの世界が見えてくる。
入門書では解説できないこと
前回(「再利用できる言語をXMLで作る」)は、かなり複雑高度で抽象的な解説だったので、「うへっ!」と両手をあげてしまった人もいるだろう。大切なことなので、あえて難しいことも書いたが、それは前回限りとしよう。今回は、XMLの基本的な問題に立ち返って、だれもが抱く疑問について考えてみよう。今回の主役は、standaloneである。
■XML宣言は解説されるけれど
どのようなXMLの入門書でも、読み始めてすぐにXML宣言というものが紹介される。例えば、以下のような例が書かれ、このような宣言をXML文書の先頭に書くという流儀と、その価値が説明される。
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
ここで、「<?xml」と「?>」は、この文書がXML文書であることを明確に主張させる効果があることは、簡単に理解できる。これが最初の行にあればXML文書だと、自動判定するソフトを作成するのも簡単だ。「version="1.0"」は単なるお約束で、特定の機能はない。しかし、見て分かるとおり、もし将来XML 2.0が登場したときには、ここに、2.0を書くだろうという推測は容易にできる。「encoding=」は、シフトJISやEUC-JPというさまざまな文字エンコーディングが世の中に混在していて、間違うと文字化けを引き起こすという知識があれば、XML宣言に明示することで、文字化け防止の効果があることが理解できるだろう。
では、最後に出てくる「standalone="yes"」とは、一体どんな機能を持っているのだろうか。これについて説明していない入門書も珍しくないし、説明してあっても、説明が複雑になってしまうケースが多く、読者の立場から見て、明快に「分かった!」といえるかというと、少し難しい。
それは入門書の著者に説明能力が足りないのではない。もともと、standaloneというのは、深く突っ込んでいくと、複雑精緻なXMLのメカニズムの非常にデリケートな問題と直結しているのである。そのため、XMLとは何かということを深く把握しなければ、なかなか明快に「分かった!」とは叫べないのである。それが、この連載で9回目にしてやっとstandaloneが登場する理由でもある。
実体についてあらためて考える
実体については、この連載ですでに解説済みである(「第5回 DTDを読んでみよう」参照)。しかしそのときは、実体を参照する際に使用する実体の複雑な絡まり合い(例えば<の定義など)を解説することが主なテーマであって、standaloneとのかかわりについては、触れていなかった。
ここでは、より深く実体に関して解説をしてみよう。
以前の解説では、実体には、通常の実体のほかに、パラメータ実体があるという説明を行った。だが、実は、これは非常に大ざっぱな分類なのである。厳密に実体を分類すると、以下のようなバリエーションが存在する。
- パラメータ実体(parameter entity)
- 内部実体(internal entity)
- 外部解析対象実体(external parsed entity)
- 解析対象外実体(unparsed entity)
パラメータ実体は、連載第5回で説明したように、DTD内部でマクロ定義のように機能する実体である。内部実体は、連載第5回で説明したような、直接的に文字列に置き換えられる実体である。この2つはすでに説明済みなので、詳しくは説明しない。問題は残りの2つである。
外部解析対象実体とは何だ
外部解析対象実体というのは、XML文書のツリーのフラグメント(断片)を別のファイルに置き、それを参照する実体である。例えば、以下のようなXML文書があったとする。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY hoge SYSTEM "sample002.xml"> ]> <test>&hoge;</test>
この3行目で規定されている実体は、外部解析対象実体である。SYSTEMキーワードの後ろに記述された文字列が、参照される外部解析対象実体の所在を表している。つまり、sample002.xmlというファイルがあり、そこに、hogeと名付けられた実体の本体が存在していることを示している。以下が、sample002.xmlの内容であったとしよう。
<?xml version="1.0" encoding="UTF-8"?> <strong>hello!</strong>
この2つのファイルを合わせると以下のXML文書と等価になる。
<?xml version="1.0" encoding="UTF-8"?> <test><strong>hello!</strong></test>
ここで、参照される実体のファイル名を、あっさりとsample002.xmlと名付けてしまったが、このファイルは厳密にいうと、XML文書ではない。一見、XML文書のように見えるが、実はそうではない。XML文書ではないとすると、これは何かというと、そのものズバリ、外部解析対象実体というのがその名である。もちろん、外部解析対象実体も、XMLの仕様書で書式が規定されているものである。
■外部解析対象実体はXML文書とは違う
外部解析対象実体の基本的な書き方は、XML文書と変わることはない。XML文書の一部が別のファイルに書かれているようなものだと考えればそれでよい。しかし、以下の点でXML文書と相違がある。
まず、ファイルの先頭にあるのは、XML宣言ではなく、テキスト宣言と呼ばれる。一見、同じ「<?xml」で始まる文字列であり、そっくり同じに見えるが、テキスト宣言にはstandaloneに関する書式が存在しない。その理由は、今回の説明を最後まで読めば、「なるほど、それは当然だ」と理解できるだろう。「standalone="yes"」の文書が、外部のファイルを参照することはあり得ないので、yesとnoを区別するまでもないのである。なお、テキスト宣言はオプションなので書いても書かなくてもよい。
もう1つの相違は、そのファイルの中で最上位になる唯一の要素という考え方が存在しないことである。XML文書なら、そのような唯一の要素がルート要素として存在するのだが、外部解析対象実体には、そのようなものはない。外部解析対象実体に記述できる内容は、要素の開始タグと終了タグの間に記述できる内容に等しい。つまり、文字を書いてもよいし、要素を書いてもよいし、実体の参照を書いてもよいし、CDATAセクションを書いてもよいし、処理命令(PI)を書いてもよいし、もちろんコメントを書いてもよい。それらを幾つでも連ねて書くことができる。
■XML文書としては間違いでもよい!
具体的な例を挙げよう。以下の例は、いずれも、XML文書としては正しくないものである。しかし、外部解析対象実体としては正しい内容を持っているものである。
以下の例は、XML文書として見るとルート要素が複数あるのでエラーになる。
<?xml version="1.0" encoding="UTF-8"?> <strong>Hello!</strong> <strong>XML!</strong> <strong>World!</strong>
以下の例は、XML文書として見るとルート要素がなく、文字列だけなのでエラーになる。
<?xml version="1.0" encoding="UTF-8"?> Hello!
以下の例は、XML文書として見るとルート要素の前後に文字列があるのでエラーになる。
<?xml version="1.0" encoding="UTF-8"?> Hello! <strong>XML!</strong> World!
このことから分かるとおり、実は、この世界には、XML文書によく似ていながら、XML文書ではない、外部解析対象実体というものが存在していて、それが利用されることがある。この点は、XML利用上の注意点である。
解析対象外実体の重要性は……
XML仕様書を、最初から最後まで読破しようとしたとき、最大の障害物となるのが解析対象外実体ではないかと感じる。その理由は2つある。まず、XML仕様書に登場する多くの機能や概念は、HTMLなどでもおなじみのものが多いのに、解析対象外実体はそうではないことがある。多少なりともHTMLをかじったことがあれば、要素や属性に関する説明でつまずいたりしないだろう。だが、解析対象外実体は、HTMLで使うことのない概念であり機能である。
もう1つの理由は、圧倒的な情報量の不足である。XML仕様書の中には、HTMLでおなじみではない機能も多く含まれている。XML宣言などはその典型的な例といえる。しかし、仕様書の中で詳しく説明が書かれているため、ここで戸惑うことはないだろう。ところが、解析対象外実体に関しては、書式は書いてあるものの、具体的にそれが何をするための機能であるのかが、明確に書かれておらず、非常に分かりにくい。ここでは、この分かりにくい解析対象外実体を、もうちょっとかみ砕いて説明してみよう。
■XML構文ではない外部ファイルを扱う
解析対象外実体は、XMLの構文ではない外部ファイルを実体として扱うものである。XMLの構文であれば、外部解析対象実体として扱うことができる。解析対象というのはXMLの構文として解析できる対象ということである。それに対して、解析対象外というのは、XMLの構文として解析できない対象ということである。
さて、解析対象外実体を規定するときは、以下のような書式をDTDに書き込む。
<!ENTITY pic SYSTEM "picture.gif" NDATA gif >
この宣言は、picという名前の実体はGIF形式の画像データであり、"picture. gif"というファイルにその内容が存在することを示している。解析対象外実体はENTITY型またはENTITIES型の属性の値として、名前で参照するという規定があるので、これを利用する場合は属性を使う。ENTITY型として宣言されたsampleという属性があるとすれば、sample="pic"という属性を書き込むことができる。
解析対象外実体を利用する場合、その名前の前後に&と;を付ける必要はない。上の例では、単純にpicと書けば、文書中にGIF形式の画像データへの参照を挿入することができる。
■今のところ知らなくても構わない
ここで、「あれ?」と思った人も多いだろう。HTMLなどでは、画像データを挿入するために、解析対象外実体など使わないからだ。img要素があれば、画像の挿入ぐらい簡単にできる。あえて、解析対象外実体などという複雑なものを利用する意味が、ピンとこないかもしれない。
その認識は、少なくとも2001年のいま現在では正しい。しかし、SGMLが生まれた当初には、妥当性のある機能だったといえる。SGMLは、もともと、閉じたシステムで使用されることを前提にした設計になっている。そのため、外部のファイルを参照する際に、ただファイル名を示すだけでは不十分であり、参照するファイルがどのような形式のデータであるかを明確に識別する識別符号を入れる必要があった。この識別符号が、記法(Notation)と呼ばれるものである。
記法は、DTDの中に記述される記法宣言によって宣言される。これは<!NOTATIONで始まる書式で行う。そして、ここで規定されたキーワードをNDATAキーワードの後ろに続けて記述して、実体を宣言すれば、どのようなデータ型かを明示しながら解析対象外実体を指定することができる。しかし、現在のWWWネットワークは、よりリッチなHTTPプロトコルの存在を前提にデザインが進んでいる。あるデータが、どのような形式で記述されているかは、HTTPプロトコルがHTTPヘッダで伝えてくれるのである。そのため、それを参照する文書の中で、データの形式を明示する必要は存在しない。明示しなくても、システムがデータ型を取り違えてトラブルを起こすことはあり得ないのである(もちろん、設定がすべて正しく、ソフトにはバグがないと仮定すれば、の話である)。
以上のような理由から、解析対象外実体が利用される可能性は、現在では極めて低くなっている。ここまで解説を加えておきながら、このような結論を書くのも妙だが、解析対象外実体については、知らなくても構わないし、知っていても利用しない方がよいといえる。
実体を記述できる条件
さて、ここがXML難所の峠である。ここを越えれば、あとは下り坂だ。
以下の表は、W3Cが公開しているXMLの仕様書、つまりExtensible Markup Language (XML) 1.0 (Second Edition)よりの引用だ。ただし、用語は日本語に置き換えた。この表をモノにできれば、実体を自由自在に扱えるようになれるのだ。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
内容での参照とは、要素の内容つまり開始タグと終了タグの間に実体の参照(&...;や%...;)が出現した場合の扱いを示す。
属性値での参照とは、属性の値や、属性のデフォルト値として実体の参照が記述された場合を示す。
属性値として出現とは属性値の値として、実体の参照ではなく、実体の名前を記述する場合を示す。これは、DTDで属性をENTITY型やENTITIES型に指定した場合に可能となる表記である。
実体値として参照とは、実体宣言の中の値の一部として実体参照を記述した場合である。
DTDでの参照とは、実体値における参照を除いたDTD内での実体の参照である。
さて、表の中身を説明しよう。認識しないとは、その構文そのものが何の意味も持たず、普通の文字と見なされるということを意味する。%...;の構文は、DTD内でのみ意味があり、DTDの外では、特別な意味を何も持たない。
取り込むとは、実体の内容を、本文の一部であるかのように置き換えることを意味する。この中にマークアップを含んでもよい。<や文字参照などの実体は展開されたうえで、取り込まれる。
検証時に取り込むとは、検証(バリデーション)を行う場合には取り込むことを必須とするが、検証しないときには取り込んでも取り込まなくてもよいことを示す。ただし、取り込まない場合は、XMLパーサがアプリケーションソフトに、実体があったものの内容を取り込んでいないことを伝達しなければならない、とされている。
禁止とは、そのような実体を記述することが即刻エラーになることを示す。これに対して、上記の認識しないはすべて文字として扱い、エラーにはしない。
リテラル内で取り込むとは、上記の取り込むと基本的に同じだが、引用符'や"が特別な意味を持たないことを意味する。つまり、attr="hello!"のような属性があったときに、これに引用符を含めたいと思ったときに、attr=""hello!""と書いても安全ということを意味する。"は展開されて引用符"になるが、この引用符は、属性の区切りの引用符とは見なされない。
通知するとは、XMLパーサがアプリケーションソフトに、実体参照に出合ったことを通知しなければならないことを意味する。どちらにしても、解析対象外実体をXMLパーサが解析することはできないので(できないから「解析対象外」といわれる)、XMLパーサが何かをしてくれることは期待できない。
バイパスするとは、そこにある実体参照を実体参照として認識するものの、その場では処理しないことを意味する。これは、実体を宣言する中で参照される実体に関する規定である。つまり、後でちゃんと実体を展開すべき正しいタイミングが訪れるということを意味する。
PEとして取り込むとは、取り込まれる際に、前後に空白文字を付け加えられることを意味する。PEとはパラメータ実体のことである。この規定は、パラメータ実体が展開された際に、内部に書かれた文字と、DTD本文に書かれた文字がくっついて、意図しないキーワードが生じてしまうことを防止するために設けられている。
そしてstandaloneの意味
ここまで見てきたように、XML文書は、さまざまな実体を参照することができる。参照される実体には、内部のものもあれば、外部のものもある。ここで重要なことは、ネットワークの利用を考えた場合、外部情報を参照する必要の有無は、処理効率に大きな影響を持つと言うことである。もし、外部への参照がなければ、対象となるXML文書が1個あればネットワークに接続されていないコンピュータでも完全に処理することができる。それに対して、1個でも外部への参照があれば、それに対するアクセス能力抜きにXML文書を処理できないことになる。つまり、事前に外部への参照の必要性が判明していれば、それによって処理を最適化できる可能性があるということだ。
では、一切、外部の情報を必要とせずに処理できる究極のXML文書とは、どんなXML文書だろうか。
■外部の情報を必要としない究極のXML文書とは?
外部のファイルへの参照が一切存在しないXML文書、というのは正しい答えではない。なぜなら外部参照があったとしても、それが結果に影響を及ぼさない性質のものであれば、外部情報がなくても処理できるといえるからだ。つまり正確にいうなら、外部情報を処理しても処理しなくても、結果が同一となるようなXML文書が、外部の情報を必要とせずに処理できるXML文書と呼ぶに値する。
しかしこの条件と、standalone="yes"と記述できる条件はイコールではない。standalone="yes"は、DTDの内部と外部に関する宣言だからだ。つまり、XML文書を処理する際に必要とされる実体に関する宣言がすべて内部情報だけで与えられる場合に、standalone="yes"とすることができる。もし外部のDTDにある実体宣言を参照する実体参照を含むXML文書があれば、それはstandalone="no"としなければならない。逆に、実体に関する宣言が内部にありさえすれば,それが外部実体を参照するものであってもstandalone="yes"と記述してよい。つまり、standalone="yes"と書かれているからといって、外部情報を一切必要としないXML文書とは言い切れない。
だが、standalone="yes"であれば実体の内容が外部にある可能性はあるが、その実体がどのような種類の実体であるかは判断できるだけの情報が必ず与えられる、ということである。
今回は実体に関してだけ説明をしてきたが、実体以外にも、XML文書の解釈を変えるような情報が存在する。例えば、DTDに記述される属性のデフォルト値は、DTDが外部ファイルにある場合、これを読み込む場合と読み込まない場合とでは、アプリケーションソフトが処理する内容に変化が生じることもある。また、これは分かりにくいものだが、文字の出現を許していない要素の内容として書かれた空白文字の扱いも、変化する可能性がある。このような空白文字は、なかったことにされるのが決まりであるが、もしDTDが外部にあって読み込まれなかったとすると、そこに文字を記述することが許されているかどうかを判断する根拠が失われてしまう。そのため、本来無視されるべき空白文字が文字データとして処理されてしまう可能性もある。このような曖昧さを持つXML文書は、standalone="yes"と指定することはできない。
次回は空白文字問題について
今回は、XML 1.0の中でも、かなりやっかいな実体とstandaloneについて取り上げてみた。書いてみると、今回もかなりハードな内容になってしまったが、ちゃんとついてこられただろうか。
実は筆者も、今回の原稿を執筆中に、あらためてXML 1.0を読みながら、足りない知識があることを発見したり、思い違いがあることに気付かされた。やはり、実体とstandaloneは、XMLの中でも混乱しやすい機能であり、何度もじっくり読んで、しっかりと把握する必要があると感じた。
さて、次回は、意外と分かっているようで分からない「空白文字」の問題を取り上げてみよう。今回も少しだけ、文字の出現を許していない要素の内容として書かれた空白文字について言及したが、これに限らず、いざ調べ始めると奥が深いのである。これを知らないと、妙なところで落とし穴に落ちることもある。空白文字だからといってばかにできないのである。
それでは次回、また会おう。
■訂正
2001年3月に、standalone="yes"と宣言することができる条件に関して記述に誤りがあるといういう指摘をSatoshi Nakamuraさんより受けました。そのため、2001年4月3日付けで,「そしてstandaloneの意味」の章を訂正しました。standalone="yes"とした場合は外部実体を参照できないと記述していましたが、実際には参照できます。
2001年9月に、解析対象外実体の部分の説明を修正しました。
Copyright © ITmedia, Inc. All Rights Reserved.