Node account and availability - XSL

I need to write a generic xsl that will take in an xml document and output the number of nodes and their names. So if I have a file like:

   <assets>
    <asset>
        <type>hardware</type>
        <item>
            <name>HP laptop</name>
            <value>799</value>
        </item>
        <item>
            <name>server</name>
            <value>1000</value>
        </item>
        <item>
            <name>ViewSonic Monitor</name>
            <value>399</value>
        </item>
    </asset>
    <asset>
        <type>software</type>
        <item>
            <name>Windows Vista</name>
            <value>399</value>
        </item>
        <item>
            <name>Office XP</name>
            <value>499</value>
        </item>
        <item>
            <name>Windows 7</name>
            <value>399</value>
        </item>
          <item>
            <name>MS Project Professional 2007</name>
            <value>299</value>
          </item>
       </asset>
    </assets>

      

The output would be:

   <output>
    <node name="assets" count="1"/>
    <node name="asset" count="2"/>
    <node name= "type" count="??"/>
    <node name="item" count=??/>
    <node name="name" count=??/>
    <node name="value" count=??/>
    </output>

      

+2


source to share


3 answers


You want to use the count function:

<xsl:value-of select="count(assets/asset)" />

      



So your code will look like this:

Assets: <xsl:value-of select="count(assets)" />
Asset:  <xsl:value-of select="count(assets/asset)" />
Item:   <xsl:value-of select="count(assets/asset/item)" />

      

+5


source


A generic input solution containing nodes with any name can be accomplished with Muenchian's method :

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:key name="nodes-by-name" match="*" use="name()"/>
  <xsl:template match="/">
    <output>
      <xsl:for-each select="//*[count(.|key('nodes-by-name', name())[1]) = 1]">
        <node name="{name()}" count="{count(key('nodes-by-name', name()))}"/>
      </xsl:for-each>
    </output>
  </xsl:template>
</xsl:stylesheet>

      



Explanation: Using xsl:key

, create a mapping from names to nodes that have this name. Then repeat all unique names and print the node counter for the name. The main trick here is how to iterate over the unique names. See the linked page for an explanation of the idiom count(.|foo)=1

used to determine if foo

is a set of node containing only the context of a node.

+4


source


This is my solution using XSLT 2.0:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">

   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="/">
      <xsl:element name="output">
         <xsl:for-each-group select="//*" group-by="name()">
            <xsl:element name="node">
               <xsl:attribute name="name">
                  <xsl:value-of select="current-grouping-key()"/>
               </xsl:attribute>
               <xsl:attribute name="count">
                  <xsl:value-of select="count(current-group())"/>
               </xsl:attribute>
            </xsl:element>
         </xsl:for-each-group>
      </xsl:element>
   </xsl:template>

</xsl:stylesheet>

      

+1


source







All Articles