For Development HEAD DRAFTSearch (procedure/syntax/module):

12.60 sxml.sxpath - SXMLクエリ言語

Module: sxml.sxpath

SXPathは、XML Information set (Infoset)のインスタンスのS式フォームである SXMLのためのクエリ言語です。

これは最初にOleg Kiselyovによって書かれ、Dmitry LizorkinとKirill Lisovsky によって改良されました。 このモジュールにはまた、Dmitry LizorkinとKirill LisovskyによりSXPathのために 書かれたたくさんの手続きが盛り込まれています。

現在のバージョンは、sxpathlib.scm v3.915、sxpath.scm v1.1、sxpath-ext.scm v1.911を ベースにしています。

このマニュアルは、そのほとんどがオリジナルのソースファイルのコメントより 導出されています。

このモジュールは3つのレイヤから構成されます。

  1. SXMLツリーへのアクセスやその変換の手段を提供する基本的なコンバータや アプリケータ(適用子)。
  2. 省略形のSXPathを取り、与えられたノードセットから指定されたパスを満足する ノードセットを選択するScheme関数を返す、高レベルなクエリ言語コンパイラ。
  3. W3CのXPathコア関数ライブラリのSXML版を実装する拡張ライブラリ。

12.60.1 SXPathの基本的なコンバータとアプリケータ

コンバータは、以下を満たす関数です。

  type Converter = Node|Nodeset -> Nodeset

コンバータは、述語としての役割を担うこともあります。 その場合、コンバータが、ノードやノードセットに適用され、空ではないノードセットを 返す場合、述語としてのコンバータは満足したものとみなされます。 このファイルを通して、nilノードセットは失敗を表す#fと等価です。

Function: nodeset? x

{sxml.sxpath} 与えられたオブジェクトがノードセットならば、#tを返します。

Function: as-nodeset x

{sxml.sxpath} xがノードセットならば、それをそのまま返し、そうでなければそれを リストでラップして返します。

Function: sxml:element? obj

{sxml.sxpath} objがSXMLの要素であれば#tを返し、そうでなければ#fを 返す述語です。

Function: ntype-names?? crit

{sxml.sxpath} 関数ntype-names??は、判定基準として受け付け可能なノード名のリストを取り、 関数を返します。この関数は、ノードに適用された際、そのノード名が判定基準リストに 含まれていれば#tを、含まれていなければ#fを返す関数です。

 ntype-names?? :: ListOfNames -> Node -> Boolean
Function: ntype?? crit

{sxml.sxpath} 関数ntype??は、型に関する判定基準を取り、関数を返します。 この関数は、ノードに適用された際、そのノードがそのテストを満足するかを 返します。

  ntype?? :: Crit -> Node -> Boolean

判定基準critは、以下のシンボルのうちの1つです。

id

そのノードが正しい名前(id)を持っているかをテストします。

@

そのノードがattributes-listであるかをテストします。

*

そのノードがElementであるかをテストします。

*text*

そのノードがテキストノードであるかをテストします。

*data*

そのノードがデータノード(テキスト、数値、真偽値などで、ペアではない)であるか をテストします。

*PI*

そのノードがPIノードであるかをテストします。

*COMMENT*

そのノードがCOMMENTノードであるかをテストします。

*ENTITY*

そのノードがENTITYノードであるかをテストします。

*any*

どんなタイプのノードに対しても#tを返します。

Function: ntype-namespace-id?? ns-id

{sxml.sxpath} この関数は、名前空間IDを取り、述語Node -> Booleanを 返します。この述語はまさにその名前空間IDを持つノードに対しては #tを返します。ns-idは文字列です。 (ntype-namespace-id?? #f)は、完全修飾されていない名前を 持つノードに対して#tを返します。

Function: sxml:invert pred

{sxml.sxpath} この関数は、述語を取り、それを反対にして返します。 与えられた述語が#fや’()を返す場合、反対にされたものは 与えられたノード(#t)を返します。

Function: node-eq? other
Function: node-equal? other

{sxml.sxpath} 等価な述語としてのコンパータにカリー化します。すなわち、

  ((node-eq? a) b)    ≡ (eq? a b)
  ((node-equal? a) b) ≡ (equal? a b)
Function: node-pos n

{sxml.sxpath}

 node-pos:: N -> Nodeset -> Nodeset, or
 node-pos:: N -> Converter

