第2回
XML文書間のカット&ペースト(1):実体参照
対話的XMLアプリケーションを設計・実装する際には、XML文書間のカット&ペーストを避けては通れない。しかしその実現には、実体参照の扱い、名前空間の扱い、ID/IDREF属性の扱いをはじめとするさまざまな問題がある。
■話題 カット&ペースト、実体参照
■程度 A (XMLアプリケーショ ンの設計者、研究者などを対象とする)
■目的 問題点の分析
この連載をはじめてお読みになる方は、「XML深層探求について」をご覧ください。
檜山正幸
2000/7/22
1. はじめに
目次 |
1. はじめに 2. お約束 (前提、用語、記法など) 3. なにが問題になるのか 4. 実体参照の取り扱い 5. 実体名の書き換えは どのように行うべきか 6. まとめ |
XML文書間のカット&ペーストをどのように実行すべきか、これは難しい問題である。しかし、対話的XMLアプリケーションを設計・実装する者はこの問題を避けては通れない。また、カット&ペースト問題を考察することによって、XMLに関するより深い洞察を得ることができる。さらには、カット&ペースト問題の解決は、XMLベースの複合文書アーキテクチャ構築に直結する。そこで今回は、カット&ペースト問題を取り上げる。とはいえ、問題が複雑なので、今後何回かに分けて述べる。
2. お約束 (前提、用語、記法など)
カット&ペースト問題を考えるときは、すべてのタグ名、属性名が名前空間に属していることが前提になる。この前提がないと、議論が成立しないから、以下では、つねに名前空間の使用を仮定する(名前空間に関しては本連載第1回も参照)。 ただし、短い例や文書断片の引用では、名前空間宣言を省略していることもある。煩雑になるのを避けるためである。
説明用の例で、名前空間URIを正直に書くのは面倒なので、しばしば<xx:foo
xmlns:xx="XX NS">
のような書き方をする。つまり、"XX
NS"
は長いURIの代理あるいは略記と解釈していただきたい。
本記事および引き続く記事内で、カット&ペーストの例をいくつか出すが、どの例でも、文書Aの一部をカットして文書Bにペーストするものとする。文書Aのなかでカッ
トすべき部分は「<!--** ここから **-->
」「<!--**
ここまで **-->
」というコメントで示す。文書Bの挿入点は「<!--**
ここに **-->
」というコメントで示す。以下はその例である。
[文書A]<div calss="announce"> |
[文書B]<news id="i1" xmlns="NEWS-ML NS"> |
カットすべき範囲としては、任意の範囲ではなく、解析対象となる部分を考える。つまり、(要素|文字データ|参照|CDATAセクション|PI|コメント)*
というモデルにマッチする部分だけを考える。この仮定は話を簡単にするためのもので、現実には、もっと一般的な範囲を考える必要がある。
また、カットされ、クリップボードに送られる文書の一部をカット断片と呼ぶことにする。この用語は、私が説明の都合上導入するもので、一般的には認知されていない言葉だ(認知されるとよいと思うが)。
実体宣言は、DTD内部サブセットまたは外部サブセットに書かれるが、やはり話を簡単にするため、DTD内部サブセットに全ての実体宣言が書かれるとする。
Note: | 私は、DOCTYPE宣言不要論者だ。DOCTYPE宣言がないと、実体宣言を書くべきDTD内部サブセットもなくなる(もちろん、外部サブセットもなくなる)。結局、 DOCTYPE宣言を排除すれば、実体宣言の居場所も奪ってしまう。しかし一方で、 実体を完全に捨て去れと主張する気にはなれない(弱気か?)。悩ましい。このジレンマについては、いずれ述べるかもしれない。 |
3. なにが問題になるのか
XML文書間でのカット&ペーストの際には、次のような点を考えなくてはならない。
- 実体参照の扱い
- 名前空間の扱い
- ID/IDREF属性の扱い
- HTMLアンカーなどの(ID/IDREF以外の)参照の扱い
- 妥当性の検証と維持
- その他の細かい問題(例えば、「CDATAセクション内テキストの一部をカット&コピーするとき、それはCDATAセクションとして挿入すべきか否か」 など)
以上に列挙した問題点では、表示のことを考えていない。表示を考慮すると、さらにやっかいな問題が出てくる。また、DTDやスキーマから提供されるデフォルト値、スクリプトやイベントハンドリングなども、カット&ペースト問題を難解にする。だが今回は、上に挙げた最初の1つ、実体参照の問題だけを扱うことにする。残りの問題もこのシリーズでいずれ扱うだろう。
Note: | 表示のときの問題点を2つ指摘しておく。箇条書きの番号のように、XMLソースにもDOMツリーにも現れないデータがある。このような「生成された表示用データ」をどう扱うかは難しい問題だ。ペーストする相手によって振る舞いを変える必要があるかもしれない。また、スタイルシートによって与えられたスタイル情報を、どう運ぶかも問題だ。XHTML文書なら、style属性を使ってインラインに展開する手もあるが、文脈セレクタ(descendant、child、 adjacent siblingセレクタ)によって指定されたスタイルだったら具合が悪いし、一旦インライン展開してしまうと、タグ名や属性の変更に追従することもできない。かといって、スタイルシートそのものを相手先文書に挿入するのも一般的には無理がある。 |
4. 実体参照の取り扱い
まずは例1を見ていただきたい。文書Aから文書Bへのカット&ペーストを、単純なテキスト挿入で行ったものである。
例1:単純テキスト挿入によるカット&ペースト [文書A]<!DOCTYPE foo [ |
[文書B]<!DOCTYPE foo [ |
[文書B’(ペーストの結果)]<!DOCTYPE foo [ |
「Hello, world.」が「ハロー株式会社」に変わってしまっている。困ったこ とだ。簡単な対処法として、文書A内の実体参照を展開してからクリップボードに入れる方法がある。この方法は安全であるが、文書Aの実体参照が、文書B内では実体参照として認識できなくなる。ここでは、実体参照は実体参照のまま、カット&ペーストすることを目指そう。
まず、実体参照と同時に実体宣言も運ぶ必要があるのは明らかだろう。上の例では、「&hello;
がHello,
world.
である」という情報を、文書Bに記述したい。しかし、次のように同名の実体を2度宣言するわけにはいかない。構文エラーにはならないが、最初の宣言だけが有効になる。
<!ENTITY hello "Hello, world."> |
Note: | 同名の実体に関する複数の宣言があったとき、最初の宣言が有効になり、残りの宣言は無視されるという規則は、直感と常識に反する。SGMLとの悪しき後方互換性である。若干、宣言性が増す(非手続き的になる)とも言えるが、たいしてメリットはない。エラーにするほうがずっと健全だ。 |
この例からも明らかなように、実体名は単純な(つまり、構造化されていない)名前だから、2つの文書を混ぜれば、実体名の衝突は容易に生じる。 実体名の束縛に関して入れ子のスコーピング(ブロックスコーピング)は使えない。実体束縛は文書グローバルスコーピングだから、文書の一部分に対して、束縛(実体宣言の効果)をオーバライドする手段はまったくないのだ。
ブロックスコーピングによる名前の局所化が使えないなら、残された手段は実体名の書き換えだけである。幸いにも、束縛されている名前をリネームしても最終展開結果には何の影響も与えない。例えば、次の2つのインスタンスの展開結果は同じである。
<!DOCTYPE foo [ |
<!DOCTYPE foo [ |
Note: | 実体名の変更は、論理式の「束縛変数のリネーミング」に相当する。ラムダ計算なら、α変換(α-conversion)と呼ばれている書き換えに当たる。 |
Note: | 実体名のブロックスコーピングは絶対に不可能なのだろうか? ある種のブロックスコーピングができなくもない。ペーストする対象が文書全体の場合を考えよう。文書を入れ子にすることはできないが、リンクを張ることはできる。文書ロード時にリンクがトラバースされ埋め込み表示されるなら、少なくとも表示上は、文書の入れ子のように見える。このとき、「入れ子になった文書」間では、実体名の衝突は起こり得ない。 これは、文書という単位で名前のスコープは完全に閉じてしまうので、ほんとの意味のブロックスコーピングではないが、リンクをベースにしたカット&ペーストというアイディアは追求する価値がある。 |
カット&ペーストによる実体名の衝突が発生しても、 衝突を避けるように名前を選んでリネームすれば、うまく処理することができる。しかし、問題は残るのだ。節を改めて論じよう。
5. 実体名の書き換えはどのように行うべきか
理論的には、実体名は束縛された名前(bound name)だから、実際の名前(綴り)が何であるかは本質的ではない。だが現実には、名前そのものが重要な場合が多い。実体名を決める当事者は、ときに長い議論と調整を経て具体的な綴りを決定する。多くの場合、実体名が実体に対する記述やヒントになっているし、その実体名がユーザーインターフェイスに現れるかもしれない。そのような「貴重な」名前を勝手に書き換えてよいわけはない。つまり、何とかしてもとの名前を残したい。
名前の衝突を避ける手段はリネームだけ、しかし貴重な名前を無傷で残したい ── 原理的に両立は不可能だ。だが、ある前提のもとでは、悪くない(と思える)妥協的解決はできる。
実体名を変更する際に、もとの綴りに接頭辞を付けることにする。この接頭辞は、名前空間接頭辞とは全く異なる概念で、実体名の綴りの一部を形成するに過ぎないことに注意しよう。さて、この規則を守っても、何度もリネームが繰り返されると、myName
がC.B-A_myName
のような名前になってしまう。接頭辞と本体を区切る文字(セパレータ)を標準化し、リネームの手順を次のように規定してみよう。ここでは、セパレータにアンダースコア「_
」を使うとする。
- 新しく命名するときは、実体名にセパレータ「
_
」を入れない。 - 処理系は、必要に応じて、接頭辞とセパレータ「
_
」を先頭に付けて、実体名を変更してよい。 - すでにセパレータを含む実体名を変更する際は、セパレータより後の部分をそのまま残し、接頭辞を置き換えることにより変更する。
- ユーザーインターフェイスなどで実体名を使用する場合は、接頭辞を取り除いた部分を使う。例えば、
A_myName
ならmyName
の部分を使う。
この規則に従った名前の変更は、例えば、myName
→ A_myName
→ B_myName
のように行われる。この規則を、仮に実体名接頭辞規約と呼ぶことにしよう。この規約を守ってもらえれば、ある程度はうまくいく。ただし、ほんとに守ってもらえるかどうかはまったく別問題である。いや、制度的・政治的な力を使わないかぎり、守ってもらえないだろう。悲しいかな、それが現実だ。
6. まとめ
今回扱った内容をまとめると、次のようになる。
- XML文書間のカット&ペーストには多くの困難な問題がある。
- 実体参照を含む部分のカット&ペーストの際に、対応する実体宣言の情報も運ぶ必要がある。
- 意図せぬ実体名の衝突を避けるため、実体名の変更が必要になる。
- 実体名の変更は慎重に行う必要がある。「実体名接頭辞規約」は妥協的な解決とはなるが、規約が実効的かどうかは、制度的・政治的な問題である。
- 本連載第1回「XML名前空間の落とし穴」 (http://www.atmarkit.co.jp/fxml/ddd/ddd001/ddd001-namespaces1.html)
■履歴
2000/7/22 (公開)
「XML深層探求」 |
- 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」の詳細も紹介する
|
|