I am doing an XSLT stylesheet to transform from XML to XML, the layouts are very different and my case is causing a headache. I can only use XSLT 1.0 and I'm not finding the way to do it.
Input File
<?xml version="1.0"?>
<Root>
<ParentNode>
<Node>
<Id>1</Id>
<Date>2019-02-01</Date>
<ReferenceLine>1</ReferenceLine>
</Node>
<Node>
<Id>2</Id>
<Date>2019-02-01</Date>
<ReferenceLine>1</ReferenceLine>
</Node>
<Node>
<Id>3</Id>
<Date>2019-02-02</Date>
<ReferenceLine>2</ReferenceLine>
</Node>
</ParentNode>
</Root>
Output File
<Lines>
<Line>
<LineNum>1</LineNum>
<Node>1 - 2</Node>
</Line>
<Line>
<LineNum>2</LineNum>
<Node>3</Node>
</Line>
</Lines>
So what I need is to concatenate in the Output all the nodes that appears with reference to the line. While I can have multiple ocurrences of Node in the Input file, in the output file I can only have one ocurrence inside the Line node.
You can achieve with this the XSLT-1.0 method Muenchian Grouping. If you search on SO, you'll find a lot of examples. Applying this method, your stylesheet could look like this.
This stylesheet concatenates all the Id
s separated by a -
.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="nd" match="Node" use="ReferenceLine" />
<xsl:template match="/Root/ParentNode">
<Lines>
<xsl:for-each select="Node[generate-id() = generate-id(key('nd',ReferenceLine)[1])]">
<Line>
<LineNum><xsl:value-of select="Id" /></LineNum>
<Node>
<xsl:for-each select="key('nd',ReferenceLine)">
<xsl:sort order="ascending" select="Id" />
<xsl:value-of select="Id" />
<xsl:if test="position() != last()">
<xsl:text> - </xsl:text>
</xsl:if>
</xsl:for-each>
</Node>
</Line>
</xsl:for-each>
</Lines>
</xsl:template>
</xsl:stylesheet>
If you only want to get a range as the result, replace the inner for-each
with the following:
...
<xsl:for-each select="key('nd',ReferenceLine)">
<xsl:sort order="ascending" select="Id" />
<xsl:if test="position() = 1">
<xsl:value-of select="Id" />
</xsl:if>
<xsl:if test="position() = last() and position() != 1">
<xsl:value-of select="concat(' - ',Id)" />
</xsl:if>
</xsl:for-each>
...
But be aware that this range would ignore gaps and only use the lowest and the highest value.