XSLT counting elements with a given value

e-holder picture e-holder · Apr 6, 2009 · Viewed 107.9k times · Source

I need to count the number of elements in an XML file that have a particular value (to verify uniqueness). The XML file looks like this:

EDIT: I updated the original "simplified" XML with the actual hairy mess someone designed. Unfortunately, this is going to make all the previous Answers really confusing and wrong unless edited.

<root>
  <ac>
   <Properties>
     <Property Name="Alive">
      <Properties>
        <Property Name="ID">
         <Properties>
           <Property Name="Value">
            <long>11007</long>
           </Property>
         </Properties>
        </Property>
      </Properties>
     </Property>
     <Property Name="Dead">
      <Properties>
        <Property Name="ID">
         <Properties>
           <Property Name="Value">
            <long>11008</long>
           </Property>
         </Properties>
        </Property>
      </Properties>
     </Property>
     ...
     <Property Name="MostlyDeadAllDay">
      <Properties>
        <Property Name="ID">
         <Properties>
           <Property Name="Value">
            <long>99001</long>
           </Property>
         </Properties>
        </Property>
      </Properties>
     </Property>
   </Properties>
  </ac>
</root>

I am trying to define a variable to see how many of the properties at the Alive/Dead level have the long value (ID) as defined in a template parameter. Something along these lines (though I suspect this is wrong)...

<xsl:param name="parPropId"/>
<xsl:variable name="countProperties">
   <xsl:value-of select="count(/root/ac/
      Properties/Property/
      Properties/Property[@Name = 'ID']/
      Properites/Property[@Name = 'Value']/long = $parPropId)"/>
</xsl:variable>

There can be multiple Property elements defined at the "ID" level. But I am fairly certain I can count on just one Property element ("Value") under "ID", and only one "long" element under "Value".

[disclaimer] Whoever designed the overall XML file I'm stuck working with REALLY didn't know how to structure XML (e.g., backwards use of attributes versus elements). I fear my XSLT thinking has temporarily been warped :) as a result. [/disclaimer]

Answer

Cerebrus picture Cerebrus · Apr 6, 2009

This XPath:

count(//Property[long = '11007'])

returns the same value as:

count(//Property/long[text() = '11007'])

...except that the first counts Property nodes that match the criterion and the second counts long child nodes that match the criterion.

As per your comment and reading your question a couple of times, I believe that you want to find uniqueness based on a combination of criteria. Therefore, in actuality, I think you are actually checking multiple conditions. The following would work as well:

count(//Property[@Name = 'Alive'][long = '11007'])

because it means the same thing as:

count(//Property[@Name = 'Alive' and long = '11007'])

Of course, you would substitute the values for parameters in your template. The above code only illustrates the point.

EDIT (after question edit)


You were quite right about the XML being horrible. In fact, this is a downright CodingHorror candidate! I had to keep recounting to keep track of the "Property" node I was on presently. I feel your pain!

Here you go:

count(/root/ac/Properties/Property[Properties/Property/Properties/Property/long = $parPropId])

Note that I have removed all the other checks (for ID and Value). They appear not to be required since you are able to arrive at the relevant node using the hierarchy in the XML. Also, you already mentioned that the check for uniqueness is based only on the contents of the long element.