Reputation: 1291
I want to merge the position elements and create a new element called travel after comparing the type attribute. First type="from" has to merge with first type="to" found and should continue with the rest.
INPUT
<?xml version="1.0" encoding="UTF-8"?>
<JOB age="0" priority="N">
<container ID="TSTU2345678" TP="4200"/>
<container ID="TSTU3456789" TP="4200"/>
<position refID="Y.Test:AA.01.01.1" name="AA0101.1" type="from"/>
<position refID="Y.Test:AA.01.02.1" name="AA0102.1" type="from"/>
<position refID="Y.Test:AA.02.02.1" name="AA0202.1" type="to"/>
<position refID="Y.Test:AA.02.03.1" name="AA0203.1" type="to"/>
</JOB>
Required OUTPUT
<JOB age="0" priority="N">
<Travel FromrefID="Y.Test:AA.01.01.1" Fromname="AA0101.1" TorefID="Y.Test:AA.02.02.1" Toname="AA0202.1"/>
<Travel FromrefID="Y.Test:AA.01.02.1" Fromname="AA0102.1" TorefID="Y.Test:AA.02.03.1" Toname="AA0203.1"/>
</JOB>
What I have tried so far. It adds all To types into from element. I am not sure how to select the first and mark it as used. Also how do I change the attribute names. Please help.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="position" match="job/position" use="@type" />
<xsl:template match="job">
<xsl:copy>
<xsl:apply-templates select="@*" />
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<JOB>
<xsl:apply-templates select="job/@*" />
<xsl:for-each select="key('position','from')">
<Travel>
<xsl:apply-templates select="@*" />
<xsl:for-each select="key('position','to')">
<To>
<xsl:apply-templates select="@*" />
</To>
</xsl:for-each>
</Travel>
</xsl:for-each>
</JOB>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Thanks.
Upvotes: 2
Views: 327
Reputation: 66714
An alternative XSLT 1.0 solution
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|position[@type='from']"/>
</xsl:copy>
</xsl:template>
<xsl:template match="position[@type='from']">
<xsl:variable name="fromPos" select="position()"/>
<Travel FromRefId="{@refID}" Fromname="{@name}">
<xsl:apply-templates select="position[@type='to'][position()=$fromPos]"/>
</Travel>
</xsl:template>
<xsl:template match="position[@type='to']">
<xsl:attribute name="TorefID">
<xsl:value-of select="@refID"/>
</xsl:attribute>
<xsl:attribute name="Toname">
<xsl:value-of select="@name"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1
Reputation: 116957
I'd suggest you look at it this way:
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:key name="to" match="position[@type='to']" use="count(preceding-sibling::position[@type='to'])" />
<xsl:template match="/JOB">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:for-each select="position[@type='from']">
<xsl:variable name="to" select="key('to', position() - 1)" />
<Travel FromrefID="{@refID}" Fromname="{@name}" TorefID="{$to/@refID}" Toname="{$to/@name}"/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note that we are assuming here that there are equal number of "from" and "to" positions (or at lest that the number of "from" positions is not less than the number of "to" positions).
Upvotes: 3