競プロ備忘録

競プロerの備忘録

XMLのお勉強その5 - Namespaces in XML 1.0

前回

XMLのお勉強その4 - XML 1.0 ③ - 競プロ備忘録

正規表現の実装にハマっていて、時間があきました。

読んでいく仕様書

今回はNamespaces in XML 1.0です。

原文:Namespaces in XML 1.0 (Third Edition)
和文Namespaces in XML 1.0 (Third Edition) (日本語訳)

XML 1.0に名前空間の概念を導入するための仕様です。ちなみに、XML 1.1に適用するためのNamespaces in XML 1.1もあります。

XML 1.0の本体ほど重厚ではないので、今回1回で終わりたいです。

本編

1 Motivation and Summary

なぜ名前空間を導入するのかという話です。

XMLの想定用途を鑑みると、あるソフトウェアモジュール向けに定義された語彙の集合があるはずで、複数のソフトウェアモジュールから参照されるXML文書では語彙が重複することが嬉しくない、というのが動機のようです。
名前空間を分割することで、語彙が重複しても名前空間でその語彙の意味するところを特定できるわけですね。

イメージしやすいような具体例としては、ITmediaの記事の例がわかりやすそうです。

名前空間がない場合のXML文書は以下のとおりです。

<ComplexDocument>
    <title>President</title>
    <title>Monthly Report</title>
</ComplexDocument>

文書作成者の意図としては、"President"は肩書を意味しており、"Monthly Report"は文書の標題を指しているわけですが、XML文書ではいずれも"title"でタグ付けされており、意図を正確に伝えるのは困難です。
しかし、名前空間を導入することで、それぞれの"title"が意味するところを明確にできます。

<ComplexDocument
  xmlns:person="http://hoge/person"
  xmlns:document="http://hoge/document" >
    <person:title>President</person:title>
    <document:title>Monthly Report</document:title>
</ComplexDocument>

1.1章はそんなに重要なことは書いていないので、省略です。
仕様の適合性についてだけは注意が必要で、名前空間仕様に従う文書は、Namespace Constraint (名前空間拘束)に従うことが要求されます。

2 XML Namespaces

2.1 Basic Concepts

名前空間を理解するための基本的な用語である「XML名前空間」「展開名」「名前空間名」「局所名」「有修飾名」の定義です。

[Definition: An XML namespace is identified by a URI reference [RFC3986]; element and attribute names may be placed in an XML namespace using the mechanisms described in this specification. ]
[Definition: An expanded name is a pair consisting of a namespace name and a local name. ]
[Definition: For a name N in a namespace identified by a URI I, the namespace name is I. For a name N that is not in a namespace, the namespace name has no value. ]
[Definition: In either case the local name is N. ]
[Definition: A qualified name is a name subject to namespace interpretation. ]

XML名前空間」は、RFC3986によるURI参照で識別され、要素名・属性名はあるXML名前空間に属します。
「展開名」は、名前空間名、局所名の組です。
名前空間名」は、ある名前NがあるURIであるIで識別される名前空間に属するとき、Iです。属する名前空間がなければ、値はありません。
「局所名」は、上の説明で言うところのNです。
「有修飾名」は、名前空間の解釈の対象になる名前です。

個人的には、局所名とか有修飾名の和名はあんまり聞き馴染みがなく、どっちかというと英名のほうが聞き馴染みがあります。

名前空間を導入する動機であるところの名前の衝突の回避は、名前空間と局所名をペアで使うことによって達成できます。
ただし、名前空間名となるURIは普通XMLの名前よりも非常に長く、また名前に使用できない文字も含みます。従って、XML文書名で実際に現れるのは有修飾名となります。

有修飾名の説明はいまいち分かりづらいのですが、仕様書の説明の通り、接頭辞を伴う名前であるか、接頭辞を伴わない名前が有修飾名です。接頭辞によって(接頭辞がない場合は接頭辞がないことによって)名前空間を識別できます。

2.2 Use of URIs as Namespace Names

URIとして認識される文字列と、実際に名前空間名として使用できる文字列の差分の話です。

まず、空文字列は使用できません。
また、相対URI参照は非推奨となっています。

