Hi Patrik,
Thank you very much for your help.
I appreciate your solution of rec2 recursion key. It's a good idea!
For XSLT 2.0, unfortunatelly it's unsupported. Perhaps is for this that
<xsl:copy>
<xsl:for-each-group select="*" group-starting-with="rec1">
fails with the error : "xsl:for-each-group cannot be child of xsl:copy"
Could you help me, please, with a solution in XSLT 1.0?
Thanks a lot
Nicola
---------- Messaggio inoltrato ----------
From: nick public <nickpubl(_at_)gmail(_dot_)com>
To: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
Cc:
Bcc:
Date: Wed, 29 Mar 2017 04:11:57 +0200
Subject: XML: From flat to hierarchical with grouping
I need to manipolate this XML:
<root>
<row>
<rec1>
<fld1-1/>
</rec1>
<rec2>
<fld2-1/>
</rec2>
<rec3>
<fld3-1/>
</rec3>
<rec3>
<fld3-2/>
</rec3>
<rec2>
<fld2-2/>
</rec2>
<rec3>
<fld3-3/>
</rec3>
<rec3>
<fld3-4/>
</rec3>
</row>
<row>
<rec1>
<fld1-2/>
</rec1>
<rec2>
<fld2-3/>
</rec2>
<rec3>
<fld3-5/>
</rec3>
<rec3>
<fld3-6/>
</rec3>
<rec2>
<fld2-4/>
</rec2>
<rec3>
<fld3-7/>
</rec3>
<rec3>
<fld3-8/>
</rec3>
</row>
</root>
to convert it in this other hierarchical structure
<root_new>
<row>
<rec1>
<fld1-1/>
<rec2>
<fld2-1/>
<rec3>
<fld3-1/>
</rec3>
<rec3>
<fld3-2/>
</rec3>
</rec2>
<rec2>
<fld2-2/>
<rec3>
<fld3-3/>
</rec3>
<rec3>
<fld3-4/>
</rec3>
</rec2>
</rec1>
</row>
<row>
<rec1>
<fld1-2/>
<rec2>
<fld2-3/>
<rec3>
<fld3-5/>
</rec3>
<rec3>
<fld3-6/>
</rec3>
</rec2>
<rec2>
<fld2-4/>
<rec3>
<fld3-7/>
</rec3>
<rec3>
<fld3-8/>
</rec3>
</rec2>
</rec1>
</row>
</root_new>
To better explain, for each <row> element I have to include in <rec1>,
just 1 per <row>, all <rec2> in same <row> and in each <rec2> all
following <rec3> until next <rec2> in same <row>.
row-rec1-rec2-rec3-rec3-rec2-rec3
have to e transformed in
row
rec1
rec2
rec3
rec3
rec2
rec3
<rec1>, <rec2> and <rec3> have own fields <fld*>.
With following script
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
<xsl:output method="xml" indent="yes"/>
<xsl:key name="k2" match="rec2"
use="generate-id(preceding-sibling::rec1[1])" />
<xsl:key name="k3" match="rec3"
use="generate-id(preceding-sibling::rec2[1])" />
<xsl:template match="/">
<root_new>
<xsl:apply-templates select="//root/row" />
</root_new>
</xsl:template>
<xsl:template match="//row">
<xsl:copy>
<xsl:apply-templates select="rec1" />
</xsl:copy>
</xsl:template>
<xsl:template match="rec1">
<xsl:copy>
<xsl:copy-of select="./*"/>
<xsl:copy-of select="key('k2', generate-id())"/>
<xsl:apply-templates select="rec2" />
</xsl:copy>
</xsl:template>
<xsl:template match="rec2">
<xsl:copy>
<xsl:copy-of select="./*"/>
<xsl:copy-of select="key('k3', generate-id())"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I can obtain this partial result
<?xml version="1.0" encoding="utf-8"?>
<root_new>
<row>
<rec1>
<fld1-1 />
<rec2>
<fld2-1 />
</rec2>
<rec2>
<fld2-2 />
</rec2>
</rec1>
</row>
<row>
<rec1>
<fld1-2 />
<rec2>
<fld2-3 />
</rec2>
<rec2>
<fld2-4 />
</rec2>
</rec1>
</row>
</root_new>
but I'm not able to include the <rec3> elements under the <rec2> that
precede them.
Could you help me please?
Ciao from Italy
Nicola
--------------------------------------------------------
Do you need to index your XML? Try the OpenSource XMLSmartHelper Framework
---------- Messaggio inoltrato ----------
From: "Dr. Patrik Stellmann" <Patrik(_dot_)Stellmann(_at_)gdv-dl(_dot_)de>
To: "xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com"
<xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com>
Cc:
Bcc:
Date: Wed, 29 Mar 2017 04:41:31 +0000
Subject: AW: [xsl] XML: From flat to hierarchical with grouping
Hi Nicola,
you need to process the rec2 elements that you got with key('k2',
generate-id()) recursively instead of just copying them. And since
rec1 does not contain rec2 elements the <xsl:apply-templates
select="rec2" /> has no effect.
So this template should work:
<xsl:template match="rec1">
<xsl:copy>
<xsl:copy-of select="./*"/>
<xsl:apply-templates select="key('k2', generate-id())"/>
</xsl:copy>
</xsl:template>
And if XSLT 2.0 is an option you could write it like this:
<xsl:template match="row">
<xsl:copy>
<xsl:for-each-group select="*" group-starting-with="rec1">
<xsl:copy>
<xsl:copy-of select="*"/>
<xsl:for-each-group select="current-group()[position() > 1]"
group-starting-with="rec2">
<xsl:copy>
<xsl:copy-of select="*"/>
<xsl:copy-of select="current-group()[position() > 1]"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
Some comments to the other code:
- <xsl:apply-templates select="//root/row" /> will look for any root
element on any level. If you know the root element can only be on top
level you should use select="root/row".
- <xsl:template match="//row"> is identical to <xsl:template match="row">
- <xsl:copy-of select="./*"/> is identical to <xsl:copy-of select="*"/>
Regards,
Patrik
------------------------------------------------------------------
Systemarchitektur & IT-Projekte
Tel: +49 40 33449-1142
Fax: +49 40 33449-1400
E-Mail: mailto:Patrik(_dot_)Stellmann(_at_)gdv-dl(_dot_)de
--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/1167547
or by email: xsl-list-unsub(_at_)lists(_dot_)mulberrytech(_dot_)com
--~--