[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

RE: [xsl] Constructing a tree from leaf nodes (knowing the tree structure)?


Subject: RE: [xsl] Constructing a tree from leaf nodes (knowing the tree structure)?
From: "Simon Shutter" <simon@xxxxxxxxxxx>
Date: Thu, 19 Apr 2007 15:04:12 -0700

Ken,

Wow!  I hadn't expected a solution but this is great and nicely documented
also.  I will give it a go on my real data.

Thank you very much,

Simon

-----Original Message-----
From: G. Ken Holman [mailto:gkholman@xxxxxxxxxxxxxxxxxxxx] 
Sent: April 19, 2007 1:44 PM
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
Subject: Re: [xsl] Constructing a tree from leaf nodes (knowing the tree
structure)?

At 2007-04-19 13:01 -0700, Simon Shutter wrote:
>This is probably a poorly posed question but essentially I am trying to
>determine if XSLT is suited to the following problem.

Giving all inputs and expected output is a great start.  All I had to 
guess was what the value of result= is supposed to be ... I've 
assumed it is a logical and of descendent result= attributes.

>Say I have a node fragment that defines a tree structure in which each
>element appears only once eg.
>...
>I then have data for some of the leaf nodes ie.
>...
>In this example the leaf node </f> is missing.
>
>Is it possible to create a node fragment that mimics the tree structure and
>sets ancestor attributes according to the presence or absence of leaf nodes
>and their attributes?
>
>The desired output would be:
>
><a complete="false" result="false">
>   <b complete="true" result="false">
>     <c complete="true" result="true"/>
>     <d complete="true" result="false"/>
>   </b>
>   <e complete="false" result="true">
>     <f complete="false" result=""/>
>     <g complete="true" result="true"/>
>       <h complete="true" result="true"/>
>       <i complete="true" result="true"/>
>     </g>
>   </e>
></a>

The example below appears to do what you want ... what was it about 
XSLT processing that you wanted to know to measure suitability?  What 
is nice about XSLT is its node orientation and use of the descendant 
axis, which I took advantage of.  I haven't tried the answer with 
XSLT 1, the answer below uses XSLT 2.

Someone else on the list might have a more compact solution than mine.

I hope this helps.

. . . . . . . . . . .  Ken


T:\ftemp>call xslt2 shutter.xml shutter.xsl shutter.out

T:\ftemp>type shutter.out
<a complete="false" result="false">
   <b complete="true" result="false">
     <c complete="true" result="true"/>
     <d complete="true" result="false"/>
   </b>
   <e complete="false" result="true">
     <f complete="false" result=""/>
     <g complete="true" result="true">
       <h complete="true" result="true"/>
       <i complete="true" result="true"/>
     </g>
   </e>
</a>
T:\ftemp>type leaves.xml
<leaves>
<c complete="true" result="true"/>
<d complete="true" result="false"/>
<h complete="true" result="true"/>
<i complete="true" result="true"/>
</leaves>

T:\ftemp>type shutter.xml
<a>
   <b>
     <c/>
     <d/>
   </b>
   <e>
     <f/>
     <g>
       <h/>
       <i/>
     </g>
   </e>
</a>

T:\ftemp>type shutter.xsl
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                 version="2.0">

<xsl:output omit-xml-declaration="yes"/>

<xsl:key name="leaves" match="*[not(*)]" use="name(.)"/>

<xsl:variable name="leaves" select="document('leaves.xml')"/>

<xsl:template match="*">
   <xsl:copy>
     <xsl:attribute name="complete">
       <xsl:choose>
         <!--at a leaf use the value from leaves, or 'false' if not there-->
         <xsl:when test="not(*)">
           <xsl:value-of select="( key('leaves',name(.),$leaves)/@complete,
                                   'false' )[1]"/>
         </xsl:when>
         <!--at a branch, check all leaves for any not being in leaf file-->
         <xsl:when test=".//*[not(*)][not(key('leaves',name(.),$leaves))]"
                       >false</xsl:when>
         <!--there are no absent leaves, so this is complete-->
         <xsl:otherwise>true</xsl:otherwise>
       </xsl:choose>
     </xsl:attribute>
     <xsl:attribute name="result">
       <xsl:choose>
         <!--at a leaf, use the value from leaves, or '' if not there-->
         <xsl:when test="not(*)">
           <xsl:value-of select="key('leaves',name(.),$leaves)/@result"/>
         </xsl:when>
         <!--at a branch, check all leaves for any being false-->
         <xsl:when test=".//*[not(*)]/key('leaves',name(.),$leaves)/@result
=
                         'false'">false</xsl:when>
         <!--there are none that are false, so then all must be true-->
         <xsl:otherwise>true</xsl:otherwise>
       </xsl:choose>
     </xsl:attribute>
     <xsl:apply-templates/>
   </xsl:copy>
</xsl:template>

</xsl:stylesheet>
T:\ftemp>rem Done!

--
World-wide corporate, govt. & user group XML, XSL and UBL training
RSS feeds:     publicly-available developer resources and training
G. Ken Holman                 mailto:gkholman@xxxxxxxxxxxxxxxxxxxx
Crane Softwrights Ltd.          http://www.CraneSoftwrights.com/s/
Box 266, Kars, Ontario CANADA K0A-2E0    +1(613)489-0999 (F:-0995)
Male Cancer Awareness Aug'05  http://www.CraneSoftwrights.com/s/bc
Legal business disclaimers:  http://www.CraneSoftwrights.com/legal


Current Thread
Keywords