使用XSLT计算XML中字符串的出现次数

时间:2011-07-13 13:30:22

标签: xml xslt count

我想使用XSLT来计算XML文档中特定节点中字符串出现次数。 考虑这个例子

 <mainNode>
<book>
    <price> 100 </price>
    <city> chennai </city>
    <list>
        <language> c java ruby </language>
    </list>
</book>

<book>
    <price> 200 </price>
    <city> banglore </city>
    <list>
        <language> c java </language>
    </list>
</book>

<book>
    <price> 300 </price>
    <city> delhi </city>
    <list>
        <language> java ruby </language>
    </list>
</book>
</mainNode>      

这里我想计算“java”

的出现次数

我希望输出像这样:: java - 3

怎么做?任何想法???

4 个答案:

答案 0 :(得分:7)

使用

count(/*/*/list/language[contains(., 'java')])

完整的XSLT转换

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

 <xsl:template match="/">
     java -- <xsl:value-of select=
       "count(/*/*/list/language[contains(., 'java')]) "/>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档

<mainNode>
    <book>
        <price> 100 </price>
        <city> chennai </city>
        <list>
            <language> c java ruby </language>
        </list>
    </book>
    <book>
        <price> 200 </price>
        <city> banglore </city>
        <list>
            <language> c java </language>
        </list>
    </book>
    <book>
        <price> 300 </price>
        <city> delhi </city>
        <list>
            <language> java ruby </language>
        </list>
    </book>
</mainNode>

产生了想要的正确结果

 java -- 3

<强>更新

如果我们计算所有字符串的出现次数 - 而不仅仅是包含字符串的所有节点 - 以下是如何做到的:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes"/>
    <xsl:param name="pWord" select="' java '"/>

    <xsl:template match="/">
        <xsl:variable name="vResult">
            <xsl:apply-templates/>
        </xsl:variable>
        <xsl:value-of select="concat($pWord, '--- ')"/>
        <xsl:value-of select="string-length($vResult)"/>
    </xsl:template>

    <xsl:template match="list/language" name="countWord">
        <xsl:param name="pText" select="."/>

        <xsl:if test="contains($pText, $pWord)">
            <xsl:text>X</xsl:text>
            <xsl:call-template name="countWord">
                <xsl:with-param name="pText"
                 select="concat(' ', substring-after($pText, $pWord))"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>

    <xsl:template match="text()"/>
</xsl:stylesheet>

在此XML文档上应用此转换时

<mainNode>
    <book>
        <price> 100 </price>
        <city> chennai </city>
        <list>
            <language> c java ruby </language>
        </list>
    </book>
    <book>
        <price> 200 </price>
        <city> banglore </city>
        <list>
            <language> c java </language>
        </list>
    </book>
    <book>
        <price> 300 </price>
        <city> delhi </city>
        <list>
            <language> java java ruby </language>
        </list>
    </book>
</mainNode>

产生了想要的正确结果

 java --- 4

答案 1 :(得分:2)

也许你可以尝试这个XSL Template to count substrings

<xsl:template name="substring-count">
  <xsl:param name="string"/>
  <xsl:param name="substr"/>
  <xsl:choose>
    <xsl:when test="contains($string, $substr) and $string and $substr">
      <xsl:variable name="rest">
        <xsl:call-template name="substring-count">
          <xsl:with-param name="string" select="substring-after($string, $substr)"/>
          <xsl:with-param name="substr" select="$substr"/>
        </xsl:call-template>
      </xsl:variable>
      <xsl:value-of select="$rest + 1"/>
    </xsl:when>
    <xsl:otherwise>0</xsl:otherwise>
  </xsl:choose>
</xsl:template>

用法:

<xsl:call-template name="substring-count">
  <xsl:with-param name="string" select="'mary had a little lamb'" />
  <xsl:with-param name="substr" select="'lamb'" />
</xsl:call-template>

答案 2 :(得分:1)

<xsl:value-of>声明中尝试此操作:

count(//language[contains(concat(' ',.,' '), ' java ')])

如果您的文档结构是相对静态的,或者您在其他地方有language个节点用于其他目的,则可以将//language替换为/mainNode/book/list/language

concat位可能看起来有些复杂,但是要确保在您正在查看的文本的开头和结尾有一个空格,并且在任意一侧搜索' java '空格,您不会错误地包含恰好包含java的其他字词,例如javascript

如果'java'可能在节点中不止一次存在,那么您将需要使用递归模板。这是一种方式:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" />
  <xsl:template match="/">
    <xsl:variable name="list">
      <xsl:for-each select="//language">
        <xsl:call-template name="count">
          <xsl:with-param name="lang">java</xsl:with-param>
        </xsl:call-template>
      </xsl:for-each>
    </xsl:variable>
    <xsl:value-of select="concat('java -- ',string-length($list))" />
  </xsl:template>

  <xsl:template name="count">
    <xsl:param name="lang" />
    <xsl:param name="text" select="text()" />
    <xsl:if test="contains(concat(' ',$text,' '),concat(' ',$lang,' '))">
      <xsl:text>0</xsl:text>
      <xsl:call-template name="count">
        <xsl:with-param name="lang" select="$lang" />
        <xsl:with-param name="text" select="substring-after($text,$lang)" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

这实际上创建了一个0的字符串,每次出现一个java,然后只使用该字符串的长度。

如果您可以选择使用XSLT 2.0,则可以创建一个计算字符串中出现次数的函数,并使用<xsl:value-of select="sum(mycountfunction(//language))" />或类似的东西。

正如我在您的问题评论中指出的那样,更好地设计源XML会有很大帮助;如果每种语言都有自己的元素,那么这一切都不是必需的。当然,这可能超出了您的控制范围,但如果您可以选择更改它(或说服提供商更改它),我强烈推荐它。

答案 3 :(得分:0)