ノードセットのN番目の要素を選択し、1つの要素を持つノードセットを返します。 N番目の要素が存在しなければ、空のノードセットを返します。 ((node-pos 1) Nodeset)は、ノードセットの先頭ノードがあればそれを選択します。 ((node-pos 2) Nodeset)は、2番目のノードがあればそれを選択します。 Nは負の数でも構いません。その場合、ノードはリストの末尾から数えられます。 ((node-pos -1) Nodeset)は、空ではないノードセットの最後のノードを選択します。 ((node-pos -2) Nodeset)は、最後から2番目のノードがあればそれを選択します。

Function: sxml:filter pred?

{sxml.sxpath}

 filter:: Converter -> Converter

フィルタリングを行う、フィルタアプリケータです。 引数のコンバータは、#fあるいはnilとなることが失敗を意味する述語と みなされます。

Function: take-until pred?

{sxml.sxpath}

 take-until:: Converter -> Converter, or
 take-until:: Pred -> Node|Nodeset -> Nodeset

述語としてのコンバータとノードセットが与えられると、 ノードセットの各要素に述語を適用し、 述語が#fあるいはnil以外を返すと、 (その述語が失敗した)その時点までに処理された要素を返します。 take-untilは、上のフィルタのバリエーションの1つです。 take-untilは、その述語を満足する最初の要素(それ自体は含まない)まで、 順序付けられた入力のセットの要素をパスします。 ((take-until (not pred)) nset)により返されるノードセットは、 ((filter pred) nset)により返されるノードセットのサブセット – 具体的には接頭辞 –になります。

Function: take-after pred?

{sxml.sxpath}

take-after:: Converter -> Converter, or
take-after:: Pred -> Node|Nodeset -> Nodeset

述語としてのコンバータとノードセットを与えると、 述語をノードセットの各要素に適用し、 述語が#fかnil以外を返すと、 まだ述語が適用されていない要素を返します。 つまり、述語を満足する最初の要素の後に続く要素を返します。 take-aftertake-untilを一緒に使うと、 入力のノードセットを3つのパート: 述語を満足する最初の要素、その要素の前の部分、その要素の後の部分に 分けます。

Function: map-union proc lst

{sxml.sxpath} procをlstの各要素に適用し、結果のリストを返します。 procがノードセットを返す場合、それを結果につなぎ合わせます。

別の観点から見ると、map-unionはConverter->Converter関数で、 結合を行いたいコンテキストでの引数としてのコンバータに 位置します。

Function: node-reverse node-or-nodeset

{sxml.sxpath}

node-reverse :: Converter, or
node-reverse:: Node|Nodeset -> Nodeset

ノードセットでのノードの順番を逆順にします。 この基本的なコンバータは、逆順のドキュメントオーダーを実装するために 必要です。(XPath勧告を参照して下さい。)

Function: node-trace title

{sxml.sxpath}

 node-trace:: String -> Converter

(node-trace title)は、それ自身を返すコンバータです。 また、自身が適用されるノードやノードセットを、’title’という プリフィックスを付けてプリントします。 このコンバータは、デバッグの際にとても便利です。

コンバータの組み合わせに続くものは、コンバータを一変させる、 あるいはコンバータのシーケンスを1つの強力なコンバータにつなぎ合わせる 高階関数です。そのゴールは、XPathのロケーションパスに対応する コンバータとなることです。

別の観点から見ると、コンバータは、コンバータ群の適用の固定され 名前の付いたパターンとみなせます。 以下に挙げるのは、XPathのロケーションパスの仕様を実装する そのようなパターンの完全なセットです。 結局のところ、これら全てのコンビネータはいくつかの基本的なブロック、 通常の関数的なコンポジション、map-unionとfilterアプリケータ、 ノードセットユニオンなどから構築することができます

Function: select-kids test-pred?

{sxml.sxpath}

select-kids:: Pred -> Node -> Nodeset

ノードを与えると、述語(実際はコンバータ)を満足するその子要素の (順序付けられた)サブセットを返します。

select-kids:: Pred -> Nodeset -> Nodeset

上と同じですが、ノードセットの全てのノードの子要素から選択します。

Function: node-self pred

{sxml.sxpath}

 node-self:: Pred -> Node -> Nodeset, or
 node-self:: Converter -> Converter

