XPath拉多个匹配

时间:2017-03-08 13:56:03

标签: xml xpath xquery

(BaseX)错误

我在BaseX中的大型数据集上运行查询,但是一个XQuery使我的程序崩溃并出现错误[XPTY0004] Item expected, sequence found: (attribute begin {"6"}, ...).

在我的查询中,我试图通过比较begin(XML中存在的属性)与number()来确保一个元素位于另一个元素之前。但每当我在我的数据集上尝试最基本的XQueries(返回匹配节点)时(例如使用this online tool),我得到的错误类似于我之前的错误:

  

[错误] SaxonCE.XSLT20Processor 14:08:39.692严重:XPathException in   invokeTransform:不允许包含多个项目的序列   number()的第一个参数(" 6"," 10")

所以我猜这个节点的兄弟姐妹正在发生一些事情,即这些节点不止一个,并且不清楚应该比较哪些节点。示例如下。

为什么订单很重要?

XPath用于树库的查询引擎:语言注释语料库。在某些情况下,我们希望节点按顺序匹配,有时无关紧要。作为一个简单的例子:有时我们希望匹配某些特定的东西,如有关的人,其中订单文章,形容词,名词很重要。在其他查询中它并不重要,我们希望匹配短语,例如可用时间,其中文章,形容词,名词的顺序可以是任何顺序。

换句话说,在第一种情况下,应该尊重元素的顺序,在第二种情况下,它不应该被尊重。这是一个可能包含文章,形容词和名词的结构的XPath表示。

node[@cat="np" and node[@pt="art"] and node[@pt="adj"] and node[@pt="n"]]

默认情况下,XPath不关心这些元素的顺序并执行贪婪搜索,即它还会匹配可用时间art,{{1 },n)。但是我想重新编写上面的XPath以确保节点的顺序得到遵守,因此构造如可用时间adjartn)与相关人adjartadj)不匹配。

n

一种方法是使用语料库中可用的# Possible representation of *the time available* <node id="0" begin="1" cat="np"> <node id="1" begin="1" pt="art" text="the" /> <node id="2" begin="2" pt="n" text="time" /> <node id="3" begin="3" pt="adj" text="available" /> </node> # Possible representation of *the concerned man* <node id="0" begin="1" cat="np"> <node id="1" begin="1" pt="art" text="the" /> <node id="2" begin="2" pt="adj" text="concerned" /> <node id="3" begin="3" pt="n" text="man" /> </node> 属性的数字比较。它是数字递增的,所以如果我们想确保XPath的顺序完好无损,我们可以说begin的每个子节点的数值应该小于下一个使用@cat="np"。但正如我在上面所示,这会导致错误 - 在我刚刚展示的简单示例代码中不会出现错误。

另一个例子。

number()

此XPath应匹配:

<node id="0" begin="2">
    <node id="1" begin="2">
        <node id="2" begin="2"/>
        <node id="3" begin="3"/>
    </node>
    <node id="4" begin="5">
        <node id="5" begin="5"/>
    </node>
    <node id="6" begin="6"/>
</node>

但是当通过XQuery处理器时,你会得到上述错误。 /node/node[number(@begin) < number(../node/@begin)]

我尝试了@Michael Kay提供的解决方案,但似乎也出现了同样的问题。

的XQuery

A sequence of more than one item is not allowed as the first argument of number() ("2", "5", ...)

数据

for $node in node[every $n in node[position() lt last()] satisfies (number($n/@begin) lt number($n/following-sibling::node/@begin))]
return $node

错误

  

