I have a requirement to transform a sequential XML node list into a hierarchical, but I run into some XSLT specific knowledge gap. The input XML contains articles, colors and sizes. In the sample below 'Record1' is an article, 'Record2' represents a color and 'Record3' are the sizes. The number of colors and sizes (record2 and record3) elements can vary.
<root>
<Record1>...</Record1>
<Record2>...</Record2>
<Record3>...</Record3>
<Record3>...</Record3>
<Record2>...</Record2>
<Record3>...</Record3>
<Record3>...</Record3>
<Record3>...</Record3>
<Record3>...</Record3>
<Record1>...</Record1>
<Record2>...</Record2>
<Record3>...</Record3>
<Record3>...</Record3>
<Record2>...</Record2>
<Record3>...</Record3>
<Record3>...</Record3>
<Record3>...</Record3>
<Record3>...</Record3>
</root>
All fields are on the same hierarchical level, but still I have to create this structure as output:
<root>
<article> -> Record1
<color> -> Record2
<size>...</size> -> Record3
<size>...</size> -> Record3
</color>
<color> -> Record2
<size>...</size> -> Record3
<size>...</size> -> Record3
<size>...</size> -> Record3
<size>...</size> -> Record3
</color>
</article>
<article> -> Record1
<color> -> Record2
<size>...</size> -> Record3
<size>...</size> -> Record3
</color>
<color> -> Record2
<size>...</size> -> Record3
<size>...</size> -> Record3
<size>...</size> -> Record3
<size>...</size> -> Record3
</color>
</article>
</root>
I've tried to iterate the nodes sequentially but for example the 'article' (=record1) node tag needs to remain unclosed while 'color' (=record2) nodes are processed. The same counts for processing 'size' (=record3) having 'color' unclosed, but that is not allowed by XSLT. My next idea was to call a template for every article, color and size level, but I don't know how to select for example all 'record3' nodes between the current 'record2' and the next article represented by 'record1'.
I've also got a limitation on the XSLT version because I need this transformation in BizTalk Server which only supports XSLT 1.0
Can someone push me in the right direction?
Here's one XSLT 1.0 option. I'm not sure what you wanted to do with the values of Record1 and Record2, so I put them in a val
attribute.
XML Input
<root>
<Record1>article1</Record1>
<Record2>color1</Record2>
<Record3>size1</Record3>
<Record3>size2</Record3>
<Record2>color2</Record2>
<Record3>size3</Record3>
<Record3>size4</Record3>
<Record3>size5</Record3>
<Record3>size6</Record3>
<Record1>article2</Record1>
<Record2>color3</Record2>
<Record3>size7</Record3>
<Record3>size8</Record3>
<Record2>color4</Record2>
<Record3>size9</Record3>
<Record3>size10</Record3>
<Record3>size11</Record3>
<Record3>size12</Record3>
</root>
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="Record1"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Record1">
<article val="{.}">
<xsl:apply-templates select="following-sibling::Record2[generate-id(preceding-sibling::Record1[1])=generate-id(current())]"/>
</article>
</xsl:template>
<xsl:template match="Record2">
<color val="{.}">
<xsl:apply-templates select="following-sibling::Record3[generate-id(preceding-sibling::Record2[1])=generate-id(current())]"/>
</color>
</xsl:template>
<xsl:template match="Record3">
<size>
<xsl:value-of select="."/>
</size>
</xsl:template>
</xsl:stylesheet>
XML Output
<root>
<article val="article1">
<color val="color1">
<size>size1</size>
<size>size2</size>
</color>
<color val="color2">
<size>size3</size>
<size>size4</size>
<size>size5</size>
<size>size6</size>
</color>
</article>
<article val="article2">
<color val="color3">
<size>size7</size>
<size>size8</size>
</color>
<color val="color4">
<size>size9</size>
<size>size10</size>
<size>size11</size>
<size>size12</size>
</color>
</article>
</root>