XQuery 中的 element() 与 node() [英] element() vs. node() in XQuery

查看:26
本文介绍了XQuery 中的 element() 与 node()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人能告诉我 XQuery 中 node()element() 类型之间的确切区别吗?文档指出 element() 是一个元素节点,而 node() 是任何节点,所以如果我理解正确 element()node() 的子集.

Can someone tell me the exact difference between node() and element() types in XQuery? The documentation states that element() is an element node, while node() is any node, so if I understand it correctly element() is a subset of node().

问题是我有一个这样的 XQuery 函数:

The thing is I have an XQuery function like this:

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

现在我想用另一个函数获得的参数调用该函数,比如functionX(我无法控制):

Now I want to call the function with a parameter which is obtained by another function, say functionX (which I have no control over):

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

问题是,functionX 返回一个 node() 所以它不会让我直接传递 $parameter.我尝试将函数的类型更改为使用 node() 而不是 element(),但是我似乎无法从中读取任何数据.$value 只是空的.

The problem is, functionX returns an node() so it will not let me pass the $parameter directly. I tried changing the type of my function to take a node() instead of an element(), but then I can’t seem to read any data from it. $value is just empty.

是否有某种方法可以将节点转换为元素,或者我是否应该只是遗漏了什么?

Is there some way of either converting the node to an element or should am I just missing something?

据我所知,问题出在我尝试使用 $arg1/subelement 获取子元素的部分.显然,如果 $arg1element() 则可以这样做,但如果它是 node() 则不行.

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().

更新:我已经测试了下面 Dimitre 提供的示例,它确实在 Saxon 和 eXist DB(这是我用作 XQuery 引擎的情况下)都运行良好.该问题实际上发生在 eXist DB 的 request:get-data() 函数中.此函数通过REST 获取使用eXist 时POST 请求提供的数据,将其解析为XML 并将其作为node() 返回.但是由于某种原因,当我将数据传递给另一个函数时,XQuery 并不承认它是一个有效的 element(),即使它是.如果我手动提取它(即复制输出并将其粘贴到我的源代码中),请将其分配给一个变量并将其传递给我的函数,一切顺利.但是如果我直接通过它,它会给我一个运行时错误(并且确实没有通过 instance of 测试).

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().

I need to be able to either make it ignore this type-check or "typecast" the data to an element().

推荐答案

data() 仅仅因为参数类型是 node() 而为元素返回空值听起来像对我来说是一个错误.您使用的是什么 XQuery 处理器?

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 的动态测试就足够了.

It sounds like you need to placate static type checking, which you can do using a treat as expression. I don't believe a dynamic test using instance of will suffice.

试试这个:

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

引自 Michael Kay 的代表作第 4 版,treat as 操作符本质上是告诉系统您知道运行时类型是什么,并且您希望进行任何检查推迟到运行时,因为你确信你的代码是正确的."(第 679 页)

Quoting from the 4th edition of Michael Kay's magnum opus, "The treat as operator is essentially telling the system that you know what the runtime type is going to be, and you want any checking to be deferred until runtime, because you're confident that your code is correct." (p. 679)

更新:我认为以上实际上是错误的,因为 treat as 只是一个断言.它不会改变类型注释 node(),这意味着它也是一个错误的断言,对你没有帮助.嗯...我真正想要的是 cast as,但这仅适用于原子类型.我想我被难住了.也许您应该更改 XQuery 引擎.:-) 如果我想到别的东西,我会回来报告的.另外,我很想知道 Dimitre 的解决方案是否适合您.

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.

更新 #2: 我早些时候在这里退步了.我可以再退一步吗?;-) 现在我的理论是 treat as will 基于 node() 被解释为各种特定的联合节点类型注释,而不是作为运行时类型注释本身(请参阅项目类型"中的注释"" 部分 XQuery 形式语义.)在运行时,类型注释将是 element().使用 treat as 向类型检查器保证这将是真的.现在我屏住呼吸等待:它对你有用吗?

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?

