Re: [xsl] Flattening hierarchic xml, retaining some structure

Subject: Re: [xsl] Flattening hierarchic xml, retaining some structure
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Wed, 19 Jan 2005 18:09:33 -0500
Ragulf,

This is actually not a straightforward flattening, though it may appear to be.

The trick is that you not only need to flatten, you also need to group. Some subnode elements here (the one that have mynode children, not content) will disappear in the flattening, while the others (those with content) will be promoted -- but the latter have to be grouped, with siblings, but not with all siblings, just with those up to the next subnode with a mynode child.

So one hierarchy has to be interpolated while another is removed!

(Note how much easier it would be if you could just leave all mynode[@name='xxx'] nodes together, rather than leave all subnodes in their current order.)

For this kind of problem in XSLT 1.0 there are two tried-and-true methods: key-based grouping by "leader" nodes (an adaptation of Muenchian positional grouping), and the fun "tree visitor" or "forward stepping" technique, whereby you explicitly walk the tree forward one node at a time, copying them until you hit one that ends the group.

I have used the latter approach, below.

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

<xsl:output indent="yes"/>

  <xsl:template match="/">
    <xsl:apply-templates select="//subnode[not(mynode)]"/>
    <!-- Reach down and grab all the subnodes that are candidates
         for promotion. -->
  </xsl:template>

<xsl:template match="subnode">
<xsl:choose>
<!-- This is implemented as a 'choose' for clarity: you could have
done the filtering in the 'select'. -->
<xsl:when test="preceding-sibling::subnode[1][not(mynode)]"/>
<!-- When there's an immediately preceding subnode sibling without
a mynode child, we're already accounted for, so we're suppressed. -->
<xsl:otherwise>
<xsl:variable name="lead-subnode" select="."/>
<xsl:for-each select="ancestor::mynode[1]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="$lead-subnode" mode="tuck"/>
<!-- Here we kick off the 'forward step' explicit recursion -->
</xsl:copy>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>


  <xsl:template match="subnode" mode="tuck">
    <xsl:copy-of select="."/>
    <xsl:apply-templates select="following-sibling::subnode[1]" mode="tuck"/>
    <!-- Step forward to the next sibling if there is one. -->
  </xsl:template>

  <xsl:template match="subnode[mynode]" mode="tuck"/>
  <!-- When this template is matched, we're done tucking for now. -->

Cheers,
Wendell

At 09:56 AM 1/19/2005, you wrote:
Hi, I have a hierarchic structure, that I would like to flatten.
I have tried looking for some examples, but I have not been able to expand the examples I have found, so that they will work on my problem.
(I have no influence on either input or output format, which is simplified here)


Regards,
Ragulf Pickaxe :-)

Expanding on the example from David Pawson's site:
http://www.dpawson.co.uk/xsl/sect2/flatten.html
I have the following XML and would like to generate the flat structure as shown:


Input XML:
<mynode name="xxx">
 <subnode>Some text</subnode>
 <subnode>
   <mynode name="yyy">
     <subnode><b>Some bold text</b></subnode>
     <subnode>Some text where some of it is <b>bold text</b></subnode>
     <subnode>
       <mynode name="zzz">
         <subnode>Some text</subnode>
         <subnode>Some text</subnode>
       </mynode>
     </subnode>
     <subnode>Yet some text</subnode>
   </mynode>
 </subnode>
 <subnode>Some more text, <i>italic</i> is some of it</subnode>
</mynode>

Output XML:
<mynode name="xxx">
 <subnode>Some text</subnode>
<mynode name="yyy">
 <subnode><b>Some bold text</b></subnode>
 <subnode>Some text where some of it is <b>bold text</b></subnode>
</mynode>
<mynode name="zzz">
 <subnode>Some text</subnode>
 <subnode>Some text</subnode>
</mynode>
<mynode name="yyy">
 <subnode>Yet some text</subnode>
</mynode>
<mynode name="xxx">
 <subnode>Some more text, <i>italic</i> is some of it</subnode>
</mynode>

I have tried some various forms of modes, and such, but alas no success, and no idea how to obtain success. The problem is with retaining the subnode under mynode structure, while also having the subnodes in the same document order as in the input.

_________________________________________________________________
FREE pop-up blocking with the new MSN Toolbar - get it now! http://toolbar.msn.click-url.com/go/onm00200415ave/direct/01/

___&&__&_&___&_&__&&&__&_&__&__&&____&&_&___&__&_&&_____&__&__&&_____&_&&_
"Thus I make my own use of the telegraph, without consulting
the directors, like the sparrows, which I perceive use it
extensively for a perch." -- Thoreau


Current Thread