XQueryのelement()とnode()の比較

XQueryの node()型と element()型の正確な違いを教えてもらえますか? element()は要素ノードであり、 node()は任意のノードなので、 node()のサブセットです。

私はこのようなXQuery関数を持っています:

declare function local:myFunction($arg1 as element()) as element() {
    let $value := data($arg1/subelement)
   etc...
};

他の関数、例えば functionX (これは私が制御できない)によって得られたパラメータで関数を呼び出すことにします:

let $parameter := someNamespace:functionX()
return local:myFunction($parameter)

問題は、 functionX $ parameter を直接渡すことができないように node()を返すことです。 element()の代わりに node()を使用するように関数の型を変更しようとしましたが、そのデータを読み取ることができません。 $ value は空白です。

ノードを要素に変換する何らかの方法がありますか?何かが欠落していますか?

EDIT: As far as I can tell the problem is in the part where I try to get the subelement using $arg1/subelement. Apparently you can do this if $arg1 is an element() but not if it is a node().

UPDATE: I have tested the example provided by Dimitre below, and it indeed works fine, both with Saxon and with eXist DB (which is what I am using as the XQuery engine). The problem actually occurs with the request:get-data() function from eXist DB. This function gets data provided by the POST request when using eXist through REST, parses it as xml and returns it as a node(). But for some reason when I pass the data to another function XQuery doesn’t acknowledge it as being a valid element(), even though it is. If I extract it manually (i.e. copy the output and paste it to my source code), assign it to a variable and pass it to my function all goes well. But if I pass it directly it gives me a runtime error (and indeed fails the instance of test).

私は、この型チェックを無視するか、データを element()に「型キャスト」することができるようにする必要があります。

5
@ NobleK:あなたは私の答えを読んで、それは役に立ちましたか?
追加された 著者 Dimitre Novatchev,
@NobleK、あなたが提供した新しい情報に基づいて答えを更新しました。私たちは私たちの謎を解決したと信じています。 :-)
追加された 著者 Evan Lenz,
こんにちはDimitre。私はそれを読んだが、私は仕事をしているのでまだテストする機会はなかった。私は今晩遅くそれを見て、あなたの所見が何であるかを教えてくれるでしょう。
追加された 著者 pajevic,

3 答え

data() returning empty for an element just because the argument type is node() sounds like a bug to me. What XQuery processor are you using?

あなたは treat as の式を使って静的型検査を行う必要があるようです。私は、 instance of を使った動的テストで十分であるとは思いません。

これを試して:

let $parameter := someNamespace:functionX() treat as element()
return local:myFunction($parameter)

treat as 」演算子は、基本的に、実行時の型がどのようなものになるかを知っていることをシステムに伝えており、実行時まですべてのチェックを延期したいあなたのコードが正しいと確信しているからです。 (p。679)

UPDATE: I think the above is actually wrong, since treat as is just an assertion. It doesn't change the type annotation node(), which means it's also a wrong assertion and doesn't help you. Hmmm... What I really want is cast as, but that only works for atomic types. I guess I'm stumped. Maybe you should change XQuery engines. :-) I'll report back if I think of something else. Also, I'm curious to find out if Dimitre's solution works for you.

UPDATE #2: I had backpedaled here earlier. Can I backpedal again? ;-) Now my theory is that treat as will work based on the fact that node() is interpreted as a union of the various specific node type annotations, and not as a run-time type annotation itself (see the "Note" in the "Item types" section of the XQuery formal semantics.) At run time, the type annotation will be element(). Use treat as to guarantee to the type checker that this will be true. Now I wait on bated breath: does it work for you?

EXPLANATORY ADDENDUM: Assuming this works, here's why. node() is a union type. Actual items at run time are never annotated with node(). "An item type is either an atomic type, an element type, an attribute type, a document node type, a text node type, a comment node type, or a processing instruction type."1 Notice that node() is not in that list. Thus, your XQuery engine isn't complaining that an item has type node(); rather it's complaining that it doesn't know what the type is going to be (node() means it could end up being attribute(), element(), text(), comment(), processing-instruction(), or document-node()). Why does it have to know? Because you're telling it elsewhere that it's an element (in your function's signature). It's not enough to narrow it down to one of the above six possibilities. Static type checking means that you have to guarantee—at compile time—that the types will match up (element with element, in this case). treat as is used to narrow down the static type from a general type (node()) to a more specific type (element()). It doesn't change the dynamic type. cast as, on the other hand, is used to convert an item from one type to another, changing both the static and dynamic types (e.g., xs:string to xs:boolean). It makes sense that cast as can only be used with atomic values (and not nodes), because what would it mean to convert an attribute to an element (etc.)? And there's no such thing as converting a node() item to an element() item, because there's no such thing as a node() item. node() only exists as a static union type. Moral of the story? Avoid XQuery processors that use static type checking. (Sorry for the snarky conclusion; I feel I've earned the right. :-) )

NEW ANSWER BASED ON UPDATED INFORMATION: It sounds like static type checking is a red herring (a big fat one). I believe you are in fact not dealing with an element but a document node, which is the invisible root node that contains the top-level element (document element) in the XPath data model representation of a well-formed xml document.

ツリーはこうしてモデル化されます:

      [document-node]
             |
        
             |
        

そうでない

        
             |
        

