xquery - XQuery中的element()与node()

标签 xquery exist-db

有人可以告诉我XQuery中node()element()类型之间的确切区别吗?文档指出element()是元素节点,而node()是任何节点,因此如果我正确理解element()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返回一个node(),所以它不会让我直接传递$parameter。我尝试将函数的类型更改为采用node()而不是element(),但随后似乎无法从中读取任何数据。 $value只是空的。

是否有某种将节点转换为元素的方法,还是我应该丢失某些东西?

编辑:据我所知,问题出在我尝试使用$arg1/subelement获得子元素的部分。显然,如果$arg1element(),则可以执行此操作,但如果它是node(),则不能。

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

我需要能够使它忽略此类型检查,或者将数据“类型转换”为element()

最佳答案

data()仅因为参数类型为node()而为元素返回空,这对我来说似乎是个错误。您正在使用什么XQuery处理器?

听起来您需要进行静态类型检查,可以使用treat as表达式来完成。我不相信使用instance of进行动态测试就足够了。

试试这个:

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


引自迈克尔·凯(Michael Kay)的第四版大著述,“ treat as运算符实质上是在告诉系统您知道运行时类型是什么,并且您希望将任何检查推迟到运行时进行,因为您有信心您的代码是正确的。” (第679页)

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

更新#2:我早些时候在这里退缩了。我可以再次踩踏板吗? ;-)现在我的理论是treat as将基于以下事实工作:node()被解释为各种特定节点类型注释的并集,而不是运行时类型注释本身(请参见“注意”在"Item types" section of the XQuery formal semantics中。)在运行时,类型注释将为element()。使用treat as向类型检查器保证这将是正确的。现在,我屏息呼吸:它对您有用吗?

解释性附录:假设这可行,这就是原因。 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处理器。 (对不起的结论很抱歉;我觉得我已经获得了应得的权利。:-))

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

因此,树的建模如下:

      [document-node]
             |
        <docElement>
             |
        <subelement>


而不是这样:

        <docElement>
             |
        <subelement>


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

instance of测试失败,因为该节点不是元素而是文档节点。 (它本身不是node(),因为没有这样的东西;请参阅上面的说明。)

同样,这也可以解释为什么在尝试获取文档节点的data()子级时(将函数参数类型放宽为<subelement>之后)node()返回空的原因。上面的第一个树表示表示<subelement>不是文档节点的子级;因此它返回空序列。

现在为解决方案。在传递(文档节点)参数之前,通过添加/*(或等效的/element())来获取其元素子元素(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...
};


最后,看起来description of eXist's request:get-data() function与该解释完全一致。它说:“如果它不是二进制文档,我们将尝试将其解析为XML并返回document-node()。” (添加了重点)

感谢您的冒险。原来这是常见的XPath陷阱(文档节点的意识),但是我从绕道学习到静态类型检查中学到了一些东西。

关于xquery - XQuery中的element()与node(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8554543/

相关文章:

xml - 如何处理 XQuery 中的递归?

xml - XQuery - 分组和计数

xpath - XQuery 中是否有任何方法可以获取自某个 Epoch 以来以毫秒为单位的当前时间?

exist-db - 如何为 exist-db 中的属性创建索引

xpath - 如何在 eXist-db 中使用 XQuery 和/或 XSLT 从我的 XML 创建 SVG 条形图

xpath - 为什么 xmlstarlet 不选择所有节点?

javascript - HTML 表单提交给出 400 错误请求

eXist-DB 推荐的存储用户信息的做法(超出用户/密码)?

xml - 使用 XQuery 将 XML 数据转换为 CSV 字符串

java - XQuery 使用 Saxon 9-HE 执行 Java 函数