Tricky XSLT

Here should go questions about transforming XML with XSLT and FOP.
ibelic
Posts: 2
Joined: Thu Dec 21, 2006 8:39 pm

Tricky XSLT

Post by ibelic »

Hello all,

here is the sample structure of the import file:

Code: Select all

MT_PO
DT_PO
DocumentHeader
ItemHeader
ItemDetail
DT_PO
DocumentHeader
ItemHeader
ItemDetail
ItemDetail
ItemHeader
ItemDetail
So, as you see, DocumentHeader is 1:1, ItemHeader is 1:1 and ItemDetail is 1:n cardinality. The problem is that ItemHeader and ItemDetail are on the same level in the structure, and I need transformation to structure like this:

Code: Select all

MT_PO
DT_PO
DocumentHeader
Items
Item
ItemHeader
ItemDetail
DT_PO
DocumentHeader
Items
Item
ItemHeader
ItemDetail
ItemDetail
Item
ItemHeader
ItemDetail[/quote]


Following transformation does not work because (of course) for each ItemHeader it reads all ItemDetail in the parrent node:

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<MT_PO>
<xsl:for-each select="MT_PO/DT_PO">
<DT_PO>
<xsl:copy-of select="DocumentHeader"/>
<Items>
<xsl:for-each select="ItemHeader">
<Item>
<xsl:copy-of select="."/>
<xsl:for-each select="../ItemDetail">
<xsl:copy-of select="."/>
</xsl:for-each>
</Item>
</xsl:for-each>
</Items>
</DT_PO>
</xsl:for-each>
</MT_PO>
</xsl:template>
</xsl:stylesheet>
Does anybody has idea how to do that?

Thanks in advance!

Best regards,
Ivan
Radu
Posts: 9439
Joined: Fri Jul 09, 2004 5:18 pm

Post by Radu »

Hi,

Here are two ways to accomplish this, one using the "xsl:for-each-group" construction from XSLT 2.0:

Code: Select all


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="/">
<MT_PO>
<xsl:apply-templates select="MT_PO/DT_PO"/>
</MT_PO>
</xsl:template>
<xsl:template match="DT_PO">
<DT_PO>
<xsl:copy-of select="MT_PO/DT_PO/DocumentHeader"/>
<Items>
<xsl:for-each-group select="ItemHeader|ItemDetail" group-starting-with="ItemHeader">
<Item>
<xsl:for-each select="current-group()">
<xsl:copy-of select="."/>
</xsl:for-each>
</Item>
</xsl:for-each-group>
</Items>
</DT_PO>
</xsl:template>
</xsl:stylesheet>
and the other using classic XSLT 1.0:

Code: Select all


<?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="/">
<MT_PO>
<xsl:apply-templates select="MT_PO/DT_PO"/>
</MT_PO>
</xsl:template>
<xsl:template match="DT_PO">
<DT_PO>
<xsl:copy-of select="MT_PO/DT_PO/DocumentHeader"/>
<Items>
<xsl:for-each select="ItemHeader">
<Item>
<xsl:copy-of select="."/>
<xsl:variable name="headerId" select="generate-id(.)"/>
<xsl:for-each select="following-sibling::ItemDetail[generate-id(preceding-sibling::ItemHeader[1])=$headerId]">
<xsl:copy-of select="."/>
</xsl:for-each>
</Item>
</xsl:for-each>
</Items>
</DT_PO>
</xsl:template>
</xsl:stylesheet>
Regards,
Radu
ibelic
Posts: 2
Joined: Thu Dec 21, 2006 8:39 pm

Tricky XSLT

Post by ibelic »

I think you're a genius :)

Thank you VERY much!

Regards,
Ivan
Post Reply