XQuery中的element()与node()

时间:2011-12-18 20:23:51

标签: 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()

3 个答案:

答案 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)。