I had assumed you were passing the node. But if I'm right, you were actually passing the document node (its parent). Since the document node is invisible, its serialization (what you copied and pasted) is indistinguishable from an element node, and the distinction was lost when you pasted what is now interpreted as a bare element constructor in your XQuery. (To construct a document node in XQuery, you have to wrap the element constructor with document{ ... }.)

ノードが要素ではなく文書ノードであるため、 instance of テストは失敗します。 (そのようなものはないので、これは node()ではありません;上記の説明を参照してください)。

Also, this would explain why data() returns empty when you tried to get the child of the document node (after relaxing the function argument type to node()). The first tree representation above shows that is not a child of the document node; thus it returns the empty sequence.

今すぐ解決のために。 (document node)パラメータを渡す前に、次のように/* (または/element()を追加して要素child(document要素)

let $parameter := someNamespace:functionX()/*
return local:myFunction($parameter)

あるいは、関数がドキュメントノードを取得し、渡した引数をdata()に更新します。

declare function local:myFunction($arg1 as document-node()) as element() {
    let $value := data($arg1/*/subelement)
   etc...
};

最後に、 eXistのリクエストの説明:get-data()関数が次のようになっています。この説明と完全に一致しています。 「バイナリ文書ではない場合、XMLとして解析してdocument-node()を返します」というメッセージが表示されます。 (強調が加えられた)

冒険に感謝します。これは一般的なXPathの問題(ドキュメントノードの認識)であることが判明しましたが、私たちは迂回路から静的型チェックにいくつかのことを学びました。

8
追加された
エヴァン、なぜディミトレのソリューションがうまくいくのか疑問に思う?私がコードを投稿する前に必ず行うように、Saxonで実行してください。
追加された 著者 Dimitre Novatchev,
OPanは、彼のコードで、 "$ arg1が要素の場合にこれを行うことができます"という彼の質問に、エヴァンは言います。このステートメントは、 $ arg1 が要素でない場合、ランタイムエラーのみを取得することを意味します。
追加された 著者 Dimitre Novatchev,
私は静的な型チェックを使用しているように見えるOPのXQueryエンジンを使用して動作するかどうかを意味します。その場合、 のインスタンスは、表示されているエラーメッセージを防ぐことはできません。
追加された 著者 Evan Lenz,
私の解釈は、要素を渡したにもかかわらずエラーが発生したことです。つまり、$ arg1が静的に要素であることがわかっていないとエラーが発生します。私たちは待って見なければならないと思う。 :-)
追加された 著者 Evan Lenz,
どういたしまして!それは私のための純粋な楽しみだった。 :-)
追加された 著者 Evan Lenz,
こんにちは、みんな。問題を明確にするために、私が $ arg1 に渡す値には、任意の定義によって有効な element()でなければならないXMLが含まれています。 request:get-data()関数(上記のアップデートの場合)は、単に node()であってもXMLを渡します。この場合、 のインスタンスは "false"型チェックの犠牲になるので問題を解決しません。 として扱うが私が必要とする薬になることを期待していますが、今日後で家に帰るまではテストできません。
追加された 著者 pajevic,
うわー、エヴァン、誠実に本当に素晴らしい仕事に感謝します。私はこれが私が今までに得た最も精巧な答えであると信じています。それは私の問題を解決しただけでなく、最初にそれを引き起こしていたことを私に明らかにしました。そして、あなたはお金の上にいました。/* は仕事をしました。私は実際に request:get-data()のドキュメントを読んでいましたが、 "document"と "node"の間にダッシュを入れずに戻り値の型を node()私のためにすべての混乱を引き起こした:)
追加された 著者 pajevic,
@ノーブル:あなたは大歓迎です。私は、静的型チェック・プロセッサーが有用であるなら、理論的という質問をしてきましたが、それを使った経験は一度もありませんでした。この実用的な例を見てみると、静的なエラーがなければ、しばらくの間「正常に」動作するように見えるXQueryアプリケーションのケースがあり、その後突然生産を開始する可能性があるため、ランタイムエラー。アプリケーションが重要な性質(電子商取引など)である場合、当然予測できない実行時エラーは望ましくありません。
追加された 著者 Dimitre Novatchev,
Dimitreさん、ありがとうございました。私はあなたのコードをテストしており、実際には動作します。私は、xqueryエンジンとしてeXist DBを使用していることが原因である可能性があります。しかし、あなたの例はeXistでもうまくいきました。
追加された 著者 pajevic,

node() is an element, attribute, processing instruction, text node, etc.

しかし、 data()は結果を文字列に変換します。それは基本的なタイプです。

いずれかと一致する item()を試してみるとよいでしょう。

W3C XQueryの 2.5.4.2 ItemTypeとItemのマッチングを参照してください。仕様。

あなたのサンプルコードには表示されていませんが、実際には local:myFunction の値(実際には $ value のように)を返すと仮定します。

1
追加された
クイック修正: data()は、必ずしも文字列ではなく、ゼロ以上のアトミック値のシーケンスを返します。スキーマがない場合、結果は文字列のようですが、xs:untypedAtomicと注釈が付けられます。
追加された 著者 Evan Lenz,
こんにちはlavinio。返信してくれてありがとう。しかし、私は誤解があるかもしれないと思います。この問題は関数の戻り型ではなく、引数$ arg1の型にあります。私がfunctionXから返されるノードを渡せるようにするには、$ arg1の型はnode()でなければなりません。しかし、私はそのデータを読むことができません。これは、型がelement()の場合にはうまく読み取れます。
追加された 著者 pajevic,