编辑:我还可以访问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之间的交集计数,但它是1比1的比较。是否可以使用其他一些比较节点的方法?
编辑:我确实让这个工作,但我通过使用其他一些第三方扩展来欺骗,以获得相同的结果。我仍然对其他方法感兴趣。答案 0 :(得分: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转换为节点集。
答案 1 :(得分: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()
。
答案 2 :(得分:0)
Robert的最后xsl:variable
适用于获取包含匹配文本值的结果树片段,但除非(他建议)使用EXSLT或MS扩展到XSLT 1.0将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中看到使用xsl:for-each
获取Zack请求的排序计数的方法。
答案 3 :(得分:-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>
我认为这个样式表应该实现Robert的解决方案,但我只计算'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'计数,但它远不如Robert的答案那么优雅。如果我能让XPath工作 - 总是想学习。