SaxonCE.XSLT20Processor 14:48:49.809严重:XPathException in   invokeTransform:不允许包含多个项目的序列   number()的第一个参数(&#34; 5&#34;,&#34; 6&#34;)

2017年4月19日更新

我今天遇到了一些意想不到的行为,这使得@ har07提供的解决方案不再充分。我错误地认为<node id="0" begin="2"> <node id="1" begin="2"> <node id="2" begin="2"/> <node id="3" begin="3"/> </node> <node id="4" begin="5"> <node id="5" begin="5"/> </node> <node id="6" begin="6"/> </node> 子句只对XPath中的节点有影响(而不是XML中的所有节点)。换句话说,当not()子句添加到XPath的最顶层节点时,XML中的所有子节点都将具有固定的排序单词顺序。 (现在我这样读了它,看起来很正常。)但是,我真正想要的是单词顺序只在XPath中指定的节点上设置,而不是匹配XML中的其他节点。希望和榜样能让这一点更清晰。

我们想要匹配以下XPath,not()包含cat="np"且至少两次rel="det" pt="vnw" lemma="die"

rel="mod" pt="adj"

但是,并且要求遵循此XPath的顺序,即

//node[@cat="np" and node[@rel="det" and @pt="vnw" and @lemma="die"] and count(node[@rel="mod" and @pt="adj"]) > 1]

所以//node[ @cat="np" and not(node[ position() < last() ][number(@begin) > following-sibling::node/number(@begin)]) and node[ @rel="det" and @pt="vnw" and @lemma="die" ] and count(node[ @rel="mod" and @pt="adj" ]) > 1 ] 必须在之前发生 XML中的两个rel="det"。这工作正常,所有匹配都是正确的,但找不到所有预期的匹配。原因是rel="mod"行显然是针对所有XML节点而不是XPath指定的节点。如果找到一个不符合not()规则的节点,那么即使在XPath中未指定该节点,也不会成为匹配。例如,上面的XPath将匹配以下XML,因为在not内有一个节点,其begin属性大于其下一个兄弟节点,{{1}不允许}} 规则。

cat="np"

但是,我希望此not匹配,并使<node begin="4" cat="np" id="8" rel="obj1"> <node begin="4" id="9" pos="det" pt="vnw" rel="det" word="die" lemma="die" /> <node begin="5" id="10" pos="adj" pt="adj" rel="mod" word="veelzijdige" /> <node begin="6" id="11" pos="adj" pt="adj" rel="mod" word="getalenteerde" /> <node begin="7" id="12" pos="noun" pt="n" rel="hd" word="figuren" /> <node begin="8" id="31" index="1" rel="obj1" /> <node begin="2" id="32" index="2" rel="obj2" /> </node> 函数不那么激进,即只需要在XPath中指定的节点(在此示例中为cat="np"),以及两个not()个节点)遵循订单要求,其中begin属性应小于XPath结构的下一个项 。 {/ 1}}中未在XPath中指定的其他项目允许具有大于其下一个兄弟的属性。

请注意,XPath结构的最后一项(与示例XML中的rel="det" pt="vnw" lemma="die"匹配)不一定必须具有低于XML中的后续节点的begin属性(未在XPath)。

和以前一样,我对如何使用纯XPath选项解决这个问题特别感兴趣,但也欢迎使用XQuery替代方案。优选地,作为将XPath结构作为输入的函数,并应用&#39;字顺序&#39;到它的最顶层节点及其所有后代。我们鼓励使用此处所示的XPath示例代码和用法作为示例。

3 个答案:

答案 0 :(得分:1)

我认为我理解的部分问题是:

假设我想匹配XML,其中根的每个直接子项的属性开始小于下一个兄弟。

<node id="0" begin="2">
    <node id="1" begin="2">
        <node id="2" begin="2"/>
        <node id="3" begin="3"/>
    </node>
    <node id="4" begin="5">
        <node id="5" begin="5"/>
    </node>
    <node id="6" begin="6"/>
</node>

此XPath应匹配:

/node/node[number(@begin) < number(../node/@begin)]

