有人可以告诉我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()
。
答案 0 :(得分:8)
data()
仅因为参数类型为node()
而对元素返回空,听起来像是一个错误。您使用的XQuery处理器是什么?
听起来你需要安抚静态类型检查,你可以使用treat as
表达式来做。我不相信使用instance of
进行动态测试就足够了。
试试这个:
let $parameter := someNamespace:functionX() treat as element()
return local:myFunction($parameter)
引用第4版Michael Kay的巨着," treat as
运算符实际上告诉系统你知道运行时类型是什么,你想要任何检查是否延迟到运行时,因为您确信您的代码是正确的。" (第679页)
更新:我认为上述内容实际上是错误的,因为treat as
只是一个断言。它不会更改类型注释node()
,这意味着它也是一个错误的断言,对您没有帮助。嗯...我真正想要的是cast as
,但这只适用于原子类型。我想我很难过。也许你应该改变XQuery引擎。 :-)如果我想到其他的话,我会报告回来。此外,我很想知道Dimitre的解决方案是否适合您。
更新#2:我之前已经退回过了。我可以再次退缩吗? ;-)现在我的理论是treat as
将基于node()
被解释为各种特定节点类型注释的并集而不是运行的事实而工作 -time类型注释本身(请参阅"注意"在"Item types" section of the XQuery formal semantics中。)在运行时,类型注释将为element()
。使用treat as
向类型检查器保证这是真的。现在我等着屏住呼吸:这对你有用吗?
EXPLANATORY ADDENDUM:假设这是有效的,这就是原因。 node()
是一种联合类型。运行时的实际项目永远不会使用node()
进行注释。 "项类型是原子类型,元素类型,属性类型,文档节点类型,文本节点类型,注释节点类型或处理指令类型。" {{3}请注意node()
不在该列表中。因此,您的XQuery引擎并不抱怨某个项目的类型为node()
;相反,它抱怨它不知道类型将是什么(node()
意味着它可能最终成为attribute()
,{{1 },element()
,text()
,comment()
或processing-instruction()
)。为什么要知道?因为你在其他地方告诉它它是一个元素(在你的函数的签名中)。仅仅将其缩小到以上六种可能性中的一种是不够的。静态类型检查意味着您必须在编译时保证类型将匹配(元素与元素,在本例中)。 document-node()
用于将静态类型从一般类型(treat as
)缩小到更具体的类型(node()
)。它不会改变动态类型。另一方面,element()
用于将项目从一种类型转换为另一种类型,同时更改静态和动态类型(例如,xs:string到xs:boolean)。有意义的是cast as
只能用于原子值(而不是节点),因为将属性转换为元素(等等)是什么意思?并且没有将cast as
项目转换为node()
项目的事情,因为没有element()
项目。 node()
仅作为静态联合类型存在。故事的道德启示?避免使用静态类型检查的XQuery处理器。 (抱歉这个讽刺的结论;我觉得我已经获得了正确的结果。:-))
基于更新信息的新答案:听起来像静态类型检查是一个红色的鲱鱼(一个很大的一个)。我相信你实际上不处理一个元素而是文档节点,它是包含XPath中顶级元素(文档元素)的不可见根节点数据模型表示格式良好的XML文档。
树的建模方式如下:
node()
和不这样:
[document-node]
|
<docElement>
|
<subelement>
我原以为你正在通过 <docElement>
|
<subelement>
节点。但是,如果我是对的,那么你实际上是在传递文档节点(它的父节点)。由于文档节点是不可见的,因此它的序列化(您复制和粘贴的内容)与元素节点无法区分,并且当您粘贴现在在XQuery中被解释为裸元素构造函数时,区别就会丢失。 (要在XQuery中构造文档节点,必须使用<docElement>
包装元素构造函数。)
document{ ... }
测试失败,因为节点不是元素而是文档节点。 (它本身不是instance of
,因为没有这样的东西;请参阅上面的解释。)
此外,这可以解释为什么node()
在您尝试获取文档节点的data()
子节点时(在将函数参数类型放宽为<subelement>
之后)返回空。上面的第一个树表示显示node()
不文档节点的子节点;因此它返回空序列。
现在为解决方案。在传递(文档节点)参数之前,通过附加<subelement>
(或等效的/*
)来获取其元素子元素(文档元素):
/element()
或者,让您的函数获取文档节点并更新传递给data()的参数:
let $parameter := someNamespace:functionX()/*
return local:myFunction($parameter)
最后,看起来1与此解释完全一致。它说:&#34;如果它不是二进制文档,我们尝试将其解析为XML 并返回document-node()。&#34; (重点补充)
感谢您的冒险。事实证明,这是一个常见的XPath问题(文档节点的意识),但我从绕道进入静态类型检查中学到了一些东西。
答案 1 :(得分:2)
这完全可以使用Saxon 9.3 :
declare namespace my = "my:my";
declare namespace their = "their:their";
declare function my:fun($arg1 as element()) as element()
{
$arg1/a
};
declare function their:fun2($arg1 as node()) as node()
{
$arg1
};
my:fun(their:fun2(/*) )
当以上代码应用于以下XML文档时,:
<t>
<a/>
</t>
生成正确的结果且没有错误消息:
<a/>
<强>更新强>:
以下应该适用于最节奏的静态类型检查XQuery实现:
declare namespace my = "my:my";
declare namespace their = "their:their";
declare function my:fun($arg1 as element()) as element()
{
$arg1/a
};
declare function their:fun2($arg1 as node()) as node()
{
$arg1
};
let $vRes := their:fun2(/*)
(: this prevents our code from runtime crash :)
return if($vRes instance of element())
then
(: and this assures the static type-checker
that the type is element() :)
my:fun(their:fun2(/*) treat as element())
else()
答案 2 :(得分:1)
node()
是元素,属性,处理指令,文本节点等。
但data()
将结果转换为字符串,而不是任何字符串;它是一种原始类型。
你可能想尝试item()
,它应该匹配。
请参阅W3C XQuery规范中的2.5.4.2 Matching an ItemType and an Item。
虽然示例代码中没有显示,但我假设您实际上正在从$value
返回一个值(例如您正在使用的local:myFunction
)。