简体   繁体   中英

XPath to count total number of string tokens of several nodes in XSLT 1.0?

Is there a neat XPath expression to count string tokens of a set of nodes? Eg;

<set>
  <hi>hello, there, world</hi>
  <hi>foo, bar</hi>
</set>

The answer I want for the above example is the value 5 . The first <hi> has 3 tokens. The second <hi> has 2 tokens.

The <set> may contain any number of <hi> nodes, each of which may contain any number of tokens. A token is a string separated by a comma followed by space.

I've tried;

 <xsl:value-of select="count(str:tokenize(/set//hi, ', '))"/>

but that returns the number of tokens of the first node, ie. 3 .

I'm using XSLT 1.0. (PHP libxml version 2.7.3)

You would need the expressive power of XPath 2.0 or XQuery 1.0 like count(/set/hi/tokenize(., ', ')) to do it with a single expression.

With XSLT 1.0 you need to iterate and sum up the values, perhaps using a result tree fragment as a data structure for intermediary results which you then convert (with exsl:node-set ) to a node-set for summing up the pieces.

Given your definition

A token is a string separated by a comma followed by space.

... all you want to do is count the number of commas and add 1 for each string.

Here is a bit of a hack that works in XSLT 1.0, and it exploits a translate() idiom that sometimes proves useful. Think about what it is doing (it takes a moment to sink in for a first-time user of the idiom).

T:\ftemp>type commas.xml
<set>
  <hi>hello, there, world</hi>
  <hi>foo, bar</hi>
</set>
T:\ftemp>xslt commas.xml commas.xsl
5
T:\ftemp>type commas.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

<xsl:output omit-xml-declaration="yes"/>

<xsl:template match="set">
  <xsl:variable name="commas">
    <xsl:for-each select="hi">
      <xsl:value-of select="translate(.,translate(.,',',''),'')"/>
      <xsl:text>,</xsl:text>
    </xsl:for-each>
  </xsl:variable>
  <xsl:value-of select="string-length($commas)"/>
</xsl:template>

</xsl:stylesheet>
T:\ftemp>

This is the kind of thing that's easy in XPath 2.0, but unfortunately requires some extra work in XPath 1.0. There are a number of ways you can do this with an extra step. This is the one I would use:

<xsl:variable name="tmp">
  <xsl:for-each select="//hi">
    <xsl:value-of select="concat(., ', ')"/>
  </xsl:for-each>
</xsl:variable>
<xsl:value-of select="count(str:tokenize($tmp, ', '))"/>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM