XML 名前空間入門

Aaron Skonnard
DevelopMentor

July 2002

Aaron Skonnard の『XML 名前空間入門』の初出は 2001 年 7 月の『MSDN Magazine』です。この改訂版は、著者の許可の下で掲載されています。Copyright© 2001 Microsoft Corp. and CMP Media LLC。

名前空間は、XML にとって、特にこのテクノロジに馴染みのない人にとって混乱の元となっています。筆者が読者、学生、およびカンファレンスの出席者から受ける質問の多くは、何らかの形で名前空間に関連しています。これは、Namespaces in XML 勧告が、付録を除くと 10 ページ未満という、XML 仕様の中では短い部類に属することを考えると皮肉なことです。しかし、この混乱は、仕様に記されている構文ではなく、名前空間のセマンティクスに関連しています。XML 名前空間を完全に理解するためには、名前空間とは何か、名前空間はどのように定義されるか、そしてそれらがどのように使用されるかを知っておく必要があります。

このコラムは、この 3 つの疑問に、構文の面と抽象的な面の両方から答えることを目的としています。読者は、本稿を読み終えた頃には、名前空間が XML テクノロジのファミリにどのような影響を与えるかを理解していることでしょう。

名前空間とは?

名前空間とは、それに属するすべての名前が一意であるような名前のセットです。たとえば、私の子供の名前、カリフォルニア州の企業の名前、C++ の型識別子の名前、インターネット ドメインの名前などの集まりは名前空間と考えることができます。名前空間とは、それを構成する個々の名前が一意でなくてはならない、何らかの意味で論理的なつながりを持つ名前のセットのことです。

名前空間により、一意の名前を作り出すことが簡単になります。地球全体で名前が一意でなくてはならなかった場合、次に生まれる子の名前を考えつくのがどれほど難しくなるかを想像してみてください。この一意性を、私の子供のセットというような限定されたコンテキストに制約することで、物事ははるかに単純化されます。次の子に名前を付けるときには、すでに他の子に付けたのと重ならないようにするということだけを注意すればいいからです。他の両親たちは、私が自分の子に付けたのと同じ名前を選択する可能性がありますが、それらの名前は異なる名前空間に属しているので、簡単に区別することができます。

名前空間に新しい名前が追加されるときには、名前空間オーソリティが、新しい名前がその名前空間にまだ存在していないことを確認する必要があります。子供の命名のようなシナリオでは、これは単純な作業です。しかし、これがかなり複雑になるケースもあります。今日のさまざまなインターネットのドメイン管理団体がその良い例でしょう。このステップを省略してしまうと、重複する名前が名前空間を汚染し、曖昧さなしに特定の名前を参照するのが不可能になってしまいます。このようなことが起こった場合、その名前のセットは公式には名前空間とは見なされなくなります。定義上、名前空間はそのメンバの一意性を保証できなくてはならないからです。

名前空間そのものも、何かの目的に使用するためには、名前を持つ必要があります。名前空間に名前が付けられたら、そのメンバを参照することが可能となります。たとえば、図 1 の 2 つのボックスに示したサンプルの名前空間を考えてみましょう。これらのサンプルの名前空間の名前は、それぞれ Microsoft と AcmeHardware です。この 2 つの名前空間は同じローカル名をいくつか含んでいますが、これも図 1 に示すように、名前空間で修飾された名前を使用することで、曖昧さを残さない参照が可能になります。

図 1. 曖昧さのない名前空間

もちろん、これは名前空間名もまた一意であることを前提としています。これが保証できない場合には、実際の名前空間名そのものを何らかの名前空間に入れる必要があります。たとえば、複数の AcmeHardware 金物店がある場合 (カリフォルニア州に 1 つ、ユタ州に 1 つ) には、次に示すように、AcmeHardware という名前を 2 つの名前空間に入れることで衝突が解決されます。

California.AcmeHardware.Paint
Utah.AcmeHardware.Paint

このパターンは、名前空間名の一意性を保証するために何度でも繰り返すことができます。インターネット ドメイン ネーム システム (DNS) はまさにこのような構造を持つ、多数の名前空間を含んだ 1 つの巨大な名前空間です。

