3

编辑:我也可以访问ESXLT功能。

我有两个节点集的字符串标记。一组包含如下值:

/Geography/North America/California/San Francisco
/Geography/Asia/Japan/Tokyo/Shinjuku

另一组包含如下值:

/Geography/North America/
/Geography/Asia/Japan/

我的目标是找到两者之间的“匹配”。当第 1 组中的任何字符串以第 2 组中的字符串开头时,就会进行匹配。例如,将在/Geography/North America/California/San Francisco/Geography/North America/之间进行匹配,因为来自第 1 组的字符串以第 2 组中的字符串开头。

我可以通过使用第三方扩展来使用通配符比较字符串。我还可以在 Xpath 中使用正则表达式。

我的问题是如何构建 Xpath 以在两组的所有节点之间使用函数进行选择?XSL 也是一个可行的选择。

这个 XPATH:

count($set1[.=$set2])

将产生 set1 和 set2 之间的交集计数,但这是一对一的比较。是否可以使用其他一些方法来比较节点?

编辑:我确实得到了这个工作,但我通过使用其他一些第三方扩展来获得相同的结果来作弊。我仍然对完成这项工作的其他方法感兴趣。

4

4 回答 4

2

这:

<xsl:variable name="matches" select="$set1[starts-with(., $set2)]"/>

将设置$matches为一个节点集,其中包含$set1其文本值以 $set2 中节点的文本值开头的每个节点。这就是你要找的,对吧?

编辑:

好吧,我只是错了。这就是为什么。

starts-with期望它的两个参数都是字符串。如果不是,它将在评估函数之前将它们转换为字符串。

如果你给它一个节点集作为它的参数之一,它使用节点集的字符串值,它是集合中第一个节点的文本值。所以在上面,$set2永远不会被搜索;只有列表中的第一个节点会被检查,因此谓词只会找到$set1$set2.

我被误导了,因为这种模式(过去几天我一直在使用)确实有效:

<xsl:variable name="hits" select="$set1[. = $set2]"/>

但是该谓词使用节点集之间的比较,而不是文本值之间的比较。

做到这一点的理想方法是嵌套谓词。也就是说,“我想找到每个节点,$set1其中有一个节点$set2的值以...开头”,这就是 XPath 崩溃的地方。从什么开始?你想写的是这样的:

<xsl:variable name="matches" select="$set1[$set2[starts-with(?, .)]]"/>

只有没有表达式可以编写?,它将返回当前由外部谓词测试的节点。(除非我遗漏了一些非常明显的东西。)

为了得到你想要的,你必须单独测试每个节点:

<xsl:variable name="matches">
  <xsl:for-each select="$set1">
    <xsl:if test="$set2[starts-with(current(), .)]">
      <xsl:copy-of select="."/>
    </xsl:if>
  </xsl:for-each>
</xsl:variable>

这不是一个非常令人满意的解决方案,因为它评估的是结果树片段,而不是节点集。如果要在 XPath 表达式中使用变量,则必须使用扩展函数(如msxsl:node-set)将 RTF 转换为节点集。

于 2008-11-07T19:19:59.117 回答
1

有一个简单而纯粹的 XSLT 1.0 解决方案(不需要扩展)来查找匹配数

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>

    <xsl:template match="/">
        <xsl:variable name="vStars">
            <xsl:for-each select="*/regions/*">
                <xsl:for-each select="/*/cities/*[starts-with(.,current())]">
                    <xsl:value-of select="'*'"/>
                </xsl:for-each>
            </xsl:for-each>
        </xsl:variable>

        <xsl:value-of select="string-length($vStars)"/>
    </xsl:template>
</xsl:stylesheet>

当此转换应用于以下 XML 文档时

<t>
    <cities>
        <city>/Geography/North America/California/San Francisco</city>
        <city>/Geography/Asia/Japan/Tokyo/Shinjuku</city>
    </cities>
    <regions>
        <region>/Geography/North America/</region>
        <region>/Geography/Asia/Japan/</region>
    </regions>
</t>

产生正确的结果

2

请注意,为找到的每个匹配项生成一个字符(星号),所有这些星号构成$vStars变量的内容。然后我们简单地输出它的string-length().

于 2008-12-05T23:14:26.663 回答
0

Robert 的最后一个xsl:variable对于获取包含匹配文本值的结果树片段很有用,但除非(如他所建议的那样)您使用 XSLT 1.0 的 EXSLT 或 MS 扩展来将 RTF 转换为节点集,否则您无法计算匹配文本节点。

这是我在之前的回复中提到的 XSLT 样式表,它重复出现在我给出的示例输入文档中,以给出集合 1 中的文本节点的计数,其中集合 2 中的节点与它的部分或全部匹配:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output indent="yes" method="text"/>

  <xsl:template match="/">
    <xsl:call-template name="count-matches">
      <xsl:with-param name="set1-node" select="sets/set[1]/text[1]"/>
      <xsl:with-param name="set2-node" select="sets/set[2]/text[1]"/>
      <xsl:with-param name="total-count" select="0"/>
    </xsl:call-template>
    <xsl:text>
</xsl:text>
  </xsl:template>

  <xsl:template name="count-matches">
    <xsl:param name="set1-node"/>
    <xsl:param name="set2-node"/>
    <xsl:param name="total-count" select="0"/>
    <xsl:variable name="this-count">
      <xsl:choose>
        <xsl:when test="contains($set1-node, $set2-node)">
          <xsl:value-of select="1"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="0"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="$set2-node/following-sibling::text">
        <xsl:call-template name="count-matches">
          <xsl:with-param name="set1-node"
                          select="$set1-node"/>
          <xsl:with-param name="set2-node"
                          select="$set2-node/following-sibling::text[1]"/>
          <xsl:with-param name="total-count"
                          select="$total-count + $this-count"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:when test="$set1-node/following-sibling::text">
        <xsl:call-template name="count-matches">
          <xsl:with-param name="set1-node"
                          select="$set1-node/following-sibling::text[1]"/>
          <xsl:with-param name="set2-node"
                          select="$set2-node/preceding-sibling::text[last()]"/>
          <xsl:with-param name="total-count"
                          select="$total-count + $this-count"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$total-count + $this-count"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

不是特别简洁,但是因为 XSLT 不允许程序员为已经定义的变量分配新值,所以递归通常是必要的。我在 XSLT 1.0 中看不到一种方法来计算 Zack 使用xsl:for-each.

于 2008-11-08T22:20:52.373 回答
-1

我想我无法使上面的 XPath 工作。我从以下 XML 文档开始初始化两个节点集:

<?xml version="1.0"?>
<sets>
  <set>
    <text>/Geography/North America/California/San Francisco</text>
    <text>/Geography/Asia/Japan/Tokyo/Shinjuku</text>
  </set>
  <set>
    <text>/Geography/North America/</text>
    <text>/Geography/Asia/Japan/</text>
  </set>
</sets>

我认为这个样式表应该实现罗伯特的解决方案,但我只得到'1'的计数:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:template match="/">
    <xsl:variable name="set1" select="sets/set[1]/text/text()"/>
    <xsl:variable name="set2" select="sets/set[2]/text/text()"/>
    <xsl:value-of select="count($set1[starts-with(., $set2)])"/>
    <xsl:text>
</xsl:text>
  </xsl:template>

</xsl:stylesheet>

我确实编写了一个使用递归模板的样式表,并且确实使用给定的输入文档生成了正确的“2”计数,但它远没有罗伯特的答案那么优雅。要是我能让 XPath 工作就好了——我总是想学习。

于 2008-11-07T23:10:17.220 回答