现在,相当清楚为什么会给你一个错误。在谓词中,..选择id = 0的节点,它有三个子节点(带有id 1,4和6),并且每个节点都有一个@begin属性,所以{{1选择三个属性的序列。

您的查询似乎与散文要求没有任何关系,即

其中root的每个直接子项的属性begin小于下一个兄弟

条件是

  

node [node [position()lt last()]中的每个$ n都满足   (number($ n / @ begin)lt number($ n / following-sibling :: node / @ begin)]

答案 1 :(得分:1)

关于 a-sequence-of-more-than-item-is-not-allowed 异常,请注意XPath 2.0及更高版本和XQuery支持函数调用在路径步骤(.../number())。也就是说,您可以在单个number()上调用node,一次传递一个begin属性,以避免异常:

/node/node[number(@begin) < ../node/number(@begin)]

但是,如果至少有一个兄弟truenode属性值大于begin当前属性,则上面XPath中使用的谓词表达式的计算结果为begin node,这似乎不是理想的行为。

您可以对建议的XQuery应用相同的修复,但显然还有另一个类似的问题,因为lt用于将值与值序列进行比较(要清楚,我指的是建议的XQuery中的第二个lt。您可以尝试以下,稍加修改,XQuery:

for $node in node[
    every $n in node[position() lt last()] 
    satisfies not($n/following-sibling::node[number(@begin) lt number($n/@begin)])
]
return $node
  

“一种方法是使用语料库中可用的begin属性的数字比较。它是数字升序,所以如果我们想确保XPath的顺序是完整的,我们可以说使用@cat="np"number()的每个子节点的数值应该小于下一个子节点。“

如果我理解正确,您可以使用以下XPath:

/node/node[
    not(
        node[position() < last()]
            [number(@begin) > following-sibling::node/number(@begin)]
    )
]

<强> demo

XPath应该返回所有第二级node元素,其中,对于除了当前第二级node中的最后一个之外的每个子node,以下任何一个node都没有} begin属性的值在数值上低于当前子node的值。

给出以下示例XML:

<node id="0" begin="2">
    <node id="0" begin="1" cat="np">
        <node id="1" begin="1" pt="art" text="the" />
        <node id="2" begin="3" pt="n" text="time" />
        <node id="3" begin="2" pt="adj" text="available" />
    </node>
    <node id="0" begin="1" cat="np">
        <node id="1" begin="1" pt="art" text="the" />
        <node id="2" begin="2" pt="adj" text="concerned" />
        <node id="3" begin="3" pt="n" text="man" />
    </node>
</node>

仅选择第二个node,因为它是唯一具有node属性值的第二级begin,按升序排列:

<node id="0" begin="1" cat="np">
   <node id="1" begin="1" pt="art" text="the"/>
   <node id="2" begin="2" pt="adj" text="concerned"/>
   <node id="3" begin="3" pt="n" text="man"/>
</node>

2017年4月19日更新:

  

...但是,我希望此cat="np"匹配,并使not()函数不那么激进,即只需要在XPath中指定的节点(在此示例中为rel="det" pt="vnw" lemma="die",并且两个rel="mod" pt="adj"节点)遵循订单要求,其中begin属性应小于XPath结构的下一项。

然后我们需要向{strong>指定 not()中的那些节点添加另一个谓词,这是我们检查属性订单要求的地方:

node[(@rel="det" and @pt="vnw" and @lemma="die") or (@rel="mod" and @pt="adj")]
    [position() < last()]
    [number(@begin) > 
         following-sibling::node[(@rel="det" and @pt="vnw" and @lemma="die") or (@rel="mod" and @pt="adj")]/number(@begin)
    ]

所以完整的表达式如下:

//node[@cat="np" and 
    not(node[(@rel="det" and @pt="vnw" and @lemma="die") or (@rel="mod" and @pt="adj")]
            [position() < last()]
            [number(@begin) > 
                 following-sibling::node[
                    (@rel="det" and @pt="vnw" and @lemma="die") or (@rel="mod" and @pt="adj")
                 ]/number(@begin)
            ]
    ) 
    and node[@rel="det" and @pt="vnw" and @lemma="die"] 
    and count(node[@rel="mod" and @pt="adj"]) > 1
]

<强> demo

答案 2 :(得分:-1)

就您的递归搜索请求而言:

从xml树的内层使用//node[@pt=("art" or "adj" or "n")]/ancestor::*搜索。在您的示例x​​ml中,这将以递归方式返回(每个元素组)每个顶级。

了解更多信息:http://www.w3.org/TR/xpath-30/