このタイプの名前空間の分割がなければ、一意性を保証するためにはきわめて長い (わかりにくい) 名前を使わざるをえなくなるでしょう。

MicrosoftWindowsOperatingSystemPaintApplication

分割不可能な 1 つのグローバルな名前空間しかなかった場合、どれほど複雑になり、フラストレーションの種になるか想像してみてください。人々は、ほとんどの状況では明示的には示されないものの、日常的な社会生活の中で名前空間を多用しています。しかし、ソフトウェア開発で名前空間を使用するときには、具体的な構文を通して明示的に示す必要があります。XML の名前空間に話を進める前に、今日の主流のプログラミング言語の 1 つにおける名前空間構文の例を取り上げてみましょう。

プログラミング言語における名前空間

プログラミング言語で名前空間を使用するためには、名前空間を定義し、名前空間の中の何かを参照するための構文に習熟する必要があります。今日の多くの言語は、C++、Java、および C# を含めて、名前空間のサポートを提供しています。C++ では、名前空間は次のように namespace ブロックを通して定義されます。

namespace foo1
{
   class bar
   {
      キキキ
   };
   class baz
   {
      キキキ
   };
}
namespace foo2
{
   class bar
   {
      キキキ
   };
   class baz
   {
      キキキ
   };
}

この例は、それぞれ bar と baz という 2 つの名前 (この例ではクラス識別子) を含んだ、foo1 と foo2 という 2 つの名前空間を定義しています。

foo1::bar b1;   // foo1 の中の bar クラスを参照
foo2::bar b2;   // foo2 の中の bar クラスを参照

特定の名前空間の中の bar クラスを参照するためには、bar 識別子を名前空間識別子で修飾する必要があります。

簡単に参照を行えるように、1 つのソース ファイルの中で特定の名前空間を使用することを宣言することも可能です。これにより、指定された名前空間はソース ファイルの既定の名前空間の 1 つとなり、個々の名前空間メンバを完全修飾する必要がなくなります (もちろん、曖昧さを避けるために必要な場合は除きます)。

using namespace foo1;
bar b1; // foo1 の中の bar クラスを参照

このように、C++ における名前空間の定義と使用の構文は、単純で簡単です。C# は、いくつかの小さな違いを除けば、これと同じ仕組みを持っています。Java における名前空間の構文はいくぶん異なりますが、概念は同じです。

名前空間は、多くのプログラミング言語で、名前の衝突を避けるために使用されています。これはまさに、XML 1.0 仕様の完全性を高めるために必要となる解決策であると言えます。

Namespaces in XML

多くの開発者は、XML 1.0 仕様は名前空間をサポートしていないために不完全であると感じています。XML 文書で使用されるすべての名前が 1 つのグローバルな名前空間に所属することになり、一意の名前を作るのが非常に難しくなっています。

ほとんどの開発者は、XML 1.0 の作成者たちも含めて、これが XML ベースの巨大な分散型システムで多くの曖昧さを引き起こすことになるということを予期していました。たとえば、次の XML 文書を考えてみましょう。

<student>
  <id>3235329</id>
  <name>Jeff Smith</name>
  <language>C#</language>
  <rating>9.5</rating>
</student>

この文書は、それぞれかなり一般的な名前をいくつか使用しています。student 要素は、ソフトウェア トレーニング コースの受講生をモデル化しています。id、language、および rating 要素は、受講生のデータベース レコード番号、好みのプログラミング言語、および受講生がコースに与えた評価 (10 段階評価) をモデル化しています。これらの名前は、いずれも他の状況で、別の意味として使われることがあるでしょう。

たとえば、次に示す XML 文書は、同じ名前をまったく別の形で使用しています。

<student>
  <id>534-22-5252</id>
  <name>Jill Smith</name>
  <language>Spanish</language>
  <rating>3.2</rating>
</student>

この例では、student 要素は小学校の生徒をモデル化しています。id、language、rating 要素は、その生徒の社会保障番号、母国語、および現在の成績の平均点 (4 段階評価) をモデル化しています。この 2 つの文書の作成者は、一意性を保証するために、これよりも長い一般的でない名前を使用することもできたはずですが、結局のところそうやっても一意性を保証するのは不可能ですし、使いにくくなるだけです。

