Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT counting elements with a given value

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]

like image 632
e-holder Avatar asked Apr 06 '09 15:04

e-holder


People also ask

How do you count elements in XSLT?

The syntax says the count function tells the XSLT processor to count each time in which an XML tag found at the XPATH expression. It returns a number value that specifies the number of sequence elements contained in a sequence of items that passes by. The empty set of the count is returned to 'zero'.

How do you set a counter in XSLT?

XSLT is a functional language, not a procedural language, so you can't declare a counter. You can use xsl:number to get the position of the current node in its parent, if that helps. You can coerce a string to a number by using the XPath number() function.

Which symbol is used to get the element value in XSLT?

XSLT <xsl:value-of> Element The <xsl:value-of> element is used to extract the value of a selected node.


2 Answers

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.

like image 148
Cerebrus Avatar answered Sep 22 '22 23:09

Cerebrus


Your xpath is just a little off:

count(//Property/long[text()=$parPropId])

Edit: Cerebrus quite rightly points out that the code in your OP (using the implicit value of a node) is absolutely fine for your purposes. In fact, since it's quite likely you want to work with the "Property" node rather than the "long" node, it's probably superior to ask for //Property[long=$parPropId] than the text() xpath, though you could make a case for the latter on readability grounds.

What can I say, I'm a bit tired today :)

like image 23
annakata Avatar answered Sep 26 '22 23:09

annakata