views:

173

answers:

1

This extends from http://stackoverflow.com/questions/2133459/xml-to-xml-using-xsl-problem I managed to import exslt and modified my codes according to the solution (thanks to Kyle Butt) given as follows:

<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="set">

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

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

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

But theres error in the output which says- 'Function set:distinct() has failed. Value Cannot be null.' How to solve this?

Btw The XML input:

<gallery>
  <photo>
    <tag>
      <event>Birthday</event>
      <group>Family</group>
      <other>Swensens</other>
    </tag>
  </photo>
  <photo>..</photo>
</gallery>

& Required XML output:

<gallery>
  <event name="Birthday">
    <group name="Family">
      <photo>
        <tag>
         <other>Swensens</other>
        </tag>
      </photo>
      <photo>..</photo>
    </group>
    <group>..</group>
  </event>
  <event>..</event>
</gallery>
A: 

When I run your XSL on your input as-is, I'm not getting an error, but this is the output I get:

<gallery>
  Birthday
  <event name="Birthday"/>
</gallery>

So there are a number of things going on here that aren't getting you to the output that you want. One thing is that you're just outputting the name of the event as the gallery node's value, so to get rid of that you'll want to remove the <xsl:value-of select="." /> within the first xsl:for-each (was this debugging code left in, perhaps?)

Then looking at this part within the xsl:for-each on the events:

<xsl:variable name="event" select="." />
<xsl:for-each select="set:distinct($gallery/object/tag[event=.]/group)">

I'm seeing a few issues with the XPath within the set:distinct:

  1. The node beneath the gallery node in the XML input you gave is <photo>, and in your XPath you have "object".
  2. You're not using the $event variable to check the event node-- the predicate on tag should be [event=$event].

These are what's contributing to the error you're seeing-- the XPath isn't selecting anything so you're calling set:distinct on null.

So making these corrections, the output I get is:

<gallery>
  <event name="Birthday">
    <group name="Family"/>
  </event>
</gallery>

Then the XPath in <xsl:apply-templates select="$gallery/object[tag/event=$event][tag/group=$group]" /> needs to have object changed to photo as well. In my stylesheet I added a template that matches photo since it wasn't included in your sample stylesheet, so my stylesheet now looks like:

<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="set">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="gallery">
  <gallery>
    <xsl:variable name="gallery" select="."/>
    <xsl:for-each select="set:distinct(photo/tag/event)">
      <event name="{.}">
        <xsl:variable name="event" select="." />
        <xsl:for-each select="set:distinct($gallery/photo/tag[event=$event]/group)">
          <group name="{.}">
            <xsl:variable name="group" select="." />
            <xsl:apply-templates select="$gallery/photo[tag/event=$event][tag/group=$group]" />
          </group>
        </xsl:for-each>
      </event>
    </xsl:for-each>
  </gallery>
</xsl:template>

<xsl:template match="photo">
  <photo>
    <tag>
      <xsl:copy-of select="tag/*[not(name(.)='group') and not(name(.)='event')]" />
    </tag>
  </photo>
</xsl:template>
</xsl:stylesheet>

and the output I get is:

<gallery>
  <event name="Birthday">
    <group name="Family">
      <photo>
        <tag>
          <other>Swensens</other>
        </tag>
      </photo>
    </group>
  </event>
</gallery>

Tada!!

carolclarinet