PostgreSQLでXMLを処理してみよう!(最終回)(2)
3. PostgreSQLでのXML名前空間対応 ~xpath関数での名前空間指定~
本連載でこれまでに使ったXMLデータではXML名前空間を指定していませんでしたが、多くのXML利用では名前空間接頭辞をつけて要素名を表現する方法がよく使われます。 (これによって、一つのXMLデータの中で異なるXML Schemaで定義された要素を衝突することなく使い分けることができます。)
この記事で使用したXMLデータ (record001.xml) を名前空間接頭辞を加えて表現すると以下のようになります。
名前空間付きXMLデータ(record002.xml) :
<?xml version="1.0" encoding="Shift_JIS"?> <a:work-record xmlns:a="http://www.postgresql.jp/schema/workhistory" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.postgresql.jp/schema/workhistory C:/pgsqlxml/workhistoryNS.xsd"> <a:career> <a:period> <a:start>1970-04-01</a:start> <a:end>1980-03-31</a:end> </a:period> <a:content>製造業向け購買管理システムの開発、大手流通業向け販売管理システム開発</a:content> <a:role>プログラマー</a:role> </a:career> <a:career> <a:period> <a:start>1980-04-01</a:start> <a:end>1985-03-31</a:end> </a:period> <a:content>金融機関向け社内Webシステム開発</a:content> <a:role>プロジェクトリーダー</a:role> </a:career> <a:career> <a:period> <a:start>1985-04-01</a:start> <a:end/> </a:period> <a:content>IT戦略部にて規格提案業務</a:content> <a:role>マネージャー</a:role> </a:career> </a:work-record>
各要素の前に 「a: 」という接頭辞が付いています。「a: 」という接頭辞をこのXMLデータで使うことは、<a:work-record> 要素の属性としてxmlns:a="http://www.postgresql.jp/schema/workhistory"と書くことによって宣言されています。名前空間を識別するURI (http://www.postgresql.jp/schema/workhistory) に対して「a: 」という接頭辞を割り当てるこの宣言によって「a: 」が有効になります。
また、xsi:schemaLocation="http://www.postgresql.jp/schema/workhistory C:/pgsqlxml/workhistoryNS.xsd"の部分では、この名前空間URIと実際のXML Schemaファイルを対応付けています。
※record001.xmlにおいて名前空間なしのXML Schemaへの参照を行うときには、xsi:noNamespaceSchemaLocationという属性でXML Schemaのファイルのみを指定していたのに対し、名前空間付きのXML Schemaスキーマへの参照では、xsi:schemaLocation属性を使って、XML Schemaのファイルだけでなく、名前空間URIをペアで書いていることに注意してください。
ところで、"http://www.postgresql.jp/schema/workhistory" という名前空間URIを使ってXML名前空間に対応させたXML Schemaは以下のようになっています。
名前空間付きXML Schemaファイル (workhisotyNS.xsd):
<?xml version="1.0" encoding="Shift_JIS"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.postgresql.jp/schema/workhistory" xmlns="http://www.postgresql.jp/schema/workhistory"> <xsd:element name="work-record"> <xsd:complexType> <xsd:sequence> <xsd:element ref="career" minOccurs="0" maxOccurs="unbounded"/> </xsd:sequence> </xsd:complexType> </xsd:element> ? ? (以下、workhistory.xsdの内容と同じ) ? </xsd:schema>
<xsd:schema> 要素の属性でtargetNamespace="http://www.postgresql.jp/schema/workhistory"と書くことによってこのXML Schemaが名前空間に対応したものであることが表わされています。また、xmlns属性でその名前空間をデフォルトとして使用することを宣言しています。
3.1 関数xpathでの名前空間宣言
XML型の値を処理する為XPath1.0を実装した関数 xpath() があります (連載第2回で説明) 。PostgreSQLでは、このxpath()も名前空間に対応しています。
そこで、新しいテーブルmeiboNSにXML型の列work_historyを作成し、名前空間を使ったXMLデータ (record002.xml) を格納した上でxpath関数を使ってみましょう。
名前空間付きのXMLデータには前述の「a:」のような接頭辞が要素名に入っていますので、XPath式の中でも'/a:work-record/a:career/a:content' というように要素名に接頭辞を付けます。
meiboNSテーブルの作成とrecord002.xmlの格納:
testdb=# CREATE TABLE meiboNS(work_history xml); CREATE TABLE testdb=# \set foo '''' `type C:\pgsqlxml\record002.xml` '''' testdb=# INSERT INTO meiboNS(work_history) VALUES(:foo); INSERT 0 1
ただし、単に要素名の前に「a:」という接頭辞を付けただけのXPath式を使って処理しようとするとエラーになります。
testdb=# SELECT xpath('/a:work-record/a:career/a:content', work_history) FROM meiboNS;
ERROR: could not create XPath object
これは「a:」という接頭辞の名前空間宣言をxpath関数の中で行っていないからです。
「a:」という接頭辞が名前空間URI"http://www.postgresql.jp/schema/workhistory"を表すものであることを示すために、XPath関数の中でも名前空間の宣言を行う必要があります。XML名前空間の考え方では、XMLデータで用いられる 「a:」などの接頭辞は、"http://www.postgresql.jp/schema/workhistory"のエイリアスのようなものとして用いられるものです。したがって、”a”という文字は特に意味は持っておらず、URIで表される"http://www.postgresql.jp/schema/workhistory" が名前空間そのものを表す識別子 (名前空間URI) です。それで、XML名前空間を使うときには、必ず「a:」などの接頭辞を名前空間の識別子であるURIと対応付ける指定をどこかで行う必要があるのです。
xpath()関数で名前空間の宣言を行うには3番目の引数 (名前空間マッピング配列) を使います。
名前空間マッピング配列は、2次元配列です。以下の例では、ARRAYを入れ子にした多次元の配列コンストラクタ (副ARRAY構文) を使って2次元配列を構築し、その中で名前空間接頭辞と名前空間URIを指定しています。 (配列コンストラクタについて詳しくは、PostgreSQL文書8.4.0の「4.2.11.配列コンストラクタ」を参照してください。)
3.2 名前空間付きXMLデータの取り出し
xpath関数で3番目の引数で名前空間の宣言を行ったSELECT文は以下のようになり、これで名前空間を使ったXMLデータから目的の内容を取り出すことができます。
testdb=# SELECT xpath('/a:work-record/a:career/a:content', work_history,
array[array['a','http://www.postgresql.jp/schema/workhistory']]) FROM meiboNS;
xpath
-----------------------------------------------------------------------------------
{<a:content>製造業向け購買管理システムの開発、大手流通業向け販売管理システム開発</a:content>}
(1 row)
ところで、"http://www.postgresql.jp/schema/workhistory" という名前空間URIがこのXMLデータで利用されているXML名前空間を識別するための本質であり、「a:」という接頭辞は、たまたま割り当てられたエイリアスのようなものであると説明しましたが、名前空間URIさえ同じであれば、対応付けられる名前空間接頭辞が何であっても望むデータを取り出すことができます。
それで、以下のように、xpath式で使用する名前空間接頭辞を「b:」としても、それを名前空間宣言で"http://www.postgresql.jp/schema/workhistory"に対して割り当てれば、対象となるwork_history列のXMLデータに書かれている名前空間接頭辞「a:」とは違いますが、正しくデータの取り出しが行われます。
testdb=# SELECT xpath('/b:work-record/b:career/b:content', work_history, array[array['b','http://www.postgresql.jp/schema/workhistory']]) FROM meiboNS; xpath ----------------------------------------------------------------------------------- {<a:content>製造業向け購買管理システムの開発、大手流通業向け販売管理システム開発</a:content>} (1 row)
これで、XML名前空間の識別は名前空間URIが本質的な役割を果たし、接頭辞は一時的なエイリアスに過ぎないということが確認できました。
連載を終えるにあたって
PostgreSQLのXML機能について全6回に渡って解説してきましたが、PostgreSQLがXML処理をしっかりサポートしていることがお分かりいただけたことと思います。XMLの階層構造をたどるためのXPath (第2回、第3回で解説) や、構造変換を行うXSLT (第4回で解説) があれば幅広いXML応用が可能です。さらに今回説明したXML Schemaと連動させればXML処理としては完璧です。さらにRDBからのXMLデータ変換を行うSQL/XML (第5回で解説) もサポートしているので、RDBとXMLとの橋渡し機能も可能です。
今後は、現在追加モジュールとして提供されているXSLTの本体での実装や、現在はサポートされていないXML SchemaサポートをPostgreSQL本体で行うべきかどうかについての議論が待たれるところですが、いずれにしてもPostgreSQLでのXML利用は実用に十分耐える高いレベルにあります。
アプリケーション間での交換データ形式として定着した感のあるXMLは、これから益々その利用が広がると考えられます。これからのPostgreSQLのXML機能の充実を楽しみにしつつ、ひとまず筆を置くこととします。半年間のご愛読ありがとうございました。