0

我有 XPath 表达式的列表。.NET 中是否有一种方便的方法来测试列表中没有 XPath 表达式在其寻址范围内重叠?例子:

string1 = /nodeA/nodeB/nodeC  
string2 = /nodeA/modeB/nodeD  

会生成false,因为没有重叠。

string1 = /nodeA/nodeB/nodeC  
string2 = /nodeA/nodeB/nodeC/nodeF  

在这对参数的情况下,它将返回true,因为表达式选择的元素集string1与评估的结果重叠string2

编辑:
鉴于下面的答案,我了解到有一个可用的“相交”解决方案,但由于我需要解决重叠“冲突”而不在 XML 文档上应用表达式,所以我无法使用它。我现在明白,任意情况几乎是不可能的。所以我的解决方案是限制允许的表达式类型并进行字符串比较。

4

4 回答 4

1

我曾经为用于为 XSLT 2.0 和相关规范生成勘误表的样式表编写了与此类似的目的的代码。你可能会发现它给了你一些想法。这包含检查规范中没有文本受到多个勘误“影响”的逻辑。代码如下(它使用 saxon:evaluate 因为定义勘误表的 XML 文档包含指示基本文档中的哪些部分“受影响”的 XPath 表达式)。

这里的一个关键目标不是确定两个 XPath 表达式是否选择重叠节点,而是确定 N 个这样的表达式是否有任何重叠,其中表达式是事先不知道的——我们希望这样做而不寻找所有表达式对之间的重叠。

这里有一个有趣的表达式:count($x) != count($x/.)。这里 ($x/.) 用于强制从节点序列中消除重复节点,因此测试是询问重复消除是否会删除任何节点,即 $x 是否包含任何重复。

<!-- The following template checks that there is no element in the source document
     that is replaced or deleted by more than one erratum -->

<xsl:template name="check-for-conflicts">
  <xsl:variable name="directly-affected-elements" 
      select="er:eval-all(/er:errata/er:erratum[not(@superseded)]//er:old-text[not(starts-with(@action, 'insert-'))])"/>
  <xsl:variable name="all-affected-elements"
      select="for $e in $directly-affected-elements return $e/descendant-or-self::*"/>
  <xsl:if test="count($all-affected-elements) != count($all-affected-elements/.)">
     <!-- we now know there are duplicates, we just need to identify them... -->
     <xsl:for-each-group select="$all-affected-elements" group-by="generate-id()">
       <xsl:if test="count(current-group()) gt 1">
         <xsl:variable name="id" select="(ancestor::*/@id)[last()]"/>
         <xsl:variable name="section" select="$spec/key('id',$id)"/>
         <xsl:variable name="section-number">
           <xsl:number select="$section" level="multiple" count="div1|div2|div3|div4"/>
         </xsl:variable>
         <xsl:variable name="loc" select="er:location($section, .)"/>
         <p style="color:red">
           <xsl:text>WARNING: In </xsl:text>
           <xsl:value-of select="$section-number, $section/head"/>
           <xsl:text> (</xsl:text>
           <xsl:value-of select="$loc"/>
           <xsl:text>) Element is affected by more than one change</xsl:text>
         </p>
       </xsl:if>
     </xsl:for-each-group>
   </xsl:if>           
</xsl:template>

<!-- Support function for the check-for-conflicts template.
     This function builds a list (retaining duplicates) of all elements
     in the source document directly affected by a replacement or deletion.
     "Directly affected" means that the element is explicitly selected for replacement
     or deletion; the descendants of this element are indirectly affected. -->       

<xsl:function name="er:eval-all" as="element()*">
  <xsl:param name="in" as="element(er:old-text)*"/>
  <xsl:for-each select="$in">
    <xsl:variable name="id" select="@ref"/>
    <xsl:variable name="section" select="$spec/key('id',$id)"/>
    <xsl:variable name="exp" select="@select"/>
    <xsl:variable name="nodes" select="$section/saxon:evaluate($exp)"/>
    <xsl:sequence select="$nodes"/>
  </xsl:for-each>
</xsl:function>
于 2013-01-24T23:40:12.450 回答
0

您可以使用 XPath 2.0 中引入的intersect运算符来检查这些表达式选择的集合的交集是否为空。

/nodeA/nodeB/nodeC intersect /nodeA/nodeB/nodeC/nodeF

AFAIK,Saxon支持 XPath 2.0,它可用于 .NET 平台。在这个 SO question的答案中提到了一些替代方案。你必须自己环顾四周。我不是一个真正的 .NET 人,所以我不熟悉平台中的新功能。

您还可以寻找基于 LINQ to XML 的解决方案。在这个 msdn 线程中讨论了这样的方法。这是OP(Santosh Benjamin's)博客上的摘要。

于 2013-01-24T21:12:10.863 回答
0

没有真正的机会计算所有可能输入的重叠,但您可以轻松地为给定的输入计算重叠。

在给定输入上重叠

/nodeA/nodeB/nodeC//*[. = /nodeA/modeB/nodeD//*]

将在左查询的结果集中找到所有节点,这些节点也包含在右查询中。这模仿intersect了@Tom 为 XPath 1.0 提出的建议。如果它返回一个空集,则该输入的查询不会重叠,如果有结果,它们就会重叠。

为什么很难在任意输入上找到重叠

考虑两个查询,例如

  • //*[@id](返回具有“id”属性的 als 节点)
  • //someNode(返回所有<someNode/>元素)

如果有一个元素<someNode id="..."/>,就会有重叠,否则没有。为任意输入上的任意 XPath 表达式解决这个问题将变得非常困难。

于 2013-01-24T21:29:35.707 回答
0

对于两个 XPath 表达式,请使用

not(Expr1[count(.|Expr2) = count(Expr2)])

如果对您拥有的每一对表达式进行评估,并且答案始终为true(),那么只有这样,没有两个表达式会选择同一个节点。

如果需要查找是否有一个节点被所有表达式选中

not((Expr1 | Expr2 ... | ExprN)
      [count(.|Expr1) = count(Expr1)
     and
       count(.|Expr2) = count(Expr2)
     .  .  .  .  .  .  .  .  .  .  .  .
     and
       count(.|ExprN) = count(ExprN)
      ]
    )

true()如果没有这样的节点。

于 2013-01-24T22:50:19.580 回答