相対URI参照は非推奨、というこの文章はXMLプロセッサにどういう動作を求めているのでしょうか?
原文に助動詞はついておらず、エラーや警告などの文字もないので、相対URI参照が使用されたことを検知したらどうすべきなのかわかりません。
ここで、仕様書で提示されている"W3C XML Plenary Ballot"の内容を読んでみると、

  1. 名前空間仕様の範囲では、相対URI参照も名前空間名としては有効であるが、なんらかの基底URIを元に解決された形ではなく、指定された文字列そのものが名前空間名である
  2. Infoset, DOM, XPathなど、その他の仕様では、相対URI参照で識別する名前空間について、名前空間名の値を返すメソッドやアトリビュートの動作は未指定である

と述べられています。
1. については、このあとの2.3章の動作と同じことが述べられていると考えて良さそうです。ただ、相対URI参照も名前空間名として認識されるとハッキリ述べられています。
ただ、2. で述べられているように、他の仕様でも同様に許容されるとは限らないので、文書作成者の責任で可能な限り使用しないことが求められるようです。
補足事項として、この仕様では「名前空間URI (namespace URI)」という用語は未定義ですが、URIの仕様が指すところの「URI」とは相対参照が解決されたあとの文字列であるため、「名前空間URI」という用語を使用することには慎重であるべきである、と述べられています。慎重に使用しないと、あたかも相対URI参照を「URI」として解決した文字列こそが名前空間を識別する本来の「名前空間名」であると混同される恐れがあるので、注意が必要だということなのでしょう。

2.3 Comparing URI References

名前空間名の比較の話です。

[Definition: The two URIs are treated as strings, and they are identical if and only if the strings are identical, that is, if they are the same sequence of characters. ]

結論、単に文字列をしての一致を検査すれば良いだけです。パーセントエンコーディングしたり、された文字列をデコードしたりする必要はありません。
注意点としては、名前空間宣言におけるURI参照は、正規化された属性値であるという点があります。宣言された名前空間名がURI参照として評価される前に、文字参照実体参照は解決されます。

3 Declaring Namespaces

名前空間宣言の方法や制約の話です。

