Re: [xsl] XSLT 1: From flat XML to tree hierarchy XML. Can't seem to find the right way to do it.

Subject: Re: [xsl] XSLT 1: From flat XML to tree hierarchy XML. Can't seem to find the right way to do it.
From: "James A. Robinson" <jim.robinson@xxxxxxxxxxxx>
Date: Thu, 25 May 2006 15:31:44 -0700
> happened: tweaking the solution a bit to get rid of the namespace 
> prefixes (that's what the target system can't quite well deal with, and 
> MS 3.0/4.0 processor does not work quite well with 
> exclude-result-prefixes in all circumstances, or I just do something 
> wrong there: the prefixes are not removed) and soon the output changed.

Perhaps the input changed? If, for example, the namespace changed, my
example would have stopped working unless it was updated to reflect
the new namespace.


> Below is my solution, input, output and xslt all from live system. I 
> post it here, because perhaps you can tell me / help me with 
> understanding why the simpler and more straightforward solution of you 
> is not working anymore, and where I went wrong along the way (btw, for 
> brevity, I removed your xsl:element with just the elements themselves. 
> Not sure if that's a best practice, but anyway).
> 
> Thanks a lot for your time and effort, I think I couldn't ever come up 
> with this myself. I should learn about the key/generate-id thing hoping 
> to somehow understand. it.

I see now that I misunderstood what your desired output was.  I hadn't
picked up on the fact that you were wanting a container element for
each 1st tableName/@category-name, and then an 'item' within the
container which corresponded to that 1st tableName/@category-name.

That does add to the complexity, the match I specified to catch the
1st tableName/@category-name basically prevents you from doing a
simple apply-templates to include it in your container.

I tweaked my example to reflect my current understanding of what you
need.  It is similar to what you are doing, but it shows how you can
use mode selection to keep the code which emits an 'item' element in
one place.

I think your use of literal result elements is fine, I've thought of
it as just a matter of style.  I'd be curious if people on this list
have opinions on whether or not they've come up with rules for when to
use literal result elements and when to use xsl:element.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="1.0"
                xmlns:twz="http://www.nuntia.com/tablewiz1.0";>

  <xsl:output indent="yes" />

  <!-- table-by-category lets us look up all tableName elements matching a category -->
  <xsl:key name="table-by-category" match="twz:tableName" use="@category-name" />

  <!-- 
    At the root of our output tree emit the tableName for 'Root' -->
  <xsl:template match="/">
    <xsl:element name="tree">
      <xsl:attribute name="id">0</xsl:attribute>
      <!-- process the first tableName with a category-name matching 'Root' -->
      <xsl:apply-templates select="//twz:tableName[@category-name='Root'][1]" />
    </xsl:element>
  </xsl:template>

  <!-- 
    Emit a general item node for every 1st instance of a tableName
    with a specific category-name. Then process children, siblings,
    and self. -->
  <xsl:template match="twz:tableName[generate-id(.)=generate-id(key('table-by-category', @category-name)[1])]">
    <xsl:element name="item">
      <xsl:attribute name="text">
        <xsl:value-of select="@category-name" />
      </xsl:attribute>
      <xsl:attribute name="id">
        <xsl:value-of select="@category-name" />
      </xsl:attribute>

      <!-- process each 1st tableName which has a parent-category-name matching ours  -->
      <xsl:apply-templates select="//twz:tableName
                  [@category-parent-name=current()/@category-name]
                  [generate-id(.)=generate-id(key('table-by-category', @category-name)[1])]" />

      <!-- emit item for each tableName with this category-name, in document order -->
      <xsl:apply-templates mode="item" select="key('table-by-category',@category-name)" />
      <!--
        You could replace the line above with the following two lines of code if you needed
        the current tableName emitted as the last item in the sequence. -->
      <!--
      <xsl:apply-templates mode="standalone" select="key('table-by-category',@category-name)[position() != 1]" />
      <xsl:apply-templates mode="standalone" select="." />
      -->

    </xsl:element>
  </xsl:template>

  <!--
    Catch-all rule to emit an item for each tableName,
    not processing any children or siblings. -->
  <xsl:template mode="item" match="twz:tableName">
    <xsl:element name="item">
      <xsl:attribute name="text">
        <xsl:value-of select="string(.)" />
      </xsl:attribute>
      <xsl:copy-of select="@id" />
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
James A. Robinson                       jim.robinson@xxxxxxxxxxxx
Stanford University HighWire Press      http://highwire.stanford.edu/
+1 650 7237294 (Work)                   +1 650 7259335 (Fax)

Current Thread