xsl-list
[Top] [All Lists]

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

2006-05-25 14:15:46
Dear James,

Wow! I am simply, just, totally impressed! I just copied and pasted your solution a few hours ago and it worked at once. Then the strangest thing 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.

Well, that's not so strange, but I again copied and pasted your solution to start all over, but didn't get the lovely result back, not even with your sole solution, which worked before. I know, something must have changed, but I couldn't find it. Even undoing to very first beginning didn't give me back that euforic moment.

Not to worry, after several hours of really losing the feeling that I understood something of xslt, I tweaked a little the bold way, just inserting stuff where I wanted it in the output stream, leaving the overall framework as it was delivered by you.

Somehow, I figured that this "catch all" was not catching "all", and because every element has an "@id" attribute, the xsl:choose did not quite work. But then again, why *did* it work in the first place? I'm at a loss here and must admit that I understand far less of functional programming than I thought.

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.

Cheers!
Abel


STYLESHEET:
<?xml version="1.0" encoding="UTF-8"?>
<?altova_samplexml D:\Workspaces\Eclipse Nuntia\nuntia-spline-tablewiz\resources\examples\xml\getTableNamesResponse.xml?>

<xsl:stylesheet version="1.0" exclude-result-prefixes="twz xsl"
   xmlns:twz="http://www.nuntia.com/tablewiz1.0";
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

<!--
NOTE: xpath-default-namespace cannot be used in XSLT 1.0. And browser
don't understand XSLT 2.0 yet. Be aware that only XSLT 1.0 features can be used
-->

   <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="/">
       <tree id="0">

<!-- process the first tableName with a category-name matching 'Root' -->
           <xsl:apply-templates
               select="//twz:tableName[(_at_)category-name='Root'][1]" />
       </tree>
   </xsl:template>

   <!--
       Process each 1st tableName by emitting an item and
       then processing its descendents and then its children.
   -->
<xsl:template match="twz:tableName[generate-id(.)=generate-id(key('table-by-category', @category-name)[1])]">
       <item text="{(_at_)category-name}" id="{(_at_)category-name}">

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

<!-- process all other tableName entries with the same category-name --> <xsl:apply-templates select="key('table-by-category',@category-name)[position() != 1]" />

       <!-- make sure to create an item of this category as well -->
       <item text="{string(.)}" id="{(_at_)id}" />
       </item>
   </xsl:template>

   <!--
       Catch-all rule to emit an item for each tableName, not processing
       any children or siblings.
   -->
   <xsl:template match="twz:tableName">
       <item text="{string(.)}" id="{(_at_)id}" />
   </xsl:template>

</xsl:stylesheet>

INPUT
<response xmlns="http://www.nuntia.com/tablewiz1.0"; response="getTableNames" application="tablewiz" table-mnemonic="generic"> <responseMessage response-status="success">Table names retrieved successfully!</responseMessage> <userData timestamp="648289332528" request="getTableNames" client-version="1.0 beta"/>
     <tableName id="5" category-name="Root">testcsv3</tableName>
     <tableName id="7" category-name="Root">[New table 2]</tableName>
     <tableName id="8" category-name="Root">ee</tableName>
<tableName id="9" category-name="Personnel" category-parent-name="General">Child of Personnel</tableName> <tableName id="10" category-name="Personnel" category-parent-name="General">Other personnel member</tableName> <tableName id="11" category-name="Personnel" category-parent-name="General">Final personnel member</tableName> <tableName id="12" category-name="Printers" category-parent-name="Root">List of printers locally</tableName> <tableName id="13" category-name="Printers" category-parent-name="Root">External Oce printers</tableName>
     <tableName id="14" category-name="Root">Very rooty table</tableName>
<tableName id="15" category-name="General" category-parent-name="Root">General usage table</tableName> <tableName id="16" category-name="General" category-parent-name="Root">General cooking table</tableName> <tableName id="17" category-name="General" category-parent-name="Root">General General</tableName> <tableName id="18" category-name="General" category-parent-name="Root">Genes of mambutam</tableName> <tableName id="19" category-name="General" category-parent-name="Root">Generosity clears</tableName> <tableName id="20" category-name="Signatures" category-parent-name="Personnel">List of signatures</tableName>
 </response>

OUTPUT:
<tree id="0">
   <item text="Root" id="Root">
       <item text="Printers" id="Printers">
           <item text="External Oce printers" id="13"/>
           <item text="List of printers locally" id="12"/>
       </item>
       <item text="General" id="General">
           <item text="Personnel" id="Personnel">
               <item text="Signatures" id="Signatures">
                   <item text="List of signatures" id="20"/>
               </item>
               <item text="Other personnel member" id="10"/>
               <item text="Final personnel member" id="11"/>
               <item text="Child of Personnel" id="9"/>
           </item>
           <item text="General cooking table" id="16"/>
           <item text="General General" id="17"/>
           <item text="Genes of mambutam" id="18"/>
           <item text="Generosity clears" id="19"/>
           <item text="General usage table" id="15"/>
       </item>
       <item text="[New table 2]" id="7"/>
       <item text="ee" id="8"/>
       <item text="Very rooty table" id="14"/>
       <item text="testcsv3" id="5"/>
   </item>
</tree>



James A. Robinson wrote:

I'm afraid I'm not following what you want done with the namespaces,
but in general I think that if you are stuck doing this in XSLT 1.0
you can achieve your goal of emitting a tree from a flat list by using
the Muenchian grouping technique:

 http://www.jenitennison.com/xslt/grouping/muenchian.html

I'm dashing this off before I head to work, so apologies if it isn't
as complete as you spec out.  I *think* it does what you want in terms
of the tree structure.  Apologies if I've misunderstood what you are
after!

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

 <xsl:output indent="yes" />

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

<!-- At the root of our output tree emit the tableName for 'Root'
 -->
 <xsl:template match="/">
   <xsl:element name="abel:tree">
     <xsl:attribute name="id">0</xsl:attribute>

     <!-- process the first tableName with a category-name matching 'Root' -->
     <xsl:apply-templates select="//abel:tableName[(_at_)category-name='Root'][1]" 
/>
   </xsl:element>
 </xsl:template>

<!-- Process each 1st tableName by emitting an item and
   then processing its descendents and then its children.
 -->
 <xsl:template 
match="abel:tableName[generate-id(.)=generate-id(key('table-by-category', 
@category-name)[1])]">
   <xsl:element name="abel:item">

     <xsl:attribute name="text">
       <xsl:value-of select="string(.)" />
     </xsl:attribute>

     <xsl:attribute name="id">
       <xsl:choose>
         <xsl:when test="@id">
           <xsl:value-of select="@id" />
         </xsl:when>
         <xsl:otherwise>
           <xsl:value-of select="@category-name" />
         </xsl:otherwise>
       </xsl:choose>
     </xsl:attribute>

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

     <!-- process all other tableName entries with the same category-name -->
     <xsl:apply-templates select="key('table-by-category',@category-name)[position() != 
1]" />

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

 <!--
   Catch-all rule to emit an item for each tableName, not processing
   any children or siblings.
 -->
 <xsl:template match="abel:tableName">
   <xsl:element name="abel:item">

     <xsl:attribute name="text">
       <xsl:value-of select="string(.)" />
     </xsl:attribute>

     <xsl:attribute name="id">
       <xsl:choose>
         <xsl:when test="@id">
           <xsl:value-of select="@id" />
         </xsl:when>
         <xsl:otherwise>
           <xsl:value-of select="@category-name" />
         </xsl:otherwise>
       </xsl:choose>
     </xsl:attribute>

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

</xsl:stylesheet>

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

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--





--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--