人間はこの 2 つの文書を見て違いを見分けることができるかもしれませんが、ソフトウェアは両者をまったく同じものとして見ます。たとえば、上記のものを含む、多数の学生関連の XML 文書をサポートしなくてはならない学生管理アプリケーションを構築することになったとしましょう。コードを書いていくときに、プロフェッショナルな受講者と小学校の生徒、または他の種類の学生をどうやって (プログラム的に) 区別すればいいでしょうか。信頼の置ける区別の方法はありません。

異なる XML ボキャブラリーに属する要素または属性が、同じ文書またはアプリケーションの中で使用されるたびに、名前の衝突の問題が生じます。それ自体が変換を定義するための XML ボキャブラリーである XSLT を例に考えてみましょう。変換の際には、ユーザー定義のリテラル要素を出力することが可能です。しかし、XSLT ボキャブラリーには template という名前の要素が含まれています。では、やはり template という名前のユーザー定義リテラル要素を出力するにはどうすればいいのでしょうか。

<!-- XSLT の template 要素 -->
<template match="foo">
  <!-- この template 要素を出力したい -->
  <template match="foo"/>
</template>

名前の衝突の可能性は、複数の XML ボキャブラリーを多数ミックスする XSLT や XML Schema などの言語できわめて大きくなります。しかし、XML が名前空間をサポートしていれば、これらの問題は簡単に避けることができます。

Namespaces in XML 勧告は、XML 1.0 の命名の問題に対する W3C の解決策です。この仕様は、XML 1.0 の具体的な構文を拡張して名前空間をサポートする方法を定義しています。ほとんどの開発者は、この機能の追加を根本的かつ不可欠なものと考えているので、XML 1.0 の公式な補遺と見なされることもあります。実際、同じ理由から、今日の開発者の多くは XML 1.0 だけを取り上げるのではなく、「XML 1.0 + Namespaces」という単位で物を考えています。

Namespaces in XML 勧告は、XML 名前空間の命名の構文と、XML 名前空間の中で何かを参照するための構文を定義しています。ただし、XML 名前空間の中身を定義するための構文は扱っていません。これは XML Schema という別の仕様に任されています。どちらの領域についても、少し詳しい説明が必要でしょう。

名前空間の命名

C++ のようなプログラミング言語で名前空間を定義するときには、名前の中で使用できる文字に制約があります。XML の名前空間識別子も、具体的な構文、すなわち Uniform Resource Identifier (URI) 参照の構文に従わなくてはなりません。これは、XML 名前空間識別子が、RFC 2396 によって定義されている URI の総称構文に従わなくてはならないということを意味します。

URI の定義は、「抽象的または物理的リソースを識別するためのコンパクトな文字列」というものです。URI 参照は、ほとんどの状況では物理的なリソース (Web ページ、ダウンロードするファイルなど) を識別するために使用されますが、XML 名前空間の場合には、URI 参照は抽象的なリソース、特に名前空間を識別します。

URI 仕様によると、URI には Uniform Resource Locators (URL) と Uniform Resource Names (URN) の 2 つの一般形式があります。どちらのタイプの URI も名前空間識別子として使用することができます。次に、名前空間識別子として使用できる 2 つの URL の例を示します。

http://www.develop.com/student
http://www.ed.gov/elementary/students

次に示すものは、やはり名前空間識別子として使用できる URN の例です。

urn:www-develop-com:student
urn:www.ed.gov:elementary.students
urn:uuid:E7F73B13-05FE-44ec-81CE-F898C4A6CDB4

名前空間識別子の最も重要な属性は、それが一意であるということです。作成者は、インターネット ドメイン管理団体にドメイン名を登録することで、URL の一意性を保証することができます。ドメイン名の後に使われる文字列の一意性を保証することは、作成者の責任となります。

URN についても同じことが当てはまります。次に示すのは、基本的な URN 構文です。

urn:<namespace identifier>:<namespace specific string>

