Having issue when there are identical keys in nodeset formed - xslt-1.0

My input xml is as below, I am facing issue when there are multiple identical keys present in a nodeset, its not forming proper output structure. I am using keys to form the nodeset by using ref attribute. I am making use of these <SL id="L1S1"> and <pit ref="L1S1"> to form nodesets. Id attribute of SL node(i.e <SL id="L1S1">) is formed using "L1" in <L Id="L1">. Also ref attribute of pit node(i.e <pit ref="L1S1">) is formed using "L1" in <L Id="L1">.
This solution works fine if I do not have multiple pit nodes with same reference number. I am using xslt1.0, and I have issue when multiple identical keys are found in nodeset.
Input xml as below
<root>
<L Id="L1">
<test>ed</test>
<SL id="L1S1">
<check>
<AId>1</AId>
</check>
</SL>
<SL id="L1S2">
<check>
<AId>2</AId>
</check>
</SL>
</L>
<cp>
<current>
<Amt>20154.00</Amt>
</current>
<pi>
<pit ref="L1S1">
<value>123</value>
</pit>
<pit ref="L1S1">
<value>234</value>
</pit>
<pit ref="L1S2">
<value>1232</value>
</pit>
</pi>
</cp>
</root>
Expected output should be:
<root>
<L Id="L1">
<SL id="L1S1">
<check>
<AId>1</AId>
</check>
</SL>
<pit ref="L1S1">
<value>123</value>
</pit>
</L>
<L Id="L1">
<SL id="L1S1">
<check>
<AId>1</AId>
</check>
</SL>
<pit ref="L1S1">
<value>234</value>
</pit>
</L>
<L Id="L1">
<SL id="L1S2">
<check>
<AId>2</AId>
</check>
</SL>
<pit ref="L1S2">
<value>1232</value>
</pit>
</L>
</root>
<xsl:key name="pit-SL" match="pit" use="#ref" />
<xsl:key name="pit-L" match="pit" use="substring(#ref,1,2)" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="L[SL]">
<xsl:apply-templates select="SL"/>
</xsl:template>
<xsl:template match="SL">
<L>
<xsl:copy-of select="parent::L/#Id"/>
<xsl:copy>
<xsl:copy-of select="#id"/>
<xsl:apply-templates select="node()"/>
<xsl:copy-of select="key('pit-SL',#id)"/>
</xsl:copy>
</L>
</xsl:template>
<xsl:template match="L[not(SL)]">
<xsl:apply-templates select="key('pit-L',#Id)">
<xsl:with-param name="L" select="."/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="pit">
<xsl:param name="L"/>
<L>
<xsl:copy-of select="$L/#Id"/>
<xsl:copy-of select="."/>
</L>
</xsl:template>
<xsl:template match="cp"/>

AFAICT, you could do:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="pit" match="pit" use="#ref" />
<xsl:template match="/root">
<xsl:copy>
<xsl:for-each select="L">
<xsl:variable name="Id" select="#Id" />
<xsl:for-each select="SL">
<xsl:variable name="SL" select="." />
<xsl:for-each select="key('pit', #id)">
<L>
<xsl:copy-of select="$Id"/>
<xsl:copy-of select="$SL"/>
<xsl:copy-of select="."/>
</L>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

Not able to get details of current node in template match 'L'