select-kidsに似ていますが、自身をその子要素に適用するのでは なく、ノードそれ自身に適用します。 結果のノードセットは、1つのコンポーネントを含むか、 空(ノードが述語を満足しない場合)になります。

Function: node-join . selectors

{sxml.sxpath}

 node-join:: [LocPath] -> Node|Nodeset -> Nodeset, or
 node-join:: [Converter] -> Converter

上のタイトルコメントで説明されるようなロケーションステップ あるいはロケーションパスのシーケンスをつなぎ合わせます。

Function: node-reduce . converters

{sxml.sxpath}

 node-reduce:: [LocPath] -> Node|Nodeset -> Nodeset, or
 node-reduce:: [Converter] -> Converter

コンバータの通常の関数的なコンポジションです。 見方を変えると、((apply node-reduce converters) nodeset)(foldl apply nodeset converters)と等価です。 すなわち、コンバータのリストをノードセットをseedとして畳み込みや分解 を行うようなものです。

Function: node-or . converters

{sxml.sxpath}

 node-or:: [Converter] -> Converter

このコンビネータは、全てのコンバータを与えられたノードに適用し、 それらの結果のユニオンを作ります。 このコンビネータは、XPathのロケーションパスでの’|’オペレーション であるユニオンに対応します。

Function: node-closure test-pred?

{sxml.sxpath}

 node-closure:: Converter -> Converter