[Definition: A namespace (or more precisely, a namespace binding) is declared using a family of reserved attributes. Such an attribute's name must either be xmlns or begin xmlns:. These attributes, like any other XML attributes, may be provided directly or by default. ]

名前空間宣言の属性名は、"xmlns"に一致するか、"xmlns:"から始まるかのいずれかである必要があります。これらの属性は一般の属性と同様、属性リスト宣言によってデフォルト値を提供することもできます。

名前空間宣言の正規化された属性値は、名前空間を識別する名前空間名か空文字のいずれかである必要があります。「空文字であっても良い」というのは2.2章の記述と矛盾しないか?という気がしてしまいますが、2.2章で述べているのはあくまでも「名前空間名として空文字を使用できない」ということに過ぎません。
とはいえ、名前空間名として使用できないのに属性名として使用できるようにする意味とは?という疑問は残りますが、空文字には「既定の名前空間の打ち消し」という効果が与えられています。では既定の名前空間宣言ではない名前空間宣言で空文字を使うとどうなるのかですが、これは後に出てくる"[NSC: No Prefix Undeclaring]"によって禁止されています。
結局、名前空間宣言の正規化された属性値」として空文字は合法ですが、その名前空間宣言は既定の名前空間宣言に限られる、ということですね。

続いて、「名前空間接頭辞」「既定の名前空間」の定義です。

[Definition: If the attribute name matches PrefixedAttName, then the NCName gives the namespace prefix, used to associate element and attribute names with the namespace name in the attribute value in the scope of the element to which the declaration is attached. ]
[Definition: If the attribute name matches DefaultAttName, then the namespace name in the attribute value is that of the default namespace in the scope of the element to which the declaration is attached.]

名前空間のスコープや宣言の上書きについては後述されます。

そして、名前空間として使用できる接頭辞や、定義済名前空間名前空間接頭辞の制約についてです。

Namespace constraint: Reserved Prefixes and Namespace Names
The prefix xml is by definition bound to the namespace name http://www.w3.org/XML/1998/namespace. It MAY, but need not, be declared, and MUST NOT be bound to any other namespace name. Other prefixes MUST NOT be bound to this namespace name, and it MUST NOT be declared as the default namespace.
The prefix xmlns is used only to declare namespace bindings and is by definition bound to the namespace name http://www.w3.org/2000/xmlns/. It MUST NOT be declared . Other prefixes MUST NOT be bound to this namespace name, and it MUST NOT be declared as the default namespace. Element names MUST NOT have the prefix xmlns.
All other prefixes beginning with the three-letter sequence x, m, l, in any case combination, are reserved. This means that:

  • users SHOULD NOT use them except as defined by later specifications
  • processors MUST NOT treat them as fatal errors.

名前空間接頭辞xml名前空間http://www.w3.org/XML/1998/namespaceに束縛されるように定義されているとみなされます。
これらは、この組み合わせ以外で宣言されてはならず、また既定の名前空間として宣言されてもいけません。しかし、この組み合わせとして明示的に宣言されても構いません。つまり、

<root xmlns:xml="http://www.w3.org/XML/1998/namespace" />

は合法ということになります。もちろん、明示的に定義しなくても合法です。
対して、

<!-- 既定の名前空間として宣言してはならない -->
<root xmlns="http://www.w3.org/XML/1998/namespace" />
<!-- 'xml'以外の接頭辞を束縛してはならない -->
<root xmlns:hoge="http://www.w3.org/XML/1998/namespace" />
<!-- 'http://www.w3.org/XML/1998/namespace'以外に束縛されてはならない -->
<root xmlns:xml="http://example.com/" />

などは違法です。

名前空間接頭辞xmlns名前空間http://www.w3.org/2000/xmlns/に束縛されるように定義されているとみなされます。
既定の名前空間として宣言されてはならないというのはxml同様ですが、xmlnsは明示的に宣言することも違法です。また、xmlns以外の接頭辞がhttp://www.w3.org/2000/xmlns/に束縛されるのも違法なので、そもそも名前空間宣言の属性値としてこの名前空間名が現れることが違法とも言えます。
さらに、要素名はxmlnsを接頭辞として伴ってはなりません。属性名は名前空間宣言のためにxmlnsを接頭辞として伴うかもしれないので、要素名についてのみの制約です。

また、名前空間接頭辞として、(x|X)(m|M)(l|L)にマッチする文字列から始まる名前も予約済です。
ただし、XMLプロセッサはこれを致命的なエラーとして扱ってはならない、とも述べられています。これはXML 1.0仕様の"2.3 Common Syntactic Constructs"で述べられている制約でもあります。XML 1.0仕様では単に「将来用に予約している」と述べているのみで、どう扱うかは述べられていませんが、とにかくXMLプロセッサは合法な名前として受け付けなければならないようです。

最後に、局所名についても、接頭辞を伴わずに利用されたときに予約済名に重複するため、(x|X)(m|M)(l|L)から始まる名前を使うべきではない、と述べられています。XMLプロセッサの観点ではあまり重要ではない話です。

4 Qualified Names

有修飾名の説明です。

XML 1.0仕様では、要素名や属性名は"Name" (XML 1.0, 生成規則[5])にマッチする必要がありましたが、この仕様では更に制約を強めた"QName"(生成規則[7])にマッチする必要があります。
具体的には、コロンが高々1つだけ含まれたNameでなければならない、と考えれば良いでしょう。

有修飾名が接頭辞を伴う場合、その接頭辞はある名前空間宣言の中の名前空間名に結び付けられていなければなりません。

有修飾名が接頭辞を伴うか否かに関わらず、有修飾名は「局所部位 (LocalPart)」を持ちます。

[Definition: The LocalPart provides the local part of the qualified name.]

「局所部位」と「局所名」は基本的に同じものだと思うのですが、用語を使い分けている理由は何なんですかね…?
これは未だによく分かっていないのですが、6章によれば、接頭辞を持つ名前空間、既定の名前空間のいずれも、局所部位が局所名になります。

注意点として、接頭辞はそれに紐付いた名前空間名のプレースホルダでしかないということです。
あくまでもそれぞれの名前は、名前空間名、局所名の組で識別されるものなので、もし名前空間のスコープを超えて名前を比較する必要があるならば、有修飾名の接頭辞を名前空間名に解決し、局所名との組み合わせで比較を行うべきです。

5 Using Qualified Names

有修飾名を仕様に適合したXML文書でどのように使用するのかの説明です。

基本的には、タグ、属性、文書型宣言、要素宣言、属性リスト宣言のNameがQNameに置き換わると思えば良さそうです。
NameがQNameに置き換わったことで、いくつかの名前空間拘束が追加されます。

Namespace constraint: Prefix Declared
The namespace prefix, unless it is xml or xmlns, MUST have been declared in a namespace declaration attribute in either the start-tag of the element where the prefix is used or in an ancestor element (i.e., an element in whose content the prefixed markup occurs).
Namespace constraint: No Prefix Undeclaring
In a namespace declaration for a prefix (i.e., where the NSAttName is a PrefixedAttName), the attribute value MUST NOT be empty.

1つ目は、定義済接頭辞(xml, xmlns)以外の接頭辞は、その接頭辞が使用される要素のの開始タグ、もしくは祖先の要素で宣言されていなければならない、と述べています。
2つ目は、接頭辞を宣言する名前空間宣言の属性値は空文字であってはならない、と述べています。
これらについて理解しがたいところは特にないでしょう。(2つ目については3章でも触れた話です)

続けて、DTDと合わせて名前空間の機能を使用する場合の注意点についても述べられています。
まず、DTDは名前の検証において、接頭辞が何の名前空間に束縛されているかを解釈することはありません。もしDTDにおいて"foo:bar"という名前が許容されると宣言されているのであれば、"foo"が何の名前空間に束縛されているかは関知せず、ただ"foo:bar"という名前を許容します。このような挙動による想定外の結果を防ぐ策としては、名前空間宣言を属性リスト宣言にて#FIXEDとして宣言することが提示されています。
また、名前空間宣言のデフォルト値が外部実体を介して提供されると、XMLプロセッサが検証をするか否かによってアプリケーションに渡される結果が変化し得ます。そのため、名前空間宣言のデフォルト値を提供する場合、内部サブセットに宣言を記述しなければならない、と述べられています。とはいえ、これはXML文書作成者向けの制約であると思われ、XMLプロセッサの観点では、単に提供された内容と設定の通りに処理をすれば良いだけでしょう。

6 Applying Namespaces to Elements and Attributes

6.1 Namespace Scoping

名前空間のスコープの話です。

名前空間のスコープは、名前空間宣言が出現した開始タグから終了タグまでの範囲から、その宣言で束縛した接頭辞と同じ接頭辞を束縛する宣言による名前空間のスコープを除外した範囲です。
名前空間が宣言されたのが空要素タグであるならば、そのタグ自身がスコープとなります。

ある名前空間宣言の効果は、その宣言で指定された接頭辞を伴い、かつその名前空間のスコープ内に現れる名前に適用されます。
そのような名前の名前空間名は宣言で指定された名前空間名であり、局所名は名前の局所部位です。

6.2 Namespace Defaulting

既定の名前空間の話です。

既定の名前空間についても、スコープの考え方は同様です。

ただし、注意点が2つあります。
1つは、接頭辞を持たない属性名についてです。接頭辞を持たない属性名は常に名前空間名を持ちません。既定の名前空間名前空間名が適用されるわけではありません。
もう1つは、既定の名前空間の取り消しについてです。既定の名前空間宣言だけは属性値が空でも構いません。このとき、既定の名前空間は取り消され、この宣言のスコープでは、接頭辞を持たない名前は名前空間名を持ちません。

接頭辞のない名前の局所名については接頭辞付きの名前と同様、常に局所部位(つまり、名前そのもの)と同じです。
接頭辞のない要素名の名前空間名は、自身をスコープとして含む既定の名前空間宣言があるのであれば、そのうち自身に最も近い祖先要素での宣言で指定された名前空間名です。そのような宣言がなければ、値を持ちません。

6.3 Uniqueness of Attributes

属性の一意性制約についてです。属性の一意性制約はXML 1.0仕様にもありますが、この仕様では追加で、展開名も一意でなければならないという制約を設けています。

Namespace constraint: Attributes Unique
In XML documents conforming to this specification, no tag may contain two attributes which:

  1. have identical names, or
  2. have qualified names with the same local part and with prefixes which have been bound to namespace names that are identical.
  1. についてはXML 1.0仕様で述べられている制約と同じです。
  2. については、仕様中の例でも示されている通りですが、同じ名前空間名を持つ接頭辞付きの複数の名前空間宣言のいずれのスコープ内にも含まれる要素の属性については、見かけ上の名前が異なっていても、展開名が同じということがありえます。この仕様に適合した文書中の名前は展開名で識別されるべきですから、このような形での重複も許容しない、ということです。

ただし、既定の名前空間が絡むときは注意が必要です。
6.2章で述べられた通り、既定の名前空間のスコープ内の接頭辞のない属性名は名前空間名を持ちません。従って、接頭辞付きの名前空間宣言と既定の名前空間宣言が同じ属性値を持っていたとしても、属性名の展開名が重複することはありません。
以下は仕様中の例の改変です。"a"と"n1:a"は一見展開名が同じになるように見えますが、"a"は上述の通り名前空間名を持たないため、展開名が異なるということです。

<!-- 'a'と'n1:a'は一意性制約に違反しておらず合法 -->
<x xmlns:n1="http://www.w3.org" xmlns="http://www.w3.org" >
  <good a="1" n1:a="2" />
</x>

7 Conformance of Documents

この仕様に適合する文書とはどのようなものであるかの説明です。

まず、この仕様で言うところのXMLとはXML 1.0であると述べています。XML 1.1ではありません。そのうえで、XML仕様に従って整形式でなければなりません。
次に、要素名・属性名はQNameに、要素名・属性名以外のNameにマッチすべきとされている文字列についてはNCName(生成規則[4])に、それぞれマッチしなければなりません。

上記を満たすXML文書は、「名前空間整形式」であると定義されています。

[Definition: A document is namespace-well-formed if it conforms to this specification. ]

加えて、「名前空間において妥当」についても定義されています。

[Definition: A namespace-well-formed document is namespace-valid if it is valid according to the XML 1.0 specification, and all tokens other than element and attribute names which are REQUIRED, for XML 1.0 validity, to match the XML production for Name match this specification's production for NCName. ]

つまり、名前空間整形式であって、XML仕様に則って妥当でもあれば、名前空間において妥当ということになるようです。
注意すべき点として、XML仕様においてID, IDREF(S), ENTITY(ES), NOTATION型の属性値はトークンごとにNameであることが要求されているため、名前空間において妥当であるためには、これらの属性値の各トークンもNCNameであることが要求されます。

8 Conformance of Processors

XMLプロセッサの適合性についての話です。

当然といえば当然な気もしますが、名前空間整形式違反は報告されなければなりません。
そして地味に重要な点ですが、名前空間名がURI参照であるかの検査は要求されません。つまりここまで述べられてきた、名前空間名がURI参照でなければならないという制約は、文書作成者向けのものであって、XMLプロセッサ的には大して重要ではなかったということでしょうか…
とはいえ、これは嬉しいことで、別に名前空間宣言かどうかで属性についてあれこれ検査を加える必要もなく、普通の属性値と同様に扱っても良いということになります。

また「名前空間検証用の処理器」についても定義があります。

[Definition: A validating XML processor that conforms to this specification is namespace-validating if in addition it reports violations of namespace validity. ]

和訳が微妙な気がしないでもないですが、XML仕様で言うところの「妥当性を検証するXMLプロセッサ」と対になる概念でしょうか。

あとがき

1回で終わってよかったです。

前半でURIの扱いについて散々述べていたのに、最後の最後で実は検証しなくても良いです、とは驚きです。
名前空間仕様のためにURIを実装する必要は別にないということですね。とはいえ、XMLの外部実体の場所の解決とか、XML Baseとか、そのあたりで必要になるでしょうから、実装はしてみようかなと思っていますが。

次はSAXのつもりですが、XML BaseとかInfosetも見ておいたほうが良いのではないかという気がしたりしなかったり…
まあ別にSAXの次でも特に問題はないと思うので、やはり次はSAXにしておきます。