XMLマスターポイントレッスン ~ ベーシック編 ~
第11回 XML名前空間
インフォテリア株式会社 野中康弘 NONAKA, Yasuhiro
今回は、「XML名前空間」について解説します。XML名前空間とは、XML文書内にある「定義内容は異なるが同じ名前の要素名または属性名」を区別し、名前の衝突を回避するための仕組みです。 名前空間の基礎として、宣言方法や有効範囲、属性の名前空間、デフォルトの名前空間について解説します。 名前空間は、XMLの技術の中でも難易度が高いですが、ベーシックV2試験に合格するための必須知識となりますので、 しっかり学習しましょう。
名前空間とは何か、なぜ必要か
例えば、RDB上に図1のような構造を持つ表(employee表、section表)があるとします。
そこで社員ID、社員が所属する部門名、社員名の一覧を取得するためのSQL文を作成する場合、みなさんはどのようなSQL文を作成するでしょうか。employee表とsection表をsecID列で結合すれば、社員ID、社員が所属する部門名、社員名の一覧を取得できます。ただし、name列とsecID列は両方の表に存在しますので、name列とsecID列の前に表名または表の別名を指定し、どの表のname列、secID列なのか明記しなければなりません。 では、図1のデータがXML文書で表現されている場合はどうなると思いますか。2つのXML文書を利用して、社員ID、社員が所属する部門名、社員名の一覧をXML文書で作成するにはどうすれば良いか考えてみましょう。
まず、ルート要素(employeeList要素)と1人分の社員情報をまとめる要素(personList要素)を作成し、その子要素として社員ID要素(employeeデータXML文書のempID要素)、所属部門名要素(sectionデータXML文書のname要素)、社員名要素(employeeデータXML文書のname要素)を作成することが考えられると思います。しかし、この方法では問題があります。それは「要素名の衝突」です。employeeデータXML文書(LIST1)のname要素は社員名を表わす要素、sectionデータXML文書(LIST2)のname要素は部門名を表わす要素として定義されていたのですが、2つのXML文書をマージすること(LIST3)で異なる意味を持つname要素ができてしまいます。
LIST1:employeeデータXML文書(名前空間なし)
<employee>
<personInfo>
<empID>E0000001</empID>
<secID>S001</secID>
<name>山田太郎</name>
</personInfo>
<personInfo>
<empID>E0000002</empID>
<secID>S002</secID>
<name>田中一郎</name>
</personInfo>
<personInfo>
<empID>E0000003</empID>
<secID>S002</secID>
<name>鈴木次郎</name>
</personInfo>
<personInfo>
<empID>E0000004</empID>
<secID>S003</secID>
<name>高橋三郎</name>
</personInfo>
</employee>
LIST2:sectionデータXML文書(名前空間なし)
<section>
<sectionInfo>
<secID>S001</secID>
<name>営業部</name>
</sectionInfo>
<sectionInfo>
<secID>S002</secID>
<name>開発部</name>
</sectionInfo>
<sectionInfo>
<secID>S003</secID>
<name>総務部</name>
</sectionInfo>
</section>
LIST3:employeeListデータXML文書(名前空間なし)
<employeeList>
<personList>
<empID>E0000001</empID>
<name>営業部</name>
<name>山田太郎</name>
</personList>
<personList>
<empID>E0000002</empID>
<name>開発部</name>
<name>田中一郎</name>
</personList>
<personList>
<empID>E0000003</empID>
<name>開発部</name>
<name>鈴木次郎</name>
</personList>
<personList>
<empID>E0000004</empID>
<name>総務部</name>
<name>高橋三郎</name>
</personList>
</employeeList>
人間から見れば、それぞれのname要素が何を表わしているか内容で判断することはできますが、システムなどはname要素が何を表わしているかを判断できません。XML1.0の仕様自体が異なる種類のXML文書(ボキャブラリ)をマージして新しいXML文書を作成するようなことを最初から考慮していませんので、このような「要素名の衝突」という問題が発生してしまいます(図2)。
要素名の衝突を回避する
そこで、先述のRDB上の表の結合で説明した表の別名と同じような方法をXML文書でも利用できれば、「要素名の衝突」を回避できるのではないかと思った方もいるのではないでしょうか。XMLのボキャブラリをお互いに区別して、ボキャブラリを再利用できるようにするために、「XML名前空間(Namespaces in XML)」という仕様がW3Cより勧告されています。このXML名前空間を利用することで要素名の衝突を回避できます。
方法としては、employeeデータXML文書(LIST4)とsectionデータXML文書(LIST5)の両方にXML名前空間の宣言と記述を追加します。この2つのXML文書をマージして新しいemployeeListデータXML文書(LIST6)を作成します。
LIST4:employeeデータXML文書(名前空間あり)
<emp:employee xmlns:emp="urn:corp:emp">
<emp:personInfo>
<emp:empID>E0000001</emp:empID>
<emp:secID>S001</emp:secID>
<emp:name>山田太郎</emp:name>
</emp:personInfo>
<emp:personInfo>
<emp:empID>E0000002</emp:empID>
<emp:secID>S002</emp:secID>
<emp:name>田中一郎</emp:name>
</emp:personInfo>
<emp:personInfo>
<emp:empID>E0000003</emp:empID>
<emp:secID>S002</emp:secID>
<emp:name>鈴木次郎</emp:name>
</emp:personInfo>
<emp:personInfo>
<emp:empID>E0000004</emp:empID>
<emp:secID>S003</emp:secID>
<emp:name>高橋三郎</emp:name>
</emp:personInfo>
</emp:employee>
LIST5:sectionデータXML文書(名前空間あり)
<sec:section xmlns:sec="urn:corp:sec">
<sec:sectionInfo>
<sec:secID>S001</sec:secID>
<sec:name>営業部</sec:name>
</sec:sectionInfo>
<sec:sectionInfo>
<sec:secID>S002</sec:secID>
<sec:name>開発部</sec:name>
</sec:sectionInfo>
<sec:sectionInfo>
<sec:secID>S003</sec:secID>
<sec:name>総務部</sec:name>
</sec:sectionInfo>
</sec:section>
LIST6:employeeListデータXML文書(名前空間あり)
<list:employeeList xmlns:list="urn:corp:list"
xmlns:emp="urn:corp:emp"
xmlns:sec="urn:corp:sec">
<list:personList>
<emp:empID>E0000001</emp:empID>
<sec:name>営業部</sec:name>
<emp:name>山田太郎</emp:name>
</list:personList>
<list:personList>
<emp:empID>E0000002</emp:empID>
<sec:name>開発部</sec:name>
<emp:name>田中一郎</emp:name>
</list:personList>
<list:personList>
<emp:empID>E0000003</emp:empID>
<sec:name>開発部</sec:name>
<emp:name>鈴木次郎</emp:name>
</list:personList>
<list:personList>
<emp:empID>E0000004</emp:empID>
<sec:name>総務部</sec:name>
<emp:name>高橋三郎</emp:name>
</list:personList>
</list:employeeList>
その結果、employeeListデータXML文書の「emp:name要素」は社員名を表わす要素、「sec:name要素」は部門名を表わす要素として区別できます(図3)。1つの名前空間は要素や属性の集合だと考えると理解しやすいかと思います。
名前空間の宣言
名前空間を宣言するには図4のような記述方法に従って、要素の開始タグに記述します。
また、要素や属性が名前空間に属する場合は、名前空間接頭辞と要素名、属性名の間に「:(コロン)」を記述します。
試しに、先程のemployeeListデータXML文書(LIST4)を例に、名前空間の宣言と名前空間に属する要素の記述を見てみましょう。
<emp:employee xmlns:emp="urn:corp:emp">
<emp:personInfo>
…(中略)…
</emp:personInfo>
</emp:employee>
この例では、名前空間の接頭辞を「emp」、名前空間識別子(URI)を「urn:corp:emp」として宣言しています。これは、employee要素をはじめempという接頭辞が記述されている要素名や属性名はすべてurn:corp:empという名前空間に属していることを意味します(図5)。
なお、要素名や属性名に名前空間の接頭辞が記述されていない場合は、デフォルトの名前空間が宣言されている場合を除き(デフォルトの名前空間については後で解説します)、その要素名や属性名は名前空間に属しません。また、名前空間宣言は開始タグの属性として記述しますが、通常の意味の属性とは異なります。「xmlns:~」という記述だけが存在する要素には、名前空間宣言は存在しますが属性は存在しません。
名前空間接頭辞には任意の文字列を指定しますが、特に意味を持たないのでどのような文字列でも構いません。ただし、URIは世界中で一意となるように指定しなければなりません。よく「http://~」で始まるURLが用いられますが、実際に記述されているURLにアクセスするわけではありませんので、ファイルなどが存在しなくても良いのです。URIは、あくまでも論理的な名前空間名を表わすものなので注意しましょう(図6)。
図6:名前空間URIにアクセスしても……
<emp:employee xmlns:emp="http://www.infoteria.com/jp/employee">
<emp:personInfo>
・・・(中略)・・・
</emp:personInfo>
</emp:employee>
名前空間宣言の有効範囲
名前空間宣言の有効範囲(スコープ)とは、名前空間宣言で宣言した名前空間接頭辞を記述できる範囲のことで、その範囲は名前空間宣言が記述された要素とその内容になります(要素の開始タグから終了タグまで)。名前空間宣言が記述された要素や子孫要素、その中で定義されている属性に名前空間接頭辞を記述できます。
また、名前空間宣言が記述された要素の子孫要素で別の名前空間宣言を記述したり、有効範囲内の別の名前空間接頭辞を 使用したりすることもできます(図7)。
さらに、図8のように同じ名前空間接頭辞を使用して、名前空間識別子だけを変更することもできます。これらの場合でも、その有効範囲は前述したとおりになります。
最上位要素ではなく、部分的に名前空間宣言を記述することも可能です(図7)。しかし、同じ名前空間宣言を何度も繰り返さなければならないため、その結果XML文書が見づらくなってしまいます。そのような場合は、LIST6の例にもあるように、XML文書の最上位要素にまとめて名前空間宣言を記述するほうが良いでしょう。
属性が属する名前空間
属性の場合、属性が定義されている要素が属する名前空間と同じ名前空間に属することも、要素が属する名前空間とはまったく異なる名前空間に属することもできます。属性が名前空間に属する場合の記述は要素と同じで、属性名の前に「名前空間接頭辞:」と記述します。また、属性名に名前空間接頭辞が記述されていなければ、どの名前空間にも属さないことになります(図9)。
ここで注意しなければならないのは、XML1.0の仕様では同じ名前の属性を1つの要素に記述できないことになっていますが、XML名前空間(Namespaces in XML)の仕様に準拠したXML文書では、名前空間識別子と属性名の組み合わせで属性が重複しているかどうかを判断します。したがって、名前空間識別子が異なっていれば、属性名が同じでも同じ属性であるとは見なされません(図10)。
デフォルトの名前空間
「デフォルトの名前空間」とは、名前空間接頭辞を使用しない名前空間宣言のことで(記述方法は図11を参照)、その有効範囲(スコープ)は前述の名前空間の有効範囲と同様に、デフォルトの名前空間が宣言された要素とその内容になります。
デフォルトの名前空間を利用するメリットは、名前空間接頭辞の記述を省略できることです。例えば、既存のXML文書に新しく名前空間を追加する場合、新しい名前空間を適用する要素にいちいち名前空間接頭辞を記述していくことは大変な作業となってしまいます。大きなXML文書になればなるほど時間と手間がかかり、記述ミスを誘発することは簡単に想像できます。このような場合に、対象となるXML文書にデフォルトの名前空間の宣言のみを追加すれば、名前空間接頭辞をすべての要素に記述する必要がなくなるため、手間を省けます。
しかし、反対にデメリットもあります。それは、デフォルトの名前空間を利用することで名前空間接頭辞を省略できるため、要素がどの名前空間に属するのか、名前空間の適用対象が分かりづらくなることです。また、デフォルトの名前空間を宣言した場合、その名前空間が適用されるのは要素のみで、属性には適用されないことも留意しておくべきです(図12)。
デフォルトの名前空間の上書き
デフォルトの名前空間の有効範囲の中にまったく異なるデフォルトの名前空間を宣言することで、部分的にデフォルトの名前空間を書き換えることができます。また、デフォルトの名前空間は図13の記述方法を用いることで取り消すこともできます(図14)。その場合、有効範囲の中にある要素はどの名前空間にも属さないことになります。さらに、デフォルトの名前空間の有効範囲の中に名前空間接頭辞を用いた名前空間を指定することもできます。
図13:デフォルトの名前空間を取り消すための記述
今月の確認問題
ここまで、XML名前空間について、その概要と指定方法を解説してきました。ここで、今回解説した内容がしっかりと理解できているかどうか、確認問題でチェックしましょう。
問題1
次のXML文書「product.xml」において、「商品情報」要素が属している名前空間として正しいものを1つ選択してください。
[product.xml]
<?xml version="1.0" encoding="Shift_JIS"?>
<inf:商品情報xmlns:inf="urn:product:Info"
xmlns:stk="urn:product:Stock">
<inf:商品名コード="C00001">パソコン</inf:商品名>
<inf:価格単位="円">250000</inf:価格>
<stk:数量単位="台">200</stk:数量>
</inf:商品情報>
- urn:product:Info
- urn:product:Stock
- urn:product:Info、urn:product:Stock
- 「商品情報」要素が属している名前空間はない
解説
「商品情報」要素には、名前空間「urn:product:Info」の名前空間接頭辞として「inf」、名前空間「urn:product:Stock」の名前空間接頭辞として「stk」の2つの名前空間が宣言されています。さらに、「商品情報」要素には名前空間接頭辞「inf」が記述されていますので、「商品情報」要素は名前空間「urn:product:Info」に属することになります。したがって正解はAとなります。
問題2
次のXML文書「stock.xml」において、「商品名」要素の「コード」属性が属している名前空間として正しいものを1つ選択してください。
[stock.xml]
<?xml version="1.0" encoding="Shift_JIS"?>
<stk:在庫情報xmlns:stk="urn:product:Stock"
xmlns:inf="urn:product:Info">
<stk:商品名inf:コード="X00101">ノートPC</stk:商品名>
<stk:価格>300000</stk:価格>
<stk:数量>500</stk:数量>
</stk:在庫情報>
- urn:product:Stock
- urn:product:Info
- urn:product:Stock、urn:product:Info
- 「コード」属性が属している名前空間はない
解説
まず、このXML文書の名前空間の宣言を確認します。「在庫情報」要素に名前空間「urn:product:Stock」の名前空間接頭辞として「stk」、名前空間「urn:product:Info」の名前空間接頭辞として「inf」の2つの名前空間が宣言されています。属性が名前空間に属する場合は、属性名の先頭に「名前空間接頭辞:」が記述されている必要があります。「商品名」要素の「コード」属性には名前空間接頭辞「inf」が記述されていますので、名前空間「urn:product:Info」に属します。したがって正解はBとなります。
問題3
次のXML文書「sales.xml」において、「価格」要素の「単位」属性が属している名前空間として正しいものを1つ選択してください。
[sales.xml]
<?xml version="1.0" encoding="Shift_JIS"?>
<売上情報xmlns="urn:product:Details"
xmlns:sls="urn:product:Sales">
<商品名コード="C00001">パソコン</商品名>
<価格単位="円">250000</価格>
<数量単位="台">200</数量>
</売上情報>
- urn:product:Details
- urn:product:Sales
- urn:product:Details、urn:product:Sales
- 「価格」要素の「単位」属性が属している名前空間はない
解説
「売上情報」要素には、デフォルトの名前空間「urn:product:Details」と名前空間「urn:product:Sales」の名前空間接頭辞として「sls」が宣言されています。ここでは、デフォルトの名前空間が宣言されていますので、名前空間接頭辞が記述されていない要素はすべてデフォルトの名前空間である「urn:product:Details」に属します。
しかし、デフォルトの名前空間は属性には適用されませんので、「商品名」要素の「コード」属性、「価格」要素、「数量」要素の「単位」属性はこのXML文書内で宣言されている名前空間に属さないことになります。よって正解はDとなります。
問題4
次のXML文書「product.xml」において、「商品名」要素が属している名前空間として正しいものを1つ選択してください。
[product.xml]
<?xml version="1.0" encoding="Shift_JIS"?>
<商品情報xmlns="urn:product:Info"
xmlns:stk="urn:product:Stock">
<商品名コード="C00001" xmlns="">パソコン</商品名>
<価格>250000</価格>
<数量>200</数量>
</商品情報>
- urn:product:Info
- urn:product:Stock
- urn:product:Info、urn:product:Stock
- 「商品名」要素が属している名前空間はない
解説
まず、このXML文書の名前空間宣言を確認します。「商品情報」要素にデフォルトの名前空間「urn:product:Info」と名前空間「urn:product:Stock」の名前空間接頭辞として「stk」の2つの名前空間が宣言されています。問題3と同じく、名前空間接頭辞が記述されていない要素はすべてデフォルトの名前空間に属しますが、「商品名」要素でデフォルトの名前空間の取り消し「xmlns=" "」が記述されていますので、「商品名」要素が属する名前空間はありません。
ただし、この場合、デフォルトの名前空間の取り消しの有効範囲は「商品名」要素のみとなりますので、「価格」要素、「数量」要素の名前空間には影響しません。よって正解はDとなります。
* * *
今回はXML名前空間について、名前空間の宣言、有効範囲、属性の名前空間、デフォルトの名前空間を解説しました。特に、名前空間の有効範囲は「名前空間が宣言された要素とその内容である」「デフォルトの名前空間は要素にしか適用されない」ことをしっかりと理解しておきましょう。また、要素や属性が名前空間に属しているかどうかを判断する手順を図15にまとめておきますので、参考にしてください。
さて、今回まで11回にわたって、一通り「XMLマスター:ベーシックV2」試験範囲の技術解説を行なってきました。次回からは、これまで解説した内容の復習として、模擬問題とそれをベースにした技術解説を行なっています。まず、次回は「セクション1:XML概要」と「セクション2:XML文書の作成」について解説します。お楽しみに!
野中康弘(のなかやすひろ)
インフォテリア株式会社教育部に勤務。十数年間システム開発に携わり、そのうちの半分をDBAとして過ごす。現在は、主にインフォテリア認定トレーニングコースの「XMLマスター:ベーシックV2」に対応したコースのテキスト開発や改訂を担当。 本連載もスタートから約1年が経ち、その当時の目標(禁煙することと週2回スポーツジムへ通うこと)は未だ達成できず……。 焦らず、急がず、諦めずにチャレンジ中!(けど、腹回りが……)
<掲載>P.218-223 DB Magazine 2006 December