解释性附录: 假设这有效,原因如下.node() 是联合类型.运行时的实际项目永远不会使用 node() 进行注释.项类型可以是原子类型、元素类型、属性类型、文档节点类型、文本节点类型、注释节点类型或处理指令类型."1 请注意,node() 不在该列表中.因此,您的 XQuery 引擎不会抱怨某个项目的类型为 node();而是抱怨它不知道类型将是什么(node() 意味着它可能最终成为 attribute()element()text()comment()processing-instruction()document-node()).为什么它必须知道?因为你在别处告诉它它是一个元素(在你的函数签名中).将其缩小到上述六种可能性之一是不够的.静态类型检查意味着您必须在编译时保证类型匹配(在本例中为元素与元素).treat as 用于将静态类型从一般类型(node())缩小到更具体的类型(element()).它不会改变动态类型.另一方面,cast as 用于将项目从一种类型转换为另一种类型,同时更改静态和动态类型(例如,xs:string 到 xs:boolean).cast as 只能用于原子值(而不是节点)是有道理的,因为将属性转换为元素(等)意味着什么?并且没有将 node() 项转换为 element() 项这样的事情,因为没有这样的事情 node()物品.node() 仅作为静态联合类型存在.故事的道德启示?避免使用静态类型检查的 XQuery 处理器.(抱歉得出了尖锐的结论;我觉得我赢得了权利.:-))

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. :-) )

基于更新信息的新答案:听起来静态类型检查是一个红鲱鱼(一个很大的脂肪).我相信你实际上不是在处理一个元素,而是一个文档节点,它是包含XPath中顶级元素(文档元素)的不可见根节点格式良好的 XML 文档的数据模型表示.

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]
             |
        <docElement>
             |
        <subelement>

不是像这样:

        <docElement>
             |
        <subelement>

我假设您正在传递 节点.但如果我是对的,你实际上是在传递文档节点(它的父节点).由于文档节点是不可见的,它的序列化(您复制和粘贴的内容)与元素节点无法区分,并且当您粘贴现在被解释为 XQuery 中的裸元素构造函数时,这种区别就消失了.(要在 XQuery 中构造文档节点,您必须用 document{ ... } 包装元素构造函数.)

I had assumed you were passing the <docElement> 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(),因为没有这样的东西;请参阅上面的解释.)

The instance of test fails because the node is not an element but a document-node. (It's not a node() per se, because there's no such thing; see explanation above.)

此外,这也解释了为什么当您尝试获取文档节点的 子节点时 data() 返回空的原因(在放宽函数参数类型后)node()).上面的第一个树表示表明 不是文档节点的子节点;因此它返回空序列.

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

现在是解决方案.在传递 (document node) 参数之前,通过附加 /* (或等效的 /element() )来获取它的元素子元素(文档元素),如下所示:

Now for the solution. Before passing the (document node) parameter, get its element child (the document element), by appending /* (or /element() which is equivalent) like this:

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

或者,让您的函数接受一个文档节点并更新您传递给 data() 的参数:

Alternatively, let your function take a document node and update the argument you pass to data():

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

最后,它看起来像 eXist 的 request:get-data() 函数的描述 完全符合这个解释.它说:如果它不是二进制文档,我们尝试将其解析为 XML 并返回一个 document-node()."(强调)

Finally, it looks like the description of eXist's request:get-data() function is perfectly consistent with this explanation. It says: "If its not a binary document, we attempt to parse it as XML and return a document-node()." (emphasis added)

感谢您的冒险.结果证明这是一个常见的 XPath 问题(文档节点的意识),但我从绕道进入静态类型检查中学到了一些东西.

Thanks for the adventure. This turned out to be a common XPath gotcha (awareness of document nodes), but I learned a few things from our detour into static type checking.

这篇关于XQuery 中的 element() 与 node()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