views:

82

answers:

1

I'd like to convert an XML file into another XML file with a different structure using XSL and I'm kindda new to XSL. The input section of the XML goes like this:

<set>
    <object> Value </object>
        <name> Value </name>
        <tag>
            <event> Value </event>
            <group> Value </group>
            <other> Value </other>
        </tag>
    <object>
    <object>...</object>
</set>

I would like the desired output to be:

<set>
    <event>
        <group>
            <object name="value">
                <tag>
                    <other> Value </other>
                </tag>
            </object>
        <group>
        <group>...</group>
    <event>...</event>
</set>

Meaning to say I'd like to search through the input xml and group the objects according to say the 'event' nodes followed by the 'group' nodes with the same value instead. Help?

A: 

If you're xslt processor supports exslt you could use set:distinct to loop over distinct events and distinct groups that have that event. I tested the following with xsltproc, but they should work in any processor that supports exslt:function. see: http://www.exslt.org/func/elements/function/index.html.

Please note that there is a bug in the implementation of set:distinct posted there that I've fixed for you here.

test.xslt:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:func="http://exslt.org/functions"
                xmlns:set="http://exslt.org/sets"
                extension-element-prefixes="func set">

<xsl:import href="set.distinct.function.xsl" />

<xsl:template match="set">
  <set>
  <xsl:variable name="set" select="."/>
  <xsl:value-of select="count(object/tag/event)"/><xsl:text> </xsl:text>
  <xsl:value-of select="count(set:distinct(object/tag/event))"/>
  </xsl:for-each>
  <xsl:for-each select="set:distinct(object/tag/event)">
    <event>
    <xsl:attribute name="name">
      <xsl:value-of select="."/>
    </xsl:attribute>
    <xsl:variable name="event" select="." />
    <xsl:for-each select="set:distinct($set/object/tag[event=$event]/group)">
      <group>
      <xsl:attribute name="name">
        <xsl:value-of select="."/>
      </xsl:attribute>
      <xsl:variable name="group" select="." />
      <xsl:apply-templates select="$set/object[tag/event=$event][tag/group=$group]" />
      </group>
    </xsl:for-each>
    </event>
  </xsl:for-each>
  </set>
</xsl:template>
<xsl:template match="object">
  <object name="{name}"/>
</xsl:template>
</xsl:stylesheet>

set.distinct.function.xsl:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:exsl="http://exslt.org/functions"
                xmlns:set="http://exslt.org/sets"
                extension-element-prefixes="exsl"
                exclude-result-prefixes="set">

<exsl:function name="set:distinct">
   <xsl:param name="nodes" select="/.." />
   <xsl:choose>
      <xsl:when test="not($nodes)">
         <exsl:result select="/.." />
      </xsl:when>
      <xsl:otherwise>
         <xsl:variable name="distinct" 
                       select="set:distinct($nodes[position() > 1])" />
         <exsl:result select="$distinct | $nodes[1][not(. = $distinct)]" />
      </xsl:otherwise>
   </xsl:choose>
</exsl:function>

</xsl:stylesheet>

test.xml:

<set>
    <object>
        <name> Value1 </name>
        <tag>
            <event> Value </event>
            <group> Value </group>
            <other> Value </other>
        </tag>
    </object>
    <object>
        <name> Value2 </name>
        <tag>
            <event> Value </event>
            <group> Value2 </group>
            <other> Value </other>
        </tag>
    </object>
    <object>
        <name> Value3 </name>
        <tag>
            <event> Value </event>
            <group> Value2 </group>
            <other> Value </other>
        </tag>
    </object>
    <object>
        <name> Value4 </name>
        <tag>
            <event> Value </event>
            <group> Value </group>
            <other> Value </other>
        </tag>
    </object>
    <object>
        <name> Value5 </name>
        <tag>
            <event> Value2 </event>
            <group> Value </group>
            <other> Value </other>
        </tag>
    </object>
    <object>
        <name> Value6 </name>
        <tag>
            <event> Value2 </event>
            <group> Value </group>
            <other> Value </other>
        </tag>
    </object>
</set>
Kyle Butt
How do I import a processor which supports exslt? Or what do I do if the processor does not support exslt?
Hariz
which processor are you using?
Kyle Butt
Its ok! I managed to import the exslt but another problem came up... says Function 'set:distinct()' has failed ... --> System.ArgumentNullException: Value cannot be null.
Hariz