5

有人能告诉我XQuerynode()和类型之间的确切区别吗?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直接通过。我尝试将函数的类型更改为采用 anode()而不是 a element(),但是我似乎无法从中读取任何数据。$value只是空的。

是否有某种方法可以将节点转换为元素,或者我是否应该遗漏一些东西?

编辑:据我所知,问题出在我尝试使用$arg1/subelement. 显然你可以这样做 if $arg1is anelement()但不是如果它是 a node()

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

我需要能够使其忽略此类型检查或将数据“类型转换”到element().

4

3 回答 3

8

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

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

试试这个:

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

引用 Michael Kay 的巨著第 4 版,“treat as操作员本质上是在告诉系统您知道运行时类型将是什么,并且您希望将任何检查推迟到运行时,因为您确信您的代码是正确的。” (第 679 页)

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

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

解释性附录:假设这可行,这就是原因。node()是联合类型。运行时的实际项目永远不会用node(). “项目类型可以是原子类型、元素类型、属性类型、文档节点类型、文本节点类型、注释节点类型或处理指令类型。” 1请注意node()不在该列表中。因此,您的 XQuery 引擎不会抱怨某项具有 type 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()等效的)来获取其元素子元素(文档元素):

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...
};

最后,看起来eXist 的 request:get-data() 函数的描述与这个解释完全一致。它说:“如果它不是二进制文档,我们会尝试将其解析为 XML并返回一个 document-node()。” (重点补充)

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

于 2011-12-19T20:59:15.287 回答
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()
于 2011-12-19T01:01:31.140 回答
1

node()是元素、属性、处理指令、文本节点等。

但是data()将结果转换为字符串,这不是其中的任何一个;它是一种原始类型。

您可能想尝试item(),它应该匹配。

请参阅W3C XQuery 规范中的2.5.4.2 匹配 ItemType 和 Item

虽然它没有显示在您的示例代码中,但我假设您实际上是$valuelocal:myFunction.

于 2011-12-18T20:26:08.010 回答