URN の一意性を保証するために、作成者はやはり名前空間識別子をインターネット ドメイン管理団体に登録しなくてはなりません。その後、作成者は、名前空間固有の一意な文字列を生成するためのスキーマに従う責任を負います。

XML 名前空間を定義する組織は、新しい名前空間名を作成するための一貫性のあるスキームを開発しなくてはなりません。たとえば、W3C はつねに新しい XML 名前空間を定義しています。ここでは、現在の年度とワーキング グループの名前を使用する、きわめて直観的なヒューリスティックが使用されています。図 2 に、W3C が使用しているパターンを示します。

図 2. W3C における URI の作成方法

定義上、URI は一意なので、XML 名前空間識別子の上に新たな名前空間の層を追加する必要はありません。名前空間の作成者が名前空間識別子の一意性を保証している限り、XML の中身は、つねに 1 つの名前空間識別子を使って一意に識別することができます。これにより、XML の名前空間を扱う作業が大幅に単純化されます。

XML プロセッサは名前空間識別子を不透明な文字列として扱い、決して解決可能なリソースとしては扱いません。繰り返しますが、名前空間識別子は単なる文字列に過ぎません。2 つの名前空間識別子は、それを構成する文字がまったく同じである場合に同一のものと見なされます。

結局のところ、どちらのタイプの URI 参照を使用するかはあまり問題にはなりません。多くの開発者は、読みやすく、覚えやすいという理由から URL を好みますが、柔軟性があるという理由から URN を好む開発者もいます。どちらのタイプを選ぶ場合でも、一意性を確実に保証する方法を知っておく必要があります。

名前空間の定義