述語としてのコンバータを満足するノードの全ての子孫を選択します。 このコンビネータはselect-kidsに似ていますが、孫要素やその 子要素達にも適用を行います。 このコンビネータは、XPathの軸である“descendant::”を実装します。 概念的には、このコンビネータは以下のように表現することができます。

 (define (node-closure f)
      (node-or
        (select-kids f)
         (node-reduce (select-kids (ntype?? '*)) (node-closure f))))

この定義は、字面の通り、フィックスポイントのような何かで、 永久に実行し続けます。しかし、いつかは(select-kids (ntype?? '*)) が空のノードセットを返すことは明白です。その時点では、以降の イテレーションはその結果に影響を及ぼさず停止されることができます。


12.60.2 SXPathクエリ言語

Function: sxpath abbrpath . ns-binding

{sxml.sxpath} Evaluates an abbreviated SXPath. Returns a procedure that when applied on a node or nodeset will return a nodeset matching the given path.

 sxpath:: AbbrPath -> Converter, or
 sxpath:: AbbrPath -> Node|Nodeset -> Nodeset

AbbrPath is a list or a string. If it is a list, it is translated to the full SXPath according to the following rewriting rules. More informal explanation follows shortly. If it is a string, it is an XPath query.

Note that these are abstract rules to show how it works, and not the running code examples. The nonterminals sxpath1 and sxpathr don’t exist as APIs. The term txpath is an internal function that interprets XPath query given as a string.

 (sxpath '()) -> (node-join)
 (sxpath '(path-component ...)) ->
                (node-join (sxpath1 path-component) (sxpath '(...)))
 (sxpath1 '//) -> (node-or
                     (node-self (ntype?? '*any*))
                     (node-closure (ntype?? '*any*)))
 (sxpath1 '(equal? x)) -> (select-kids (node-equal? x))
 (sxpath1 '(eq? x))    -> (select-kids (node-eq? x))
 (sxpath1 '(or@ ...))  -> (select-kids (ntype-names??
                                          (cdr '(or@ ...))))
 (sxpath1 '(not@ ...)) -> (select-kids (sxml:invert
                                         (ntype-names??
                                          (cdr '(not@ ...)))))
 (sxpath1 '(ns-id:* x)) -> (select-kids
                                      (ntype-namespace-id?? x))
 (sxpath1 ?symbol)     -> (select-kids (ntype?? ?symbol))
 (sxpath1 ?string)     -> (txpath ?string)
 (sxpath1 procedure)   -> procedure
 (sxpath1 '(?symbol ...)) -> (sxpath1 '((?symbol) ...))
 (sxpath1 '(path reducer ...)) ->
                (node-reduce (sxpath path) (sxpathr reducer) ...)
 (sxpathr number)      -> (node-pos number)
 (sxpathr path-filter) -> (filter (sxpath path-filter))

SXPath in its simplest form is a list of path components. The result procedure will follow the same path and return the matching node list. For example (one two three) will find element one then two inside it and three inside element two. The equivalent XPath would be one/two/three.

There are a few special path components (see ntype?? for the complete list):

*

matches an element node.

//

matches any one or many consecutive path components.

*text*

matches a text node (text() in XPath).

*data*

matches any data node (e.g. text, number, boolean, etc., but not pair).

@

selects the attribute list node.

A path component could be a list in one of these forms:

(equal? x)

matches if the node under examination matches x using node-equal?

(eq? x)

matches if the node under examination matches x using node-eq?

(or@ ...)

matches if the element name is one of the specified symbols.

(not@ ...)

matches if the element name is not one of the specified symbols.

(ns-id:* x)

matches the node if it’s with namespace x

(<path> n)

matches the n-th node matching same path component. n starts from 1. Negative numbers start from the end of the node list backward. This is path[n] syntax in XPath.

(<path> (<predicate>...))

matches a path component path and (sxpath (<predicate>...)) on those nodes are not empty. This is path[predicate...] syntax in XPath.

If the path component is a string, it is interpreted as an XPath query string.

If the path component is a procedure, the procedure takes three arguments: the nodeset being examined, the root node and the variable bindings.

The root node is usually the entire sxml being applied. However if you apply the result sxpath procedure with two arguments, root-node will be the second argument.

When applied with three arguments, the variable bindings are the third one. This lets you pass arguments to the procedure.

;; select all <book> elements whose style attribute value is equal to
;; the <bookstore> element's specialty attribute value.
(sxpath "//book[/bookstore/@specialty=@style]")
;; a similar query but this time make sure specialty of _all_
;; bookstores is matched
(let ([match-specialty
       (lambda (node root var-binding)
         (let ([style (car ((sxpath '(@ style *text*)) node))]
               [all-specialty ((sxpath '(bookstore @ specialty *text*)) node)])
           (fold (lambda (specialty last-result)
                   (and last-result (string=? style specialty)))
                        #t
                        all-specialty)))])
  (sxpath `(// (book (,match-specialty)))))

;; select all <bookstore> elements that are inside top-level <book>
;; element
(sxpath '(book bookstore))
;; select all <bookstore> elements from anywhere
(sxpath '(// bookstore))
;; select attribute "name" in the top-level <book> element
(sxpath '(book @ name))
;; select all <bookstore> and <bookshop> elements that are inside
;; top-level <book> element
(sxpath '(book (or@ bookstore bookshop)))
;; select all elements except <movie> that are inside top-level <book>
;; element
(sxpath '(book (not@ movie @)))
;; select the attribute "name" of the second <bookstore> element
(sxpath '(book (bookstore 2) @ name))
;; select the attribute "name" of all <bookstore> elements that has
;; attribute "recommended"
(sxpath '(book (bookstore (@ recommended)) @ name))
;; select the attribute "name" of all <bookstore> elements whose
;; "rating" attribute is 3
(sxpath '(book (bookstore (@ rating (eq? 3))) @ name))
;; select the attribute "rating" whose value is greater than 3 from
;; all <bookstore> elements
(let ([greater (lambda (nodeset root-node var-binding)
                 (filter (lambda (node)
                           (> (string->number (sxml:string-value node))
                              3))
                         nodeset))])
  (sxpath `(book bookstore @ rating ,greater)))

sxpathには、いくつかのラッパ関数があります。

Function: if-sxpath path

{sxml.sxpath} sxpathは、常にリストを返し、それはSchemeでは#tとなります。 if-sxpathは、空リストの代わりに#fを返します。

Function: if-car-sxpath path

{sxml.sxpath} もし存在すれば、最初に見つかったノードを返します。 そうでなければ、#fを返します。

Function: car-sxpath path

{sxml.sxpath} もし存在すれば、最初に見つかったノードを返します。 そうでなければ、空リストを返します。

Function: sxml:id-alist node . lpaths

{sxml.sxpath} 与えられたノードについて、(ID_value . element)の ペアのリストをインデックスとして構築します。 lpathsは、タイプIDの属性のロケーションパスです。


12.60.3 SXPathの拡張

W3CのXPathコア関数ライブラリのSXML版です。

Function: sxml:string object

{sxml.sxpath} XPathのstring関数(XPath勧告のセクション4.2)に対応するものです。 与えられたオブジェクトを文字列に変換します。 注意:

  1. ノードセットを変換する時は、ドキュメントオーダーは保持されません。
  2. number->string関数は、その結果をXPath勧告の仕様とは少し違った フォームで返します。
Function: sxml:boolean object

{sxml.sxpath} XPathのboolean関数(XPath勧告のセクション4.3)に対応するものです。 引数を真偽値に変換します。

Function: sxml:number obj

{sxml.sxpath} XPathのnumber関数(XPath勧告のセクション4.4)に対応するものです。 引数を数値に変換します。 注意:

  1. 引数は(まだ?)オプションではありません。
  2. string->numberの変換は、IEEE 754の四捨五入ではありません。
  3. NaNは、0として表現されます。
Function: sxml:string-value node

{sxml.sxpath} XPath勧告のセクション5.1 - 5.7にしたがって、与えられたノードの 文字列値を返します。

Function: sxml:node? node

{sxml.sxpath} XPathの仕様2.3にしたがい、このテストはいかなるXPathノードに 対しても真を返します。 SXMLの補助的なリストや属性のリストは除外されます。

Function: sxml:attr-list obj

{sxml.sxpath} 与えられたSXMLノードの属性のリストを返します。 与えられたノードが要素ではないか、属性のリストを持っていない場合は、 空リストが返されます。

Function: sxml:id id-index

{sxml.sxpath} SXML要素を、そのユニークなIDによって選択します(XPath勧告 4.1)。 objectを引数に取るコンバータを返します。 このobjectは、ノードセットか、’string’関数により 文字列に変換できるデータタイプです。

id-indexは、( (id-value . element) (id-value . element) ... )です。

このインデックスは、要素をそのユニークなIDによって選択するために使われます。

XPathオブジェクトの比較子:

Function: sxml:equality-cmp bool-op number-op string-op

{sxml.sxpath} XPathの等値比較: =!=のためのヘルパです。 bool-opnumber-op’string-opはそれぞれ、 真偽値、数値、文字列のペアのための比較子です。

Function: sxml:equal? a b
Function: sxml:not-equal? a b

{sxml.sxpath} XPathの等値比較: =!=に対応するもので、 デフォルトの等値テストを使います。

Function: sxml:relational-cmp op

{sxml.sxpath} 2つのXPathオブジェクトの関係比較( <><=>= ) を作ります。 opは、比較を行う手続き: <><=>=です。

XPathの軸。 結果のノードセットにおける順序は維持されます。

Function: sxml:attribute test-pred?

{sxml.sxpath} 属性の軸です。

Function: sxml:child test-pred?

{sxml.sxpath} 子要素の軸です。 この関数は、’select-kids’に似ていますが、処理命令やコメント、 実体ノードについては、空の子リストを返します。

Function: sxml:parent test-pred?

{sxml.sxpath} 親の軸です。

述語を与えると、RootNode -> Converter関数を返します。 この関数は、rootnodeに適用されると、node -> parentと なります。

このようなコンバータは、 ((sxml:parent test-pred) rootnode) を使って構築され、それが適用されたノードの親を帰します。 ノードセットに適用された場合、そのノードセットにあるノードの 親のリストを返します。 rootnodeはSXMLツリー全体のルートノードである必要はありません。 興味の対象となるブランチ(枝)のルートノードでも構いません。 parent::軸は、どんなSXMLノードにも使えます。

Function: sxml:ancestor test-pred?

{sxml.sxpath} 祖先の軸です。

Function: sxml:ancestor-or-self test-pred?

{sxml.sxpath} 祖先と自分の軸です。

Function: sxml:descendant test-pred?

{sxml.sxpath} 子孫の軸です。

Function: sxml:descendant-or-self test-pred?

{sxml.sxpath} 子孫と自分の軸です。

Function: sxml:following test-pred?

{sxml.sxpath} 後続するものの軸です。

Function: sxml:following-sibling test-pred?

{sxml.sxpath} 後続する兄弟の軸です。

Function: sxml:namespace test-pred?

{sxml.sxpath} 名前空間の軸です。

Function: sxml:preceding test-pred?

{sxml.sxpath} 先行するものの軸です。

Function: sxml:preceding-sibling test-pred?

{sxml.sxpath} 先行する兄弟の軸です。

ポピュラーなショートカット:

Function: sxml:child-nodes nodeset

{sxml.sxpath}

((sxml:child sxml:node?) nodeset)
Function: sxml:child-elements nodeset

{sxml.sxpath}

((select-kids sxml:element?) nodeset)


For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT