10
votes

I rarely use XLST and get confusing results when I try to count child nodes in a parent node.

Edit:

The XML is structured as follows:

<?xml version="1.0"?>
<Response>
  <result>
    <name>Someone</name>
    **<rating>4.5</rating>**
    <review>
      <text>Some review.</text>
    </review>
    <review>
      <text>Another review.</text>
    </review>
  </result>
  <result>
    <name>Another one</name>
    **<rating>2</rating>**
    <review>
      <text>Blah, grieve, blah.</text>
    </review>
    <review>
      <text>Blah, grrrrr, blah.</text>
    </review>
    <review>
      <text>Blah, good grrrrr, blah.</text>
    </review>
  </result>
  ...
  ...
</Response>

The template (simplified) is as follows:

**<body>
      <xsl:apply-templates/>
</body>**

<xsl:template match="Response/result">
  <div class="item">
    <div class="name">
      <xsl:value-of select="name"/>
    </div>
    <xsl:if test="rating">
        <span class="review-count">
          **(<xsl:value-of select="count(review)"/>)**
        </span>
    </xsl:if>
  </div>
</xsl:template>

I do not get the correct child node count from this approach. In addition to the count(review), I tried count(descendant::review) and several xPath variations. I know I'm missing something simple - but what?

1
What are you getting and what are you expecting? Also, your xml input doesn't have any rating elements so your count() won't get executed. - Daniel Haley
Also, you didn't remove any xsl:for-each (which would change context) when simplifying your xsl template did you? - Daniel Haley
Thanks Daniel. I amended the "rating" omission and clarified the code a bit. The only code I removed was working correctly - no xsl:for-each anywhere. - ron tornambe
What processor are you using? Using saxon and your xml/xsl:template I correctly get **(2)** for the first result and **(3)** for the second result. - Daniel Haley
You are right Daniel, it turns out I need help in counting on my fingers - the results are correct. Thanks for your effort and correct assessment. - ron tornambe

1 Answers

14
votes
<xsl:if test="rating">
    <span class="review-count">
      **(<xsl:value-of select="count(review)"/>)**
    </span>
</xsl:if>

This will never generate even a single character, because there is no rating element in the provided XML document. In case you remove the above conditional instruction, then the result contains the wanted count values.

If you indeed have rating child for at least some review elements (but failed to show this to us), then you probably also didn't show other important parts of the XML document -- such as a default namespace.

Update:

The OP has provided a more precise XML document, that contains rating elements.

I can't repro the problem.

This XSLT transformation:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:template match="Response/result">
      <div class="item">
        <div class="name">
          <xsl:value-of select="name"/>
        </div>
        <xsl:if test="rating">
            <span class="review-count">
              **(<xsl:value-of select="count(review)"/>)**
            </span>
        </xsl:if>
      </div>
    </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<Response>
  <result>
    <name>Someone</name>
      <rating>4.5</rating>
    <review>
      <text>Some review.</text>
    </review>
    <review>
      <text>Another review.</text>
    </review>
  </result>
  <result>
    <name>Another one</name>
      <rating>2</rating>
    <review>
      <text>Blah, grieve, blah.</text>
    </review>
    <review>
      <text>Blah, grrrrr, blah.</text>
    </review>
    <review>
      <text>Blah, good grrrrr, blah.</text>
    </review>
  </result>
  ...
  ...
</Response>

produces the wanted, correct result:

  <div class="item">
   <div class="name">Someone</div>
   <span class="review-count">
              **(2)**
            </span>
</div>

<div class="item">
   <div class="name">Another one</div>
   <span class="review-count">
              **(3)**
            </span>
</div>
  ...
  ...