Namespaces in XML 勧告は、名前空間の中身を定義するための構文を用意していません。多くの場合、このタイプの構文定義は必要ですらありません。今日では、多くの XML 名前空間が、要素の名前と、その属性およびセマンティクスを記述する定式的な仕様文書の形で定義されています。W3C のすべての名前空間も、まさにこの方法で定式的に定義されています (たとえば、http://www.w3.org/TR/xslt の XSLT 1.0 仕様を参照)。

名前空間が定義されたら、ソフトウェア開発者は仕様に従って名前空間を実装します。たとえば、MSXML 3.0、Xalan、および Saxon は、いずれも XSLT 1.0 仕様の実装です。これらの実装は、XSLT 1.0 名前空間に属する要素の処理がハードコードされています (http://www.w3.org/1999/XSL/Transform)。これらの実装を使用するためには、XSLT 1.0 名前空間の名前を正しく使用している XML 文書を実装に持たせる必要があります (これについては次のセクションで詳しく説明します)。XSLT 1.0 名前空間のいずれかの部分が変更された場合には、それをサポートするソフトウェアもアップデートする必要があります。

XML Schema ワーキング グループ (http://www.w3.org/XML/Schema) は、名前空間を構成する要素、属性、および型を定義するための XML ベースの構文を定義する新しい仕様 (XML Schema) を策定しました。XML Schema の登場により、次に示すように、ようやく名前空間の構文定義が可能となりました。

<schema xmlns='http://www.w3.org/2000/10/XMLSchema'
   targetNamespace='http://www.develop.com/student'
   elementFormDefault='qualified'
>
  <element name='student'/>
     <complexType>
         <sequence>
            <element name='id' type='long'/>
            <element name='name' type='string'/>
            <element name='language' type='string'/>
            <element name='rating' type='double'/>         
         </sequence>
     <complexType>
   </element>
</schema>

この例は、名前空間 http://www.develop.com/student を、student、id、name、language、および rating という 4 つの名前付き要素を含んでいるものとして定義しています。また、名前空間だけでなく、student の子要素の順序と型などのメタデータもあわせて提供しています。

XML Schema が提供しているような名前空間の構文定義により、名前と型情報を実行時に利用できる高度なソフトウェアを構築することが可能となります。XML Schema は依然として定義された要素と属性のセマンティクスを定義していないため、やはり何らかの補助仕様が必要となります。今後、ほとんどの XML 名前空間は仕様とスキーマ定義の両面から定義されることになるでしょう。

名前空間の使用

筆者は、名前空間の使用を、XML 文書中で特定の名前空間に属する 1 つまたは複数の要素または属性を使用するプロセスとして定義しています。このためには、Namespaces in XML 勧告に示されている、要素および属性名を名前空間修飾子で修飾するための構文を理解する必要があります。

要素と属性の名前は、実際には、名前空間名とローカル名の 2 つの部分から構成されています。このような 2 つの部分から成る名前は修飾名または QName と呼ばれます。

XML 文書では、名前空間プレフィックスを使って要素と属性の両方のローカル名を修飾します。プレフィックスは、通常はかなり長い名前空間識別子 (URI) の短縮形に過ぎません。プレフィックスは、まず名前空間を宣言することにより名前空間識別子にマッピングされます。次に、名前空間宣言の構文を示します。

xmlns:<prefix>='<namespace identifier>'

名前空間宣言は (要素の) 属性と似ていますが、論理的な文書構造という観点からは、公式には属性とは見なされません (つまり、DOM を使用するときに、要素の属性コレクションには現れません)。

名前空間プレフィックスは、宣言要素と、そのすべての子孫要素のスコープ内にあると見なされます。いったん宣言されると、このプレフィックスは任意の要素または属性名の前にコロンで区切って置くことができます (s:student など)。このプレフィックスを含んだ完全名が修飾名 (QName) の字句形式ということになります。

QName = <prefix>:<local name>

プレフィックスは、要素または属性を、現在のスコープのプレフィックスにマッピングされた名前空間識別子に関連付けます。

開発者が XSLT 1.0 名前空間を使いたいと考えたとします。そのためには、任意のプレフィックスを正式な XSLT 1.0 名前空間識別子 (http://www.w3.org/1999/XSL/Transform) にマッピングする名前空間宣言を用意する必要があります。その後、開発者が使用したいと考える XSLT 1.0 名前空間の個々の要素または属性に、次の例に示すように適切なプレフィックスを付ける必要があります。

<x:transform version='1.0'
   xmlns:x='http://www.w3.org/1999/XSL/Transform'
>
   <x:template match='/'>
      <hello_world/>
   </x:template>
</x:transform>

上の例は、名前空間の中の要素を参照するための構文を示しています。"x" のプレフィックスが付いた要素は http://www.w3.org/1999/XSL/Transform に属しており、それ以外のプレフィックスを持たないもの (hello_world など) はどの名前空間にも属していません。これでプロセッサは、XSLT 1.0 のプログラミング コンストラクトと、hello_world のように出力が意図されているリテラル要素を区別できるようになります。XSLT 1.0 名前空間のスペルに 1 文字でも間違いがあると、XSLT 1.0 プロセッサはその文書を自分に理解可能なボキャブラリーとして認識できません。

基本的に、個々の要素は名前空間識別子とローカル名の 2 つの部分から成る名前を持つことになります。この 2 つの名前の組み合わせは、一般に名前空間名と呼ばれます (注: プレフィックスとローカル名の組み合わせである QName とは違うことに注意してください)。

別の例として、次の XML 文書は、このコラムで前に示した XML Schema 定義の要素を使用する方法を示しています。

<d:student xmlns:d='http://www.develop.com/student'>
  <d:id>3235329</d:id>
  <d:name>Jeff Smith</d:name>
  <d:language>C#</d:language>
  <d:rating>9.5</d:rating>
</d:student>

名前空間がどのように定義されるかはともかく、その参照のための構文は同じです。

文書が複数の名前空間の要素または属性を使用する場合には、次の例に示すように、1 つの要素に対して複数の名前空間宣言を指定するのが一般的です。

<d:student xmlns:d='http://www.develop.com/student'
  xmlns:i='urn:schemas-develop-com:identifiers'
  xmlns:p='urn:schemas-develop-com:programming-languages'
>
  <i:id>3235329</i:id>
  <name>Jeff Smith</name>
  <p:language>C#</p:language>
  <d:rating>9.5</d:rating>
</d:student>

ここでは、student と rating はどちらも同じ名前空間に属しており、id と language は別の名前空間に属していますが、name はどの名前空間にも属していません。

また、名前空間プレフィックスは、次のように入れ子のスコープでプレフィックスを再現することで上書きすることが可能です。

<d:student xmlns:d='http://www.develop.com/student'>
  <d:id>3235329</d:id>  
  <d:name xmlns:d='urn:names-r-us'>Jeff Smith</d:name>
  <d:language>C#</d:language>
  <d:rating>35</d:rating>
</d:student>

この例では、urn:names-r-us namespace に属する name 要素を除くすべての要素が同じ名前空間に属しています。名前空間プレフィックスを再宣言することは可能ですが、名前空間プレフィックスの宣言を解除することはできません。たとえば、次に示す例は無効です。

<d:student xmlns:d='http://www.develop.com/student'>
  <d:id xmlns:d=''>3235329</d:id>  
   キキキ
</d:student>

XML 名前空間の中身を参照するためのプレフィックス型の構文は、ほとんどのソフトウェア開発者にとって直観的にわかりやすいものでしょう。Namespaces in XML 勧告がここで終わっていれば、名前空間が引き起こす混乱ははるかに少なくなっていたはずです。

既定の名前空間

名前空間識別子を要素名に関連付けるために使用できる、もう 1 つのタイプの名前空間宣言があります。これは、次の構文を使用する既定の名前空間宣言と呼ばれます。

xmlns='<namespace identifier>'

プレフィックスがないことに注意してください。要素に対して既定の名前空間宣言が使用された場合、そのスコープ内のすべての未修飾の要素名は、指定された名前空間識別子に自動的に関連付けられます。ただし、既定の名前空間宣言は属性にはまったく影響を与えません。属性を名前空間識別子に関連付けるには、プレフィックスを使うしかありません。

次の例を考えてみましょう:

<d:student  xmlns:d='http://www.develop.com/student'
     xmlns='urn:foo' id='3235329'
>
  <name>Jeff Smith</name>
  <language xmlns=''>C#</language>
  <rating>35</rating>
</d:student>

この例では、"student" は http://www.develop.com/student 名前空間に属し、"name" と "rating" は既定の名前空間 urn:foo に属しています。id 属性は名前空間には属していません。属性は、既定の名前空間識別子に自動的には関連付けられないからです。

また、この例の language 要素は、既定の名前空間識別子を空の文字列に設定するだけで、既定の名前空間の宣言を解除できることを示しています (プレフィックス宣言では、これは不可能だったことを思い出してください)。その結果、language 要素はどの名前空間にも属さなくなっています。

既定の名前空間の構文は、便宜性を考えて設計されていますが、一般にはその価値以上の混乱を引き起こしています。一般にこの混乱は、要素と属性が異なる方法で扱われ、入れ子になった要素に既定の名前空間識別子が割り当てられるということが一見して明白ではないことから生じます。しかし突き詰めれば、プレフィックスと既定の名前空間のどちらを選択するかは、属性が関わってくる場合を除けばほぼスタイルの問題に過ぎません。

名前空間の抽象化

XML 文書の抽象的な観点から名前空間を扱うのは、上で述べた字句上の問題を扱うよりもはるかに簡単です。XML Information Set (Infoset) は、XML 文書の抽象的な構造を定義し、開発者が上で述べた名前空間構文のような下位の複雑なシリアル化形式を扱わなくても済むようにしています。

Infoset の文脈では、個々の要素または属性は、名前空間識別子とローカル名の 2 つの名前プロパティを持ちます。図 3 は、名前空間で修飾された名前を含んでいる XML 文書の論理的構造を示しています。student、id、および language はいずれも同じ名前空間に属しているのに対し、ratings は別の名前空間に属しており、name はどの名前空間にも属していません。この文書は、前のセクションで説明したどのテクニックでもシリアル化することができます。

図 3. 名前空間で修飾された XML 文書

今日主流の API、SAX、および DOM の多くは、この抽象データ モデルを実装しています。SAX は、ContentHandler の startElement/endElement メソッド呼び出しを通して要素をモデル化しています。

public interface contentHandler
{
キキキ
void startElement(String namespaceURI, String localName, 
   String qName, Attributes atts) throws SAXException;
void endElement(String namespaceURI, String localName, 
   String qName) throws SAXException;
キキキ
}

要素は、名前空間識別子とローカル名 (およびオプションとして QName) の組み合わせによって識別されます。属性も、Attributes インターフェイスの名前空間対応のメソッドのセットを通して識別されます。文書ストリームを出力するときに名前空間名を提供するかどうかは、SAX パーサー (またはその他のプロデューサー アプリケーション) の自由です。これらのことから、SAX を使えば、各種の student 要素をプログラム的に区別するのは簡単であることがわかります。

キキキ
void startElement(String namespaceURI, String localName, 
   String qName, Attributes atts)
{
    if ( namespaceURI.equals("urn:dm:student") &&
         localName.equals("student") )
       {
        // Developmentor の student 要素はここで処理する
    }
    else if ( namespaceURI.equals("urn:www.ed.gov:student") 
         && localName.equals("student") )
       {
        // 小学校の student 要素はここで処理する
    }
}
キキキ

名前空間名 (名前空間識別子 + ローカル名) は SAX パーサーによって自動的に解決されるので、ソース文書で特定の要素または属性にどのプレフィックスが使われていたかは (使われていた場合) 関係ありません。これはシリアル化に関する細部に過ぎないからです。ただし、これは解析を行った後にプレフィックスを捨て去ってしまってよいということではありません。次の XML 文書を考えてみましょう。

<student xmlns:xsd='http://www.w3.org/2000/10/XMLSchema'
 xmlns:xsi='http://www.w3.org/2000/10/XMLSchema-instance'
>
  <age xsi:type='xsd:double'>35.0</age>
</student>

age の XML Schema xsi:type 属性は QName 値を含んでいます。要素または属性の内容に QName が使用されている場合、コンシューミング アプリケーションはそれを手動で扱う必要があります。コンシューミング アプリケーションがこの値を正しく解釈するためには、それがどの名前空間識別子 "xsd" にバインドされているのかを知っている必要があります。このため、Infoset は、文書内の個々の要素について、スコープ内の名前空間宣言のセットを保持しています。SAX はこの情報を、startPrefixMapping および endPrefixMapping メソッド呼び出しを通してモデル化しています。

DOM API も Infoset のもう 1 つの実装です。DOM の Node インターフェイスは、namespaceURI と localName の 2 つの名前プロパティを通して、要素/属性ノードの基本的アイデンティティをモデル化しています。また、nodeName および prefix プロパティを通して、ノードの QName とプレフィックスをモデル化しています。次の Java 言語コードは、DOM を使って 2 種類の student 要素を区別する方法を示しています。

void processStudent(Node n)
{
    if ( n.getNamespaceURI().equals("urn:dm:student") &&
         n.getLocalName().equals("student") )
       {
        // Developmentor の student 要素はここで処理する
    }
    else if ( 
        n.getNamespaceURI().equals("urn:www.ed.gov:student") 
        && n.getLocalName().equals("student") )
       {
        // 小学校の student 要素はここで処理する
    }
}

SAX と同様に、DOM ツリーを作成する XML パーサーは、名前空間プロパティを適切に格納する責任を負います。また、論理的な文書構造を扱ううえで、ソース文書で名前空間がどのように宣言されているかは関係ないことも SAX と同様です。DOM API を通して文書を作成するときには、作成時に個々の要素と属性に名前空間識別子を与える必要があります。

void generateStudentDocument(Document doc)
{
   Node docEl = doc.createElementNS("urn:dm:student", "student");
   doc.appendChild(docEl);
   Node n = doc.createElementNS("", "name");
   docEl.appendChild(n);
   キキキ
}

このコードでは、論理構造を直接に作成することができます。その後、名前空間宣言を下位の XML 1.0 文書にどのようにシリアル化するかは DOM の実装に任されることになります。この例の DOM ツリーは、次のようにシリアル化できるでしょう。

<student xmlns='urn:dm:student'>
   <name xmlns=''/>
</student>

XML 文書の抽象的なビューを (SAX/DOM API を通して) 扱うときには、既定の名前空間という概念がないことに注意することが重要です。上で示した例では、"student" に対する createElementNS の呼び出しによって、urn:dm:student が自動的に既定の名前空間になるわけではありません。名前空間なしの "name" に対する createElementNS の呼び出しは、name 要素に (urn:dm:student ではなく) 空の名前空間識別子を割り当てます。同じことが、SAX の startElement/endElement メソッド呼び出しシークエンスについても言えます。個々の要素/属性ノードは、つねに名前情報とは独立に扱われます。

XPath も、抽象的な文書構造の中のノードを識別する方法を定義している XML 仕様の 1 つです。XPath 式を使用すると、要素と属性を名前空間で修飾された名前で識別することができます。XPath 名のテストは単純な文字列式なので、XPath 名テストを名前空間識別子に関連付けるための方法は名前空間プレフィックスしかありません。

XPath ノード テストは QName 型であると考えることができます。つまり、ノード テストがプレフィックスを含んでいない場合には、「名前空間なし」に属する名前を要求していると見なされます。たとえば、次の XPath 式を考えてみましょう。

/student/name

この式は、どの名前空間にも属していないルートの student 要素の子であり、どの名前空間にも属していないすべての name 要素を識別しています。urn:dm:student 名前空間に属する student および name 要素を識別するためには、まず名前空間プレフィックスを urn:dm:student に関連付ける必要があります。その後、そのプレフィックスを XPath 式で使用することができます。

"dm" が XPath のコンテキストで urn:dm:student に関連付けられたとすると、次の式は、urn:dm:store 名前空間に属するルート student 要素の子であり、同様に urn:dm:store 名前空間に属する name 要素を識別することになります。

/dm:student/dm:name

クエリの対象となった文書が次のような内容だった場合、上の式は、プレフィックスとは関係なく、student の子である 3 つの name 要素すべてを識別します。これらはいずれも指定された名前空間に属しているからです。

<s:student xmlns:s='urn:dm:student'>
   <s:name/>
   <n:name xmlns:n='urn:dm:student'/>
   <s:name/>
</s:student>

プレフィックスは、XPath のコンテキストでは、実装に固有の方法でマッピングされます (MSXML 3.0 での動作については、The XML Files の 2001 年 5 月のコラムを参照してください)。これの例の 1 つは、XPath 式を使用するためのコンテキストを提供している XSLT です。XSLT 文書で名前空間で修飾された XPath 式を使用するためには、標準の名前空間宣言を使って、ターゲットの名前空間識別子を任意のプレフィックスにマッピングします。

<x:transform version='1.0'
   xmlns:x='http://www.w3.org/1999/XSL/Transform'
   xmlns:d='urn:dm:student'
>
   <x:template match='d:student'>
     <!-- ここで student を変換 -->
     <x:apply-templates select='d:name'/>
   </x:template>
   キキキ
</x:transform>

最初のテンプレートは、urn:dm:student 名前空間の student 要素にマッチングします。マッチ値が単に "student" であれば、どの名前空間にも属さない student 要素にのみマッチングすることになります。その後、apply-templates 要素は、同様に urn:dm:student 名前空間に属しているすべての子の name 要素を処理します。

このように、XML 仕様のファミリ全体を理解するためには、名前空間の字句的な仕組みと抽象的な仕組みの両方を理解しておくことが重要です。新しく登場する XML 仕様には、これに似た状況が多く存在します。

まとめ

名前空間は、それに属するすべての名前が一意であるような名前のセットです。Namespaces in XML により、要素と属性に一意の名前を付けることが可能となります。名前空間は混乱の元になりがちですが、その定義と使用の方法を、構文と抽象概念の両方から把握しておけば簡単に理解することができます。名前空間の詳細については、Namespaces in XML 勧告と「XML Namespaces by Example」を参照してください。

Aaron Skonnard は DevelopMentor のインストラクタおよび研究者として、XML カリキュラムの開発に携わっています。共著には 『Essential XML』 (Addison-Wesley Longman, 2000)、単独著には 『Essential WinInet』 (Addison-Wesley Longman, 1998) があります。連絡先は http://staff.develop.com/aarons です。