This xslt works perfectly fine but i need to get additional details under L node in the output generated, I am having issue forming test, testone nodes. The solution i tried is <xsl:apply-templates select="#*|node()|SL[#id = $ref]" mode="Loutput"/> including node() in this line but output will be wrong in this case.
Input xml as below
<root>
<L Id="L1">
<test>ed</test>
<testone><testtwo>ed</testtwo></testone>
<SL id="L1S1">
<check>
<AId>1</AId>
</check>
<MD>
<UnitNumber>1</UnitNumber>
</MD>
</SL>
<SL id="L1S2">
<check>
<AId>2</AId>
</check>
<MD>
<UnitNumber>2</UnitNumber>
</MD>
</SL>
</L>
<cp>
<current>
<Amt>20154.00</Amt>
</current>
<pi>
<pit ref="L1S1">
<value>123</value>
</pit>
<pit ref="L1S2">
<value>1232</value>
</pit>
</pi>
</cp>
</root>
Expected output should be:
<root>
<L Id="L1">
<test>ed</test>
<testone><testtwo>ed</testtwo></testone>
<SL id="L1S1">
<check>
<AId>1</AId>
</check>
<MD>
<UnitNumber>1</UnitNumber>
</MD>
</SL>
<pit ref="L1S1">
<value>123</value>
</pit>
</L>
<L Id="L1">
<test>ed</test>
<testone><testtwo>ed</testtwo></testone>
<SL id="L1S2">
<check>
<AId>2</AId>
</check>
<MD>
<UnitNumber>2</UnitNumber>
</MD>
</SL>
<pit ref="L1S2">
<value>1232</value>
</pit>
</L>
</root>
This xslt works fine with all my scenarios.
<xsl:key name="SLkey" match="SL" use="#id"/>
<xsl:key name="pitKey" match="pit" use="generate-id()"/>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="#*|.//pit"/>
</xsl:copy>
</xsl:template>
<xsl:template match="pit">
<xsl:choose>
<!-- Is there an SL node match? -->
<xsl:when test="key('SLkey', #ref)[1]">
<xsl:apply-templates select="key('SLkey', #ref)[1]/.." mode="Loutput">
<xsl:with-param name="ref" select="#ref"/>
<xsl:with-param name="pitID" select="generate-id(.)"/>
</xsl:apply-templates>
</xsl:when>
<!-- Use the first L in the document. -->
<xsl:otherwise>
<xsl:apply-templates select="//L[1]" mode="Loutput">
<xsl:with-param name="ref" select="#ref"/>
<xsl:with-param name="pitID" select="generate-id(.)"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- ********************** -->
<!-- Loutput mode templates -->
<!-- ********************** -->
<xsl:template match="node()|#*" mode="Loutput">
<xsl:copy>
<xsl:apply-templates select="node()|#*" mode="Loutput"/>
</xsl:copy>
</xsl:template>
<xsl:template match="L" mode="Loutput">
<xsl:param name="ref"/>
<xsl:param name="pitID"/>
<xsl:copy>
<xsl:apply-templates select="#*|SL[#id = $ref]" mode="Loutput"/>
<xsl:apply-templates select="key('pitKey', $pitID)" mode="Loutput"/>
</xsl:copy>
</xsl:template>
This is one approach.
<xsl:key name="SLkey" match="SL" use="#id"/>
<xsl:key name="pitKey" match="pit" use="generate-id()"/>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="#*|.//pit"/>
</xsl:copy>
</xsl:template>
<xsl:template match="pit">
<xsl:choose>
<!-- Is there an SL node match? -->
<xsl:when test="key('SLkey', #ref)[1]">
<xsl:apply-templates select="key('SLkey', #ref)[1]/.." mode="Loutput">
<xsl:with-param name="ref" select="#ref"/>
<xsl:with-param name="pitID" select="generate-id(.)"/>
</xsl:apply-templates>
</xsl:when>
<!-- Use the first L in the document. -->
<xsl:otherwise>
<xsl:apply-templates select="//L[1]" mode="Loutput">
<xsl:with-param name="ref" select="#ref"/>
<xsl:with-param name="pitID" select="generate-id(.)"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- ********************** -->
<!-- Loutput mode templates -->
<!-- ********************** -->
<xsl:template match="node()|#*" mode="Loutput">
<xsl:copy>
<xsl:apply-templates select="node()|#*" mode="Loutput"/>
</xsl:copy>
</xsl:template>
<xsl:template match="L" mode="Loutput">
<xsl:param name="ref"/>
<xsl:param name="pitID"/>
<xsl:copy>
<xsl:apply-templates select="#*|node()[local-name() != 'SL']" mode="Loutput"/>
<xsl:apply-templates select="SL[#id = $ref]" mode="Loutput"/>
<xsl:apply-templates select="key('pitKey', $pitID)" mode="Loutput"/>
</xsl:copy>
</xsl:template>

Form multiple parent node for each of its descendants

