[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Re: XSLT 1: From flat XML to tree hierarchy XML. Can't

Subject: Re: XSLT 1: From flat XML to tree hierarchy XML. Can't seem to find the right way to do it.
From: Abel Braaksma Online <abel.online@xxxxxxxxx>
Date: Thu, 25 May 2006 23:15:22 +0200
altova_samplexml
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[@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="{@category-name}" id="{@category-name}">


<!-- process each 1st tableName which has a parent-category-name matching our category-name -->
<xsl:apply-templates
select="//twz:tableName
[@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="{@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="{@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[@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
                 [@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.robinson@xxxxxxxxxxxx
Stanford University HighWire Press      http://highwire.stanford.edu/
+1 650 7237294 (Work)                   +1 650 7259335 (Fax)

Current Thread

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Cast Your Vote

We need your help – Vote for DataDirect XML Products!

  • Best SOA or XML site

Winners and finalists announced at SOA World Conference in November.

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2007 All Rights Reserved.