有人可以告诉我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
获得子元素的部分。显然,如果$arg1
是element()
,则可以执行此操作,但如果它是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/