特定のルート要素名と名前空間をもつXMLを特定する
要素名や名前空間を条件にしたいとき
xml型に任意のスキーマを持つ(ルート要素も異なる)文書を放り込むような使い方をする場合には、ルート要素が何かというのを特定しなくちゃいけないが、要素名そのものや名前空間を条件にするのは、ちょっと面倒だったりする。
PostgreSQLのxml型にXMLを放り込んでxpath()で検索というのはよく行うけれど、そういう場合って要素の値や属性値を条件にすることが多い。
やり方
要素名や名前空間そのものは、xpath関数のlocal-name()でローカル名(要素名)を、namespace-uri()で名前空間URIを取得できるんだけど、これをXPathの述語として与え、その評価結果が空値でないかで判断しなくちゃならない。
例によって、xpath()がxml型配列を返却するから、それをtext型配列にcastして、その先頭要素を取り出してリテラルと比較する。xpathの述語が真なら空値じゃない値が返却されるので、それをWHERE句に記述することになる。書いているだけでも面倒くさい・・・。
xmldb=# \d xml_test
Table "public.xml_test"
Column | Type | Modifiers
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
data | xml |
xmldb=# INSERT INTO xml_test VALUES ('<r:root xmlns:r="http://hoge.hoge"><a><b>B1</b><c>C1</c></a></r:root>');
INSERT 0 1
xmldb=# INSERT INTO xml_test VALUES ('<r:nuko xmlns:r="http://hoge.hoge"><a><b>B1</b><c>C1</c></a></r:nuko>');
INSERT 0 1
xmldb=# INSERT INTO xml_test VALUES ('<r:root xmlns:r="http://foo.bar"><a><b>B1</b><c>C1</c></a></r:root>');
INSERT 0 1
xmldb=# select * from xml_test;
data
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<r:root xmlns:r="http://hoge.hoge"><a><b>B1</b><c>C1</c></a></r:root>
<r:nuko xmlns:r="http://hoge.hoge"><a><b>B1</b><c>C1</c></a></r:nuko>
<r:root xmlns:r="http://foo.bar"><a><b>B1</b><c>C1</c></a></r:root>
(3 rows)
xmldb=# で、これに対して以下のようにSELECTしてみる。
xmldb=# select data from xml_test WHERE (xpath('/*[local-name() = "nuko" and namespace-uri() ="http://hoge.hoge"]', data )::text[])[1] <> '';
data
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<r:nuko xmlns:r="http://hoge.hoge"><a><b>B1</b><c>C1</c></a></r:nuko>
(1 row)
xmldb=# select data from xml_test WHERE (xpath('/*[local-name() = "root" and namespace-uri() ="http://hoge.hoge"]', data )::text[])[1] <> '';
data
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<r:root xmlns:r="http://hoge.hoge"><a><b>B1</b><c>C1</c></a></r:root>
(1 row)
xmldb=# select data from xml_test WHERE (xpath('/*[local-name() = "root" and namespace-uri() ="http://foo.bar"]', data )::text[])[1] <> '';
data
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<r:root xmlns:r="http://foo.bar"><a><b>B1</b><c>C1</c></a></r:root>
(1 row)
xmldb=#一応、所要の動きになってくれた。DELETE/UPDATEも同じようにできるはず。