Subject: [xsl] Flat to hierarchical with for-each-group (XSLT 2.0 solution) From: JBryant@xxxxxxxxx Date: Wed, 25 May 2005 11:33:55 -0500 |
Nadia's question yesterday set me to thinking about how to turn flat XML into hierarchical XML by using XSLT 2.0's for-each-group instruction. To simplify matters, I wrote a solution against a generic data structure rather than against Nadia's specific structure (from FrameMaker). Nadia: I'm sure you can translate this solution to your specific source files. If you do have trouble, though, I'll be happy to help. I think this will become an increasingly common problem as more and more companies try to use FrameMaker and Word as front ends to XML-based single-sourcing document-production systems. So, without further ado, here's the source file (flat as the proverbial pancake): <?xml version="1.0" encoding="UTF-8"?> <doc> <heading1>H11</heading1> <para>P1</para> <para>P2</para> <para>P3</para> <heading2>H21</heading2> <para>P4</para> <para>P5</para> <heading3>H31</heading3> <para>P6</para> <heading2>H22</heading2> <para>P7</para> <para>P8</para> <heading1>H12</heading1> <para>P9</para> </doc> Here's the XSLT: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="doc"> <doc> <xsl:for-each-group select="*" group-starting-with="heading1"> <heading1 title="{current-group()[1]}"> <xsl:for-each-group select="current-group()" group-starting-with="heading2"> <xsl:choose> <xsl:when test="current-group()[1]/name() = 'heading2'"> <heading2 title="{current-group()[1]}"> <xsl:for-each-group select="current-group()" group-starting-with="heading3"> <xsl:choose> <xsl:when test="current-group()[1]/name() = 'heading3'"> <heading3 title="{current-group()[1]}"> <xsl:apply-templates select="current-group()"/> </heading3> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="current-group()"/> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </heading2> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="current-group()"/> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </heading1> </xsl:for-each-group> </doc> </xsl:template> <xsl:template match="heading1|heading2|heading3"/> <xsl:template match="para"> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet> And here's the result (now hierarchical): <?xml version="1.0" encoding="UTF-8"?> <doc> <heading1 title="H11"> <para>P1</para> <para>P2</para> <para>P3</para> <heading2 title="H21"> <para>P4</para> <para>P5</para> <heading3 title="H31"> <para>P6</para> </heading3> </heading2> <heading2 title="H22"> <para>P7</para> <para>P8</para> </heading2> </heading1> <heading1 title="H12"> <para>P9</para> </heading1> </doc> The only mystery to me is why I had to use test="current-group()[1]/name() = 'heading2'" rather than test="current-group()/heading2" or just test="heading2". I'd love to hear the explanation for that one. Thanks. Jay Bryant Bryant Communication Services (presently consulting at Synergistic Solution Technologies)
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
[xsl] [ANN] Xml2PDF version 2.3 is , Altsoft Xml2PDF | Thread | Re: [xsl] Flat to hierarchical with, David Carlisle |
Re: [xsl] xsl:apply-templates in ja, Wendell Piez | Date | Re: [xsl] Flat to hierarchical with, David Carlisle |
Month |