これは、書籍「Ruby de XML」 内に掲載されているXPathの関数リファレンスをベースとしています。一部、 本書での記述と異なる部分もありますが (注1) 、本 質的には同じものですのでご了承下さい。
(注1) 書籍内では紙面の都合による不自 然な折り返しを防ぐために不必要な変数が導入されていたりします。
XPathの関数は以下の四つに分類されている。
関数の中にはハイフン(-)を含むものもあるが、REXMLでは、ハイフン をアンダーバー(_)に変更した関数も用意している。例えば、 a-b() という関数があったら、a-b()でも呼び出せるし、 a_b()でも呼び出せる。
ちなみに、REXMLでは、XPathの関数はモジュール REXML::Functionsで定義されている。つまり、このモジュールに関 数を追加するとXPathで使えるようになるということだ。追加する際の注 意事項はrexml/functions.rbに書いてあるので、追加する前に読ん でおくといいだろう。
この節で使うサンプルXML文書は以下のものとする。
source = <<-XML
<?PI context?>
<a>
<!--COMMENT A-->
<fuga:b xmlns:fuga='http://fuga.com/'>
<!-- COMMENT FUGA:B -->
<c number='1'>TEXT</c>
</fuga:b>
<d xml:lang='ja'>
<e xml:lang='en'/>
<f/>
</d>
<b><!-- COMMENT B --></b>
</a>
XML
position()対象となっているノードセットの中でカレントノードが軸の方向 をふまえて何番目なのかを返す。最初のノードは0ではなく1から 始まる。また、述部に数値だけを指定するとposition()=を省 略したものと解釈される。例えば、[2]は [position()=2]と解釈される。
# 対象となっているノードセットの中で逆文書順で3番目のノードを選択。 REXML::XPath.match(doc, "/a/b/preceding::node()[position()=3]") # <f/> # preceding軸の効果は()内でのみ有効になるので、 # 述部の軸はデフォルトのchild軸が適用される。 # 対象となっているノードセットの中で文書順で1番目のノードを選択。 REXML::XPath.match(doc, "/a/b/(preceding::node())[1]") # " COMMENT B "
last()現在対象としているノードセットのノード数を返す。
# ルートノードの子ノードセットの中の最後のノードを選択。 REXML::XPath.match(doc, "/a/*[position()=last()]") # <b><!-- COMMENT B --></b> # 子ノードセットのノード数が1の要素ノードを選択。 REXML::XPath.match(doc, "//*[child::node()[last()=1]]") # <c number='1'>TEXT</c> # <b><!-- COMMENT B --></b>
count(ノードセット)引数で指定されたノードセットのノード数を返す。
# 子要素ノードが無い要素ノードを選択。 REXML::XPath.match(doc, "//*[count(./*)=0]") # <c number='1'>TEXT</c> # <e xml:lang='en'/> # <f/> # <b><!-- COMMENT B --></b>
id(オブジェクト)REXMLでは実装されていない。
local-name(ノードセット=.)引数ノードセットの中の文書順で最初のノードのローカル部分を返す。ノー ドセットが省略された場合はカレントノードが指定されたものと解釈される。
# 親要素ノードのローカル部分がbのコメントノードを選択。 REXML::XPath.match(doc, "//comment()[local-name(..)='b']") # " COMMENT FUGA:B " # " COMMENT B "
namespace-uri(ノードセット=.)引数ノードセットの中の文書順で最初のノードのネームスペース名を 返す。ノードセットが省略された場合はカレントノードが指定された ものと解釈される。
# アンダーバーでもOK。 REXML::XPath.match(doc, "//*[namespace_uri()='http://fuga.com/']/*") # <c number='1'>TEXT</c>
name(ノードセット=.)引数ノードセットの中の文書順で最初のノードの展開名を表す修飾された 名前を返す。 ノードセットが省略された場合はカレントノードが指定された ものと解釈される。
# 展開名を表す修飾された名前がbの要素ノードを選択。 REXML::XPath.match(doc, "//*[name()='b']") # <b><!-- COMMENT B --></b>
string(オブジェクト=.)オブジェクトがノードセットなら、文書順で最初のノードの文字列値 を返す。それ以外の値は、そのまま文字列にしたものを返す。例えば、 1なら"1"を返し、真なら"true"を返す。引数を省略したときはカレン トノードが指定されたものと解釈される。が、REXMLでは引数を省略 したときは空文字を返す。
# number属性の属性値が1の要素ノードを選択。 REXML::XPath.match(doc, "//*[string(@number)='1']") # <c number='1'>TEXT</c>
concat(文字列,文字列, ...)引数を連結したものを返す。引数は二個以上指定できる。
# テキストがTEXTであるテキストノードを選択。
REXML::XPath.match(doc, "//text()[string(.)=concat('TE', 'X', 'T')]")
# "TEXT"
starts-with(文字列1,文字列2)文字列1が文字列2で始まるときに真を返す。
# 展開名を表す修飾された名前がcから始まる要素ノードを選択。 REXML::XPath.match(doc, "//*[starts-with(name(),'c')]") # <c number='1'>TEXT</c>
contains(文字列1,文字列2)文字列1の中に文字列2が含まれているときに真を返す。
# コメントの内容にBという文字が含まれているコメントノードを選択。 REXML::XPath.match(doc, "//comment()[contains(string(.), 'B')]") # " COMMENT FUGA:B " # " COMMENT B "
substring-before(文字列1,文字列2)文字列1の中で文字列2に最初にマッチした箇所の前にある部分文字 列を返す。文字列1に文字列2が含まれないときは空文字列を返す。
# 展開名を表す修飾された名前がaの要素ノードを選択。
REXML::XPath.match(doc, "//*[name()=substring_before('abc','b')]")
# ルート要素aを選択。
substring-after(文字列1,文字列2)文字列1の中で文字列2に最初にマッチした箇所の後にある部分文字 列を返す。文字列1に文字列2が含まれないときは空文字列を返す。
# 展開名を表す修飾された名前がcの要素ノードを選択。
REXML::XPath.match(doc, "//*[name()=substring_after('abc','b')]")
# <c number='1'>TEXT</c>
substring(文字列,数値1,数値2)文字列の数値1番目から、数値2文字分の部分文字列を返す。 数値2が省略されたときは、数値1番目から文字列の最後までを返す。 先頭の文字は0ではなく、1から始まる。
# 展開名を表す修飾された名前の先頭の文字がcの要素ノードを選択。
REXML::XPath.match(doc, "//*[substring(name(), 1, 1)='c']")
# <c number='1'>TEXT</c>
# 文字列値がTEXTのテキストノードを選択。
REXML::XPath.match(doc, "//text()[substring('abcdeTEXT', 6)=string(.)]")
# "TEXT"
string-length(文字列)文字列の文字数を返す。文字列を省略すると、カレントノードの文 字列値の文字数を返す。REXMLでは文字列を省略できない。
# 文字列値の最後の文字がAのコメントノードを選択。 REXML::XPath.match(doc,"//comment()[substring(string(.),string_length(.)-2)='A']") # "COMMENT A"
normalize-space(文字列)文字列の先頭と末尾の空白文字を取り除き、連続した空白文字を1つの スペースに置換した文字列を返す。文字列を省略すると、カレントノー ドの文字列値を対象する。
# 文字列値が"COMMENT A"のコメントノードを選択。
REXML::XPath.match(doc, "//comment()[string(.)=normalize_space('\n COMMENT \n A \n\n ')]")
# "COMMENT A"
translate(文字列1,文字列2,文字列3)文字列1の中の文字列2のn番目の文字を文字列3のn番目の文字で置換 する。文字列3の文字数が文字列2より少ないときは余った文字列2は 空文字列に対応する。例えば、 translate("abcdefg","abcd","AB")は"ABefg"を返す。
# 文字列値がTEXTの子テキストノードを持つ要素ノードを選択する。
REXML::XPath.match(doc, "//*[./text()=translate('tzEzXzt','tz','T')]")
# <c number='1'>TEXT</c>
boolean(オブジェクト)オブジェクトが数値ならゼロ、あるいはNaN(Not a Number)の時に、 ノードセットなら一つもノードが含まれていない時に、文字列なら空 文字列の時に、ブーリアンなら偽の時に、偽を返す(REXMLではNaNの 時でも真を返す)。それ以外は真になる。
# self軸は常に一つしかノードを含まないので二番目のノードはない。 REXML::XPath.match(doc, "//node()[boolean(self::node()[2])]") # 空ノードセットなので偽になり、どのノードも選択されない。
not(ブーリアン)ブーリアンの否定を返す。
true()真を返す。
false()偽を返す。
# not false and true REXML::XPath.match(doc, "//node()[not(false()) and true()]") # 全てのノードを選択。
lang(文字列)大文字小文字を区別せず、xml:lang属性の値が文字列によっ て始まっていたら真を返す。カレントノードがxml:lang属性 を持っていなかったら直近の祖先の値を用いる。もし、 xml:lang属性が見付からなかった場合は偽を返す。 REXMLでは正常に動作しない。
number(オブジェクト)オブジェクトを数値に変換する。オブジェクトの種類によって大き く四つに分けらる。
sum(ノードセット)ノードセットの各ノードのnumber()関数の結果の総和を返す。 REXMLでは実装されていない。
floor(数値)引数より小さい最大の整数を返す。例えば、floor(2.3)は2を 返す。REXMLでは引数の数が違うし、動作もしない。
ceiling(数値)引数より大きい最小の整数を返す。例えば、ceiling(2.3)は3 を返す。REXMLでは引数の数が違うし、動作もしない。
round(数値)数値にもっとも近い整数を返す。もっとも近い整数が二つある場 合は大きい方を返す。数値がNaN、正の無限大、負の無限大、正のゼ ロ、負のゼロの場合はそのものを返す。例えば、round(2.3)は 2を返す。REXMLでは引数の数が違うし、動作もしない。