Im quite new to xslt and breaking my head over this issue. Can anyone please help me with this?
Please find the input XML, I need to look for text L1 in id field of L node and search sublocation id and pit ref has text L1 and form new L1 node and arrange sublocation and pit under each L1 nodes.
<root>
<L Id="L1">
<test>ed</test>
<SubLocation id="L1S1">
<check>
<AId>1</AId>
</check>
<MD>
<UnitNumber>1</UnitNumber>
</MD>
</SubLocation>
<SubLocation id="L1S2">
<check>
<AId>2</AId>
</check>
<MD>
<UnitNumber>2</UnitNumber>
</MD>
</SubLocation>
</L>
<L Id="L2">
<test>ed</test>
<SubLocation id="L2S1">
<check>
<AId>1</AId>
</check>
<MD>
<UnitNumber>1</UnitNumber>
</MD>
</SubLocation>
<SubLocation id="L2S2">
<check>
<AId>2</AId>
</check>
<MD>
<UnitNumber>2</UnitNumber>
</MD>
</SubLocation>
</L>
<cp>
<current>
<Amt>20154.00</Amt>
</current>
<pi>
<pit ref="L1S1">
<value>1232</value>
</pit>
<pit ref="L1S2">
<value>12345</value>
</pit>
<pit ref="L1S3">
<value>12387</value>
</pit>
<pit ref="L2S1">
<value>1232</value>
</pit>
<pit ref="L2S2">
<value>12345</value>
</pit>
</pi>
</cp>
</root>
I need the o/p in following format
<root>
<L1>
<SubLocation id="L1S1">
<check>
<AId>1</AId>
</check>
<MD>
<UnitNumber>1</UnitNumber>
</MD>
</SubLocation>
<pit ref="L1S1">
<value>1232</value>
</pit>
</L1>
<L1>
<SubLocation id="L1S2">
<check>
<AId>2</AId>
</check>
<MD>
<UnitNumber>2</UnitNumber>
</MD>
</SubLocation>
<pit ref="L1S2">
<value>12345</value>
</pit>
</L1>
<L2>
<SubLocation id="L2S1">
<check>
<AId>1</AId>
</check>
<MD>
<UnitNumber>1</UnitNumber>
</MD>
</SubLocation>
<pit ref="L2S1">
<value>1232</value>
</pit>
</L2>
<L2>
<SubLocation id="L2S2">
<check>
<AId>2</AId>
</check>
<MD>
<UnitNumber>2</UnitNumber>
</MD>
</SubLocation>
<pit ref="L2S2">
<value>12345</value>
</pit>
</L2>
</root>
This is my xslt
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="cpbyid" match="pit" use="#ref"/>
<xsl:key name="slById" match="SubLocation" use="#id"/>
<xsl:key name="locbyId" match="L" use="#id"/>
<xsl:template match="#*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="L/*[#id]">
<L>
<xsl:value-of select="//L/#id"/>
<pifo>
<xsl:call-template name="subloc"></xsl:call-template>
<xsl:call-template name="identity"/>
</pifo>
</L>
</xsl:template>
<xsl:template match="pit/*[#ref]" name="subloc">
<inf>
<xsl:copy-of select="key('cpbyid', #id)"/>
<xsl:copy-of select="key('slById', #id)"/>
</inf>
</xsl:template>
</xsl:stylesheet>
I don't follow your description that well and I may be missing something. Is there a reason why you cannot do simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="pit" match="pit" use="#ref" />
<xsl:template match="/root">
<xsl:copy>
<xsl:for-each select="L/SubLocation">
<xsl:element name="{../#Id}">
<xsl:copy-of select="."/>
<xsl:copy-of select="key('pit', #id)"/>
</xsl:element>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Issue in forming node structure when multiple identical keys found in a nodeset

Scenario 1: L node having child node SL
Scenario 2: L node with no child node SL
Scenario 3: L node having child node SL and having multiple pit elements having attribute ref identical.
I need to form multiple L nodes if text "L1" () is found at other nodes like and . Id attribute of SL node(i.e ) is formed using "L1" in . Also ref attribute of pit node(i.e ) is formed using "L1" in , I need to check whether "L1" is present in either id attribute of SL or ref attribute of pit and form the desired output.
Input xml as below
<root>
<L Id="L1">
<test>ed</test>
<SL id="L1S1">
<check>
<AId>1</AId>
</check>
<MD>
<UnitNumber>1</UnitNumber>
</MD>
</SL>
<SL id="L1S2">
<check>
<AId>2</AId>
</check>
<MD>
<UnitNumber>2</UnitNumber>
</MD>
</SL>
</L>
<cp>
<current>
<Amt>20154.00</Amt>
</current>
<pi>
<pit ref="L1S1">
<value>123</value>
</pit>
<pit ref="L1S2">
<value>1232</value>
</pit>
</pi>
</cp>
</root>
Expected output should be:
<root>
<L Id="L1">
<SL id="L1S1">
<check>
<AId>1</AId>
</check>
<MD>
<UnitNumber>1</UnitNumber>
</MD>
</SL>
<pit ref="L1S1">
<value>123</value>
</pit>
</L>
<L Id="L1">
<SL id="L1S2">
<check>
<AId>2</AId>
</check>
<MD>
<UnitNumber>2</UnitNumber>
</MD>
</SL>
<pit ref="L1S2">
<value>1232</value>
</pit>
</L>
</root>
<root>
<L Id="L1">
<test>ed</test>
</L>
<cp>
<current>
<Amt>20154.00</Amt>
</current>
<pi>
<pit ref="L1S1">
<value>123</value>
</pit>
<pit ref="L1S2">
<value>1232</value>
</pit>
</pi>
</cp>
</root>
Expected output should be:
<root>
<L Id="L1">
<pit ref="L1S1">
<value>123</value>
</pit>
</L>
<L Id="L1">
<pit ref="L1S2">
<value>1232</value>
</pit>
</L>
</root>
Scenario 3:
Input xml as below
<root>
<L Id="L1">
<test>ed</test>
<SL id="L1S1">
<check>
<AId>1</AId>
</check>
<MD>
<UnitNumber>1</UnitNumber>
</MD>
</SL>
<SL id="L1S2">
<check>
<AId>2</AId>
</check>
<MD>
<UnitNumber>2</UnitNumber>
</MD>
</SL>
</L>
<cp>
<current>
<Amt>20154.00</Amt>
</current>
<pi>
<pit ref="L1S1">
<value>123</value>
</pit>
<pit ref="L1S1">
<value>234</value>
</pit>
<pit ref="L1S2">
<value>1232</value>
</pit>
<pit ref="L1S2">
<value>1</value>
</pit>
</pi>
</cp>
</root>
Expected output should be:
<root>
<L Id="L1">
<SL id="L1S1">
<check>
<AId>1</AId>
</check>
<MD>
<UnitNumber>1</UnitNumber>
</MD>
</SL>
<pit ref="L1S1">
<value>123</value>
</pit>
</L>
<L Id="L1">
<SL id="L1S1">
<check>
<AId>1</AId>
</check>
<MD>
<UnitNumber>1</UnitNumber>
</MD>
</SL>
<pit ref="L1S1">
<value>234</value>
</pit>
</L>
<L Id="L1">
<SL id="L1S2">
<check>
<AId>2</AId>
</check>
<MD>
<UnitNumber>2</UnitNumber>
</MD>
</SL>
<pit ref="L1S2">
<value>1232</value>
</pit>
</L>
<L Id="L1">
<SL id="L1S2">
<check>
<AId>2</AId>
</check>
<MD>
<UnitNumber>2</UnitNumber>
</MD>
</SL>
<pit ref="L1S2">
<value>1</value>
</pit>
</L>
</root>
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="pit-SL" match="pit" use="#ref" />
<xsl:key name="pit-L" match="pit" use="substring(#ref,1,2)" />
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="L[SL]">
<xsl:apply-templates select="SL"/>
</xsl:template>
<xsl:template match="SL">
<L>
<xsl:copy-of select="parent::L/#Id"/>
<xsl:copy>
<xsl:copy-of select="#id"/>
<xsl:apply-templates select="node()"/>
<xsl:copy-of select="key('pit-SL',#id)"/>
</xsl:copy>
</L>
</xsl:template>
<xsl:template match="L[not(SL)]">
<xsl:apply-templates select="key('pit-L',#Id)">
<xsl:with-param name="L" select="."/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="pit">
<xsl:param name="L"/>
<L>
<xsl:copy-of select="$L/#Id"/>
<xsl:copy-of select="."/>
</L>
</xsl:template>
<xsl:template match="cp"/>
</xsl:stylesheet>
Iam facing issue in 3rd scenario, when there are multiple identical pit ref keys.
Here is an approach.
<xsl:key name="SLkey" match="SL" use="#id"/>
<xsl:key name="pitKey" match="pit" use="generate-id()"/>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="#*|.//pit"/>
</xsl:copy>
</xsl:template>
<xsl:template match="pit">
<xsl:choose>
<!-- Is there an SL node match? -->
<xsl:when test="key('SLkey', #ref)[1]">
<xsl:apply-templates select="key('SLkey', #ref)[1]/.." mode="Loutput">
<xsl:with-param name="ref" select="#ref"/>
<xsl:with-param name="pitID" select="generate-id(.)"/>
</xsl:apply-templates>
</xsl:when>
<!-- Use the first L in the document. -->
<xsl:otherwise>
<xsl:apply-templates select="//L[1]" mode="Loutput">
<xsl:with-param name="ref" select="#ref"/>
<xsl:with-param name="pitID" select="generate-id(.)"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- ********************** -->
<!-- Loutput mode templates -->
<!-- ********************** -->
<xsl:template match="node()|#*" mode="Loutput">
<xsl:copy>
<xsl:apply-templates select="node()|#*" mode="Loutput"/>
</xsl:copy>
</xsl:template>
<xsl:template match="L" mode="Loutput">
<xsl:param name="ref"/>
<xsl:param name="pitID"/>
<xsl:copy>
<xsl:apply-templates select="#*|SL[#id = $ref]" mode="Loutput"/>
<xsl:apply-templates select="key('pitKey', $pitID)" mode="Loutput"/>
</xsl:copy>
</xsl:template>

XSLT grouping and transformation

I have to transform XML file by grouping identical nodes and putting them into identical parent node.
The example file looks like this:
<cars>
<car>
<seller>A</seller>
<make>Ford</make>
<model>Mondeo</model>
<type>Hatchback</type>
</car>
<car>
<seller>A</seller>
<make>Ford</make>
<model>Mondeo</model>
<type>Sedan</type>
</car>
<car>
<seller>A</seller>
<make>Ford</make>
<model>Mondeo</model>
<type>Station wagon</type>
</car>
<car>
<seller>A</seller>
<make>Citroen</make>
<model>C5</model>
<type>Sedan</type>
</car>
<car>
<seller>A</seller>
<make>Citroen</make>
<model>C4</model>
<type>Hatchback</type>
</car>
<car>
<seller>A</seller>
<make>Citroen</make>
<model>C3</model>
<type>Hatchback</type>
</car>
<car>
<seller>A</seller>
<make>Opel</make>
<model>Corsa</model>
<type>Hatchback</type>
</car>
<car>
<seller>A</seller>
<make>Opel</make>
<model>Vectra</model>
<type>Sedan</type>
</car>
<car>
<seller>A</seller>
<make>Opel</make>
<model>Vectra</model>
<type>Station wagon</type>
</car>
</cars>
This file can include much more makes, models and types in a different order but there is always only one seller. I have to get it looking like that:
<cars>
<seller>A</seller>
<make name="Ford">
<model name="Mondeo" type="Hatchback"/>
<model name="Mondeo" type="Sedan"/>
<model name="Mondeo" type="Station wagon"/>
</make>
<make name="Citroen">
<model name="C5" type="Sedan"/>
<model name="C4" type="Hatchback"/>
<model name="C3" type="Hatchback"/>
</make>
<make name="Opel">
<model name="Corsa" type="Hatchback"/>
<model name="Vectra" type="Sedan"/>
<model name="Vectra" type="Station wagon"/>
</make>
</cars>
Before I tried that by checking if present model is the same as preceding-sibling but I couldn't to this to be "elastic" and could serve much more types of each model of each make.
I know how to transform one car node into desired format :
<?xml version="1.0" encoding="UTF-8"?>
<localEntry key="CarsXSL" xmlns="http://ws.apache.org/ns/synapse">
<xsl:stylesheet version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:element name="cars">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="cars">
<xsl:element name="seller">
<xsl:value-of select="/cars/car[1]/seller/text()"/>
</xsl:element>
<xsl:element name="make">
<xsl:attribute name="name">
<xsl:value-of select="/cars/car/make/text()"/>
</xsl:attribute>
<xsl:element name="model">
<xsl:attribute name="name">
<xsl:value-of select="/cars/car/make/model/text()"/>
</xsl:attribute>
<xsl:attribute name="type">
<xsl:value-of select="/cars/car/make/model/type/text()"/>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
</localEntry>
but I don't know how to group it by make and model and then how to put them into indentical parent node.
Is it even possible?
Try this: (updated as suggested by #michael-hor257k)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:key name="card" match="car" use="make"/>
<xsl:key name="modeld" match="car" use="model"/>
<xsl:key name="types" match="car" use="concat(make, '_', model)"/>
<xsl:output indent="yes"/>
<xsl:template match="cars">
<xsl:copy>
<xsl:for-each select="car[generate-id() = generate-id(key('card', make)[1])]">
<xsl:copy>
<make name="{make}">
<xsl:for-each select="key('card', make)[generate-id() =
generate-id(key('modeld', model)[1])]">
<model name="{model}">
<xsl:copy-of select="key('types', concat(make, '_', model))/type"/>
</model>
</xsl:for-each>
</make>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
See Transformation at https://xsltfiddle.liberty-development.net/6q1S8AD/1
For your Updated Question
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:key name="card" match="car" use="make"/>
<xsl:key name="types" match="car" use="concat(make, '_', model, '_', type)"/>
<xsl:output indent="yes"/>
<xsl:template match="cars">
<xsl:copy>
<seller>
<xsl:value-of select="//seller[1]"/>
</seller>
<car>
<xsl:for-each select="//car[generate-id() = generate-id(key('card', make)[1])]">
<make name="{make}">
<xsl:for-each select="key('card', make)[generate-id() = generate-id(key('types', concat(make, '_', model, '_', type))[1])]">
<model name="{model}" type="{type}"/>
</xsl:for-each>
</make>
</xsl:for-each>
</car>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
See Transformation at https://xsltfiddle.liberty-development.net/6q1S8AD/2

Accessing current node in predicate with xsl:for-each

I'm stuck finding out how I can access the current node whilst iterating over a collection of nodes with xsL:for-each. This is my XML:
<events>
<event>
<date>
<year>2012</year>
<month>July</month>
</date>
<description>...</description>
</event>
<!-- more events -->
</events>
What I try to achieve is an HTML-representation like this:
<h2>2012</h2>
<dl>
<dt>July</dt>
<dd>One of these for every event with year=2012 and month=July</dd>
<dt>August</dt>
<!-- ... -->
</dl>
<h2>2013</h2>
<!-- ... -->
I'm using an XPath-expression to get all distinct years and then iterate over them calling a template called year with parameters $year and $events. Getting the value for $year is easy, but I'm struggling finding the right events. The following won't work, probably because . in the second predicate refers to the event being tested for the predicate. But how to access the year in there?
<xsl:template match="events">
<xsl:for-each select="event[not(date/year=preceding-sibling::event/date/year)]/date/year">
<xsl:call-template name="year">
<xsl:with-param name="year" select="." />
<xsl:with-param name="events" select="event[date/year=.]" />
</xsl:call-template>
</xsl:for-each>
</xsl:template>
Many thanks in advance!
PS: Must work with XPath and XSLT 1.0.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kYear" match="year" use="."/>
<xsl:key name="kEventByMonthYear" match="event"
use="string(date)"/>
<xsl:key name="kMonthByMonthYear" match="month"
use="string(..)"/>
<xsl:key name="kMonthByYear" match="month"
use="../year"/>
<xsl:template match="/*">
<xsl:for-each select=
"*/date/year
[generate-id() = generate-id(key('kYear', .)[1])]
">
<h2><xsl:value-of select="."/></h2>
<dl>
<xsl:apply-templates select=
"key('kMonthByYear', current())
[generate-id()
=
generate-id(key('kMonthByMonthYear',
string(..)
)[1]
)
]"/>
</dl>
</xsl:for-each>
</xsl:template>
<xsl:template match="month">
<dt><xsl:value-of select="."/></dt>
<xsl:for-each select=
"key('kEventByMonthYear', string(current()/..))">
<dd><xsl:value-of select="description"/></dd>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document:
<events>
<event>
<date>
<year>2012</year>
<month>January</month>
</date>
<description>Jan1</description>
</event>
<event>
<date>
<year>2012</year>
<month>January</month>
</date>
<description>Jan2</description>
</event>
<event>
<date>
<year>2012</year>
<month>March</month>
</date>
<description>Mar1</description>
</event>
<event>
<date>
<year>2012</year>
<month>March</month>
</date>
<description>Mar2</description>
</event>
<event>
<date>
<year>2012</year>
<month>March</month>
</date>
<description>Mar3</description>
</event>
<event>
<date>
<year>2012</year>
<month>July</month>
</date>
<description>Jul1</description>
</event>
<event>
<date>
<year>2012</year>
<month>July</month>
</date>
<description>Jul2</description>
</event>
<event>
<date>
<year>2012</year>
<month>September</month>
</date>
<description>Sep1</description>
</event>
<event>
<date>
<year>2012</year>
<month>October</month>
</date>
<description>Oct1</description>
</event>
<event>
<date>
<year>2012</year>
<month>October</month>
</date>
<description>Oct2</description>
</event>
<event>
<date>
<year>2012</year>
<month>November</month>
</date>
<description>Nov1</description>
</event>
<event>
<date>
<year>2012</year>
<month>November</month>
</date>
<description>Nov2</description>
</event>
<event>
<date>
<year>2012</year>
<month>December</month>
</date>
<description>Dec1</description>
</event>
<event>
<date>
<year>2012</year>
<month>December</month>
</date>
<description>Dec2</description>
</event>
<event>
<date>
<year>2012</year>
<month>December</month>
</date>
<description>Dec3</description>
</event>
<event>
<date>
<year>2013</year>
<month>January</month>
</date>
<description>Jan1</description>
</event>
<event>
<date>
<year>2013</year>
<month>January</month>
</date>
<description>Jan2</description>
</event>
</events>
produces the wanted, correct result:
<h2>2012</h2>
<dl>
<dt>January</dt>
<dd>Jan1</dd>
<dd>Jan2</dd>
<dt>March</dt>
<dd>Mar1</dd>
<dd>Mar2</dd>
<dd>Mar3</dd>
<dt>July</dt>
<dd>Jul1</dd>
<dd>Jul2</dd>
<dt>September</dt>
<dd>Sep1</dd>
<dt>October</dt>
<dd>Oct1</dd>
<dd>Oct2</dd>
<dt>November</dt>
<dd>Nov1</dd>
<dd>Nov2</dd>
<dt>December</dt>
<dd>Dec1</dd>
<dd>Dec2</dd>
<dd>Dec3</dd>
</dl>
<h2>2013</h2>
<dl>
<dt>January</dt>
<dd>Jan1</dd>
<dd>Jan2</dd>
</dl>
Explanation:
Proper use of the Muenchian method for grouping -- with composite grouping keys.
#Dimitre Novatchev's answer is the more elaborate one, but I found another possibility I'd like to share. It doesn't depend on keys and therefore is a bit more "newbie-friendly". On the other hand it too doesn't solve the original "accessing current node of an iteration" problem:
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="events">
<xsl:for-each select="event[not(date/year=preceding-sibling::event/date/year)]/date/year">
<xsl:call-template name="year">
<xsl:with-param name="year" select="." />
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="year">
<xsl:param name="year" />
<h2><xsl:value-of select="$year" /></h2>
<dl class="dl-horizontal">
<xsl:for-each select="//event[date/year=$year][not(date/month=preceding-sibling::event[date/year=$year]/date/month)]/date/month">
<xsl:call-template name="month">
<xsl:with-param name="month" select="." />
<xsl:with-param name="year" select="$year" />
</xsl:call-template>
</xsl:for-each>
</dl>
</xsl:template>
<xsl:template name="month">
<xsl:param name="month" />
<xsl:param name="year" />
<dt><xsl:value-of select="$month" /></dt>
<xsl:for-each select="//event[date/year=$year][date/month=$month]">
<xsl:call-template name="event">
<xsl:with-param name="event" select="." />
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="event">
<xsl:param name="event" />
<dd><xsl:copy-of select="description/node()" /></dd>
</xsl:template>
</xsl:transform>