Source XML :
<AWARD>
<FIELD1>XYZ</FIELD1>
<AWARDLINE>
<ALINENUM>1</ALINENUM>
<SHIPMENT>
<SHIPLINENUM>1.1</SHIPLINENUM>
<ACCOUNT>
<ACCOUNTLINENUM>1.1.1</ACCOUNTLINENUM>
<ASSOCIATEDREQ>
<AREQNUM>1.1.1.1</AREQNUM>
</ASSOCIATEDREQ>
</ACCOUNT>
</SHIPMENT>
</AWARDLINE>
<AWARDLINE>
<ALINENUM>2</ALINENUM>
</AWARDLINE>
</AWARD>
Apart from AWARD all the child nodes "AWARDLINE" , "SHIPMENT", "ACCOUNT", "ASSOCIATEDREQ" may exist or may not exist.
As seen above, AWARDLINE 2 does not have SHIPMENT/ACCOUNT/ASSOCIATEDREQ nodes.
Desired Target XML
<AWARD>
<FIELD1>XYZ</FIELD1>
<AWARDLINE>
<ALINENUM>1</ALINENUM>
<SHIPMENT>
<SHIPLINENUM>1.1</SHIPLINENUM>
<ACCOUNT>
<ACCOUNTLINENUM>1.1.1</ACCOUNTLINENUM>
<ASSOCIATEDREQ>
<AREQNUM>1.1.1.1</AREQNUM>
</ASSOCIATEDREQ>
</ACCOUNT>
</SHIPMENT>
</AWARDLINE>
<AWARDLINE>
<ALINENUM>2</ALINENUM>
<SHIPMENT>
<ACCOUNT>
</ASSOCIATEDREQ>
</ACCOUNT>
</SHIPMENT>
</AWARDLINE>
</AWARD>
You can use this XSLT-1.0 which does a replacement of all relevant nodes if they are present. It does selectively reconstruct the structure of the AWARDLINE element and works in combination with the identity template.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- identity template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<!-- reconstruction of the AWARDLINE element structure -->
<xsl:template match="AWARDLINE">
<xsl:copy>
<xsl:copy-of select="*[not(self::SHIPMENT)]" />
<SHIPMENT>
<xsl:copy-of select="SHIPMENT/*[not(self::ACCOUNT)]" />
<ACCOUNT>
<xsl:copy-of select="SHIPMENT/ACCOUNT/*[not(self::ASSOCIATEDREQ)]" />
<ASSOCIATEDREQ>
<xsl:copy-of select="SHIPMENT/ACCOUNT/ASSOCIATEDREQ/*" />
</ASSOCIATEDREQ>
</ACCOUNT>
</SHIPMENT>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Another possibility is using templates. This can process more than one SHIPMENT element and creates the empty structure if no other elements are present.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- identity template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<!-- reconstruction of the AWARDLINE element structure -->
<xsl:template match="AWARDLINE">
<xsl:copy>
<xsl:copy-of select="*[not(self::SHIPMENT)]|#*" />
<xsl:apply-templates select="SHIPMENT" />
</xsl:copy>
</xsl:template>
<xsl:template match="AWARDLINE[not(SHIPMENT)]">
<xsl:copy>
<xsl:copy-of select="*|#*" />
<SHIPMENT>
<ACCOUNT>
<ASSOCIATEDREQ />
</ACCOUNT>
</SHIPMENT>
</xsl:copy>
</xsl:template>
<xsl:template match="SHIPMENT">
<xsl:copy>
<xsl:copy-of select="*[not(self::ACCOUNT)]|#*" />
<xsl:apply-templates select="ACCOUNT" />
</xsl:copy>
</xsl:template>
<xsl:template match="SHIPMENT[not(ACCOUNT)]">
<xsl:copy>
<xsl:copy-of select="*|#*" />
<ACCOUNT>
<ASSOCIATEDREQ />
</ACCOUNT>
</xsl:copy>
</xsl:template>
<xsl:template match="ACCOUNT">
<xsl:copy>
<xsl:copy-of select="*[not(self::ASSOCIATEDREQ)]|#*" />
<xsl:apply-templates select="ASSOCIATEDREQ" />
</xsl:copy>
</xsl:template>
<xsl:template match="ACCOUNT[not(ASSOCIATEDREQ)]">
<xsl:copy>
<xsl:copy-of select="*|#*" />
<ASSOCIATEDREQ />
</xsl:copy>
</xsl:template>
<xsl:template match="ASSOCIATEDREQ">
<xsl:copy>
<xsl:copy-of select="*|#*" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Related
I'm struggling to get this abomination called XSLT to work. I need to get an EXACT attribute at EXACT path, pass its original value to a template and rewrite this value with the result from the template.
I'm having a file like this:
<?xml version="1.0" encoding="windows-1251"?>
<File>
<Document ReportYear="17">
...
...
</Document>
</File>
So I made an XSLT like this:
<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" encoding="windows-1251" indent="yes" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template name="formatYear">
<xsl:param name="year" />
<xsl:value-of select="$year + 2000" />
</xsl:template>
<xsl:template match="File/Document">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:attribute name="ReportYear">
<xsl:call-template name="formatYear">
<xsl:with-param name="year" select="#ReportYear" />
</xsl:call-template>
</xsl:attribute>
</xsl:copy>
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
This works fine except it closes the <Document> tag immediately and places its content immediately after itself.
Also, can I address the ReportYear attribute value without repeating it twice? I tried current() but it didn't work.
If you're closing <xsl:copy> before applying templates to the remainder of the content of <Document>, then of course <Document> will be closed before the remainder of the content of <Document> appears in the output.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" encoding="windows-1251" indent="yes" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Document">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:attribute name="ReportYear">
<xsl:value-of select="#ReportYear + 2000" />
</xsl:attribute>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
outputs
<?xml version="1.0" encoding="windows-1251"?>
<File>
<Document ReportYear="2017">
...
...
</Document>
</File>
I don't think an extra template just for adding 2000 to #ReportYear is necessary. But if you must, you can streamline the whole thing like so
<xsl:template name="formatYear">
<xsl:param name="year" select="#ReportYear" /> <!-- you can define a default value -->
<xsl:value-of select="$year + 2000" />
</xsl:template>
and
<xsl:attribute name="ReportYear">
<xsl:call-template name="formatYear" /> <!-- ...and can use it implicitly here -->
</xsl:attribute>
If you need to process the contents of the Document element with apply-templates and want to keep the result of the applied templates as the children then you need to move the apply-templates inside of the copy:
<xsl:template match="File/Document">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="ReportYear">
<xsl:call-template name="formatYear">
<xsl:with-param name="year" select="#ReportYear"/>
</xsl:call-template>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
Not sure why you haven't simply used
<xsl:template match="File/Document/#ReportYear">
<xsl:attribute name="{name()}">
<xsl:value-of select=". + 2000"/>
</xsl:attribute>
</xsl:template>
together with the identity transformation template.
Hi I have been trying to replace values in a certain tag with sequential numbers, I used position function, but it did not work.
Input xml:
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<Header>
<User id="swarnai" />
<Request-Id id="592149819" />
<Type name="Request" />
<Application-Source version="8.1.1.10" name="Siebel" />
<Application-Destination version="1.2.2" name="EVA" />
<Outgo-Timestamp time="11:40:59" date="2015-08-04" />
<DealerCode>82536</DealerCode>
<Market>00000</Market>
</Header>
<Content>
<ClaimContext>
<ClaimControl>
<ClaimEntryFlag>3</ClaimEntryFlag>
<ClaimSaveFlag>1</ClaimSaveFlag>
</ClaimControl>
<Claim>
<DealerClaimNumber>1091871</DealerClaimNumber>
<WHC>WDD</WHC>
<FIN>2120026L020301</FIN>
<RegistrationNumber>JH07E2786</RegistrationNumber>
<FirstRegistrationDate>2012-11-29</FirstRegistrationDate>
<Mileage>14317</Mileage>
<MileageIndicator>0</MileageIndicator>
<RepairDate>2013-12-03</RepairDate>
<RegularlyMaintained>true</RegularlyMaintained>
<NoFirstRegDateInd>false</NoFirstRegDateInd>
<ClaimCurrencyId>EUR</ClaimCurrencyId>
<Taxi>false</Taxi>
<DamagePosition>
<DamageSeqNumber>1</DamageSeqNumber>
<WarrantyType>0</WarrantyType>
<DamageCode>0121504</DamageCode>
<OperationPosition>
<SeqNumber>1</SeqNumber>
<Opcode>02770501</Opcode>
<WorkingUnits>18</WorkingUnits>
<MainOperationCode>true</MainOperationCode>
<OperationText>OPERATIONS: FITTING FOR COOLANT CONNECTION TO CYLINDER</OperationText>
<PriceGroup>01</PriceGroup>
</OperationPosition>
</DamagePosition>
<DamagePosition>
<DamageSeqNumber>1</DamageSeqNumber>
<WarrantyType>0</WarrantyType>
<DamageCode>0121504</DamageCode>
<PartPosition>
<SeqNumber>1</SeqNumber>
<Quantity>10</Quantity>
<PartNumber>A6512001056</PartNumber>
<DamageCausingPart>true</DamageCausingPart>
<RetailPriceAmount>1499</RetailPriceAmount>
<Express>false</Express>
</PartPosition>
</DamagePosition>
<DamagePosition>
<DamageSeqNumber>1</DamageSeqNumber>
<WarrantyType>0</WarrantyType>
<DamageCode>0121504</DamageCode>
<PartPosition>
<SeqNumber>1</SeqNumber>
<Quantity>20</Quantity>
<PartNumber>A0009890825 10</PartNumber>
<DamageCausingPart>false</DamageCausingPart>
<RetailPriceAmount>1319</RetailPriceAmount>
<Express>false</Express>
</PartPosition>
</DamagePosition>
</Claim>
</ClaimContext>
</Content>
</Envelope>
In this xml within PartPosition/SeqNumber tag I want to replace or generate sequence of numbers in SeqNumber tag.
I tried below xslt:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="no"/>
<xsl:key name="Damage_Group" match="/Envelope/Content/ClaimContext/Claim/DamagePosition" use="DamageCode" />
<xsl:template match="Claim">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
<xsl:for-each select="DamagePosition[count(. | key('Damage_Group', DamageCode)[1]) = 1]">
<xsl:sort select="DamageCode" />
<DamagePosition>
<DamageSeqNumber>
<xsl:value-of select="position()" />
</DamageSeqNumber>
<DamageCode>
<xsl:value-of select="DamageCode" />
</DamageCode>
<WarrantyType><xsl:value-of select="WarrantyType" /></WarrantyType>
<xsl:for-each select="key('Damage_Group', DamageCode)">
<xsl:copy-of select="current()/PartPosition"/>
<xsl:copy-of select="current()/OperationPosition"/>
<xsl:copy-of select="current()/SubletPosition"/>
</xsl:for-each>
</DamagePosition>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="DamagePosition">
</xsl:template>
</xsl:stylesheet>
but it is not giving me the desired output as below.
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<Header>
<User id="swarnai" />
<Request-Id id="592149819" />
<Type name="Request" />
<Application-Source version="8.1.1.10" name="Siebel" />
<Application-Destination version="1.2.2" name="EVA" />
<Outgo-Timestamp time="12:15:47" date="2015-08-04" />
<DealerCode>82536</DealerCode>
<Market>00000</Market>
</Header>
<Content>
<ClaimContext>
<ClaimControl>
<ClaimEntryFlag>3</ClaimEntryFlag>
<ClaimSaveFlag>1</ClaimSaveFlag>
</ClaimControl>
<Claim>
<DealerClaimNumber>1091871</DealerClaimNumber>
<WHC>WDD</WHC>
<FIN>2120026L020301</FIN>
<RegistrationNumber>JH07E2786</RegistrationNumber>
<FirstRegistrationDate>2012-11-29</FirstRegistrationDate>
<Mileage>14317</Mileage>
<MileageIndicator>0</MileageIndicator>
<RepairDate>2013-12-03</RepairDate>
<RegularlyMaintained>true</RegularlyMaintained>
<NoFirstRegDateInd>false</NoFirstRegDateInd>
<ClaimCurrencyId>EUR</ClaimCurrencyId>
<Taxi>false</Taxi>
<DamagePosition>
<DamageSeqNumber>1</DamageSeqNumber>
<DamageCode>0121504</DamageCode>
<WarrantyType>0</WarrantyType>
<OperationPosition>
<SeqNumber>1</SeqNumber>
<Opcode>02770501</Opcode>
<WorkingUnits>18</WorkingUnits>
<MainOperationCode>true</MainOperationCode>
<OperationText>OPERATIONS: FITTING FOR COOLANT CONNECTION TO CYLINDER</OperationText>
<PriceGroup>01</PriceGroup>
</OperationPosition>
<PartPosition>
<SeqNumber>1</SeqNumber>
<Quantity>10</Quantity>
<PartNumber>A6512001056</PartNumber>
<DamageCausingPart>true</DamageCausingPart>
<RetailPriceAmount>1499</RetailPriceAmount>
<Express>false</Express>
</PartPosition>
<PartPosition>
<SeqNumber>2</SeqNumber>
<Quantity>20</Quantity>
<PartNumber>A0009890825 10</PartNumber>
<DamageCausingPart>false</DamageCausingPart>
<RetailPriceAmount>1319</RetailPriceAmount>
<Express>false</Express>
</PartPosition>
</DamagePosition>
</Claim>
</ClaimContext>
</Content>
</Envelope>
Any help is much appreciated.
Thanks
Firstly, instead of doing this, which copies the elements without further changes to it (or its descendants)
<xsl:copy-of select="current()/PartPosition"/>
<xsl:copy-of select="current()/OperationPosition"/>
<xsl:copy-of select="current()/SubletPosition"/>
You should you template matching instead, to allow you to write a template later on to change the sequence number
<xsl:apply-templates select="PartPosition"/>
<xsl:apply-templates select="OperationPosition"/>
<xsl:apply-templates select="SubletPosition"/>
Now, what you could do, is write a template that matches the various Sequence numbers and counts the preceding siblings for the same DamageCode. Something like this
<xsl:template match="PartPosition/SeqNumber">
<SeqNumber>
<xsl:variable name="DamageCode" select="../../DamageCode" />
<xsl:value-of select="count(../../preceding-sibling::*[PartPosition][DamageCode = $DamageCode]) + 1" />
</SeqNumber>
</xsl:template>
You could repeat the template for OperationPosition and SubletPosition too, although this would be quite repetitive. Alternatively, have a single generic template to match all three:
<xsl:template match="SeqNumber">
<SeqNumber>
<xsl:variable name="DamageCode" select="../../DamageCode" />
<xsl:variable name="Parent" select="name(..)" />
<xsl:value-of select="count(../../preceding-sibling::*[*[name() = $Parent]][DamageCode = $DamageCode]) + 1" />
</SeqNumber>
</xsl:template>
However, this is not necessarily that efficient, as it is not utilizing the key and so would have to check back through all preceding siblings to find a match.
Another solution would be to pass the position of the current DamagePosition in a parameter, and then use that in checking the key.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:key name="Damage_Group"
match="/Envelope/Content/ClaimContext/Claim/DamagePosition"
use="DamageCode"/>
<xsl:template match="Claim">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:for-each select="DamagePosition[count(. | key('Damage_Group', DamageCode)[1]) = 1]">
<xsl:sort select="DamageCode"/>
<DamagePosition>
<DamageSeqNumber>
<xsl:value-of select="position()"/>
</DamageSeqNumber>
<DamageCode>
<xsl:value-of select="DamageCode"/>
</DamageCode>
<WarrantyType>
<xsl:value-of select="WarrantyType"/>
</WarrantyType>
<xsl:for-each select="key('Damage_Group', DamageCode)">
<xsl:apply-templates select="PartPosition">
<xsl:with-param name="Position" select="position()" />
</xsl:apply-templates>
<xsl:apply-templates select="OperationPosition">
<xsl:with-param name="Position" select="position()" />
</xsl:apply-templates>
<xsl:apply-templates select="SubletPosition">
<xsl:with-param name="Position" select="position()" />
</xsl:apply-templates>
</xsl:for-each>
</DamagePosition>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="DamagePosition/*">
<xsl:param name="Position" />
<xsl:copy>
<xsl:apply-templates select="#*|node()">
<xsl:with-param name="Position" select="$Position" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="SeqNumber">
<xsl:param name="Position" />
<SeqNumber>
<xsl:value-of select="count(key('Damage_Group', ../../DamageCode)[position() <= $Position][*[name() = name(current()/..)]])" />
</SeqNumber>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="DamagePosition" />
</xsl:stylesheet>
I need to modify a XML using XSLT 1.0. Basically, I need to copy the attribute (name and value) to one child.
This is the xml:
<parent id="3450">
<son1>
<name>Malcom</name>
<age>15</age>
<description>This is the middle son</description>
</son1>
<son2>
<name>Francis</name>
<age>19</age>
<description>This is the oldest son</description>
</son2>
<son3>
<name>Dewey</name>
<age>9</age>
<description>This is the youngest son</description>
</son3>
</parent>
This should be the result:
<parent id="3450">
<son1 id="3450">
<name>Malcom</name>
<age>15</age>
<description>This is the middle son</description>
</son1>
</parent>
This is the XSLT that I'm working with:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="parent/son1">
<xsl:copy>
<xsl:attribute name="id"><xsl:value-of select="../#id"/></xsl:attribute>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="parent/son2" />
<xsl:template match="parent/son3" />
The XSLT seems to be working, but my question is: Is this the right way to do it?
Thanks.
I would change
<xsl:template match="parent/son1">
<xsl:copy>
<xsl:attribute name="id"><xsl:value-of select="../#id"/></xsl:attribute>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
to
<xsl:template match="parent/son1">
<xsl:copy>
<xsl:apply-templates select="../#id | #* | node()"/>
</xsl:copy>
</xsl:template>
For each child node, I want to duplicate my parent node so that the resulting xml, contains only one child for the parent node with the other nodes being the same.
Here is a sample input
<a>
<a1>header1</a1>
<a2>header2</a2>
<a3>
<a31>
<a311>line_1</a311>
<a311>line_2</a311>
</a31>
<a32>5o$</a32>
<a33>Add</a33>
</a3>
<a4>account_holder</a4>
</a>
What I want to do is - repeat a3 for as many times as the node a311 comes. Rest all nodes are retained
Output
<a>
<a1>header1</a1>
<a2>header2</a2>
<a3>
<a31>
<a311>line_1</a311>
</a31>
<a32>5o$</a32>
<a33>Add</a33>
</a3>
<a3>
<a31>
<a311>line_2</a311>
</a31>
<a32>5o$</a32>
<a33>Add</a33>
</a3>
<a4>account_holder</a4>
</a>
More semantic with "tunnel param" pattern, this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()" name="identity">
<xsl:param name="pCurrent"/>
<xsl:copy>
<xsl:apply-templates select="#*|node()">
<xsl:with-param name="pCurrent" select="$pCurrent"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="a3">
<xsl:variable name="vCurrent" select="."/>
<xsl:variable name="vDescendants" select=".//a311"/>
<xsl:for-each select="$vDescendants|$vCurrent[not($vDescendants)]">
<xsl:variable name="vDescendant" select="."/>
<xsl:for-each select="$vCurrent">
<xsl:call-template name="identity">
<xsl:with-param name="pCurrent" select="$vDescendant"/>
</xsl:call-template>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template match="a311">
<xsl:param name="pCurrent"/>
<xsl:if test="generate-id()=generate-id($pCurrent)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Output:
<a>
<a1>header1</a1>
<a2>header2</a2>
<a3>
<a31>
<a311>line_1</a311>
</a31>
<a32>5o$</a32>
<a33>Add</a33>
</a3>
<a3>
<a31>
<a311>line_2</a311>
</a31>
<a32>5o$</a32>
<a33>Add</a33>
</a3>
<a4>account_holder</a4>
</a>
EDIT: Handling no descendants case.
The following (XSLT 1.0) stylesheet produces the desired result:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="a3">
<xsl:apply-templates select="a31/a311" />
</xsl:template>
<xsl:template match="a311">
<a3>
<a31>
<a311>
<xsl:value-of select="." />
</a311>
</a31>
<xsl:apply-templates select="../../*[not(self::a31)]" />
</a3>
</xsl:template>
</xsl:stylesheet>
You didn't specify the XSLT version. Here's an XSLT2 stylesheet that will accomplish what you want:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a3" mode="#default">
<xsl:apply-templates select="a31/a311"/>
</xsl:template>
<xsl:template match="a311">
<xsl:apply-templates select="../.." mode="x">
<xsl:with-param name="label" select="text()"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="a3" mode="x">
<xsl:param name="label"/>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<a31>
<a311><xsl:value-of select="$label"/></a311>
</a31>
<xsl:apply-templates select="* except a31"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
It uses an identity transform to pass through the "uninteresting" elements while trapping the <a3> node and applying special handling. If you need an XSLT1 solution, merely
Replace select="* except a31" with select="*[name() != 'a31']"
Remove the mode="#default" in the first "a3" template
My xml structure contains a program as the parent of both certificates and courses. I want to split the structure up to create an independent listing of certificates and courses without the common program parent. The original structure is:
<root>
<program>
<program-name>Math</program-name>
<certificate>...</certificate> <!-- There can 0 or more of these -->
<course>...</course> <!-- There can 0 or more of these -->
</program>
<program>
...
</program>
</root>
The output should look like so:
<root>
<program-courses>
<program>
<program-name>Math</program-name>
<course/> <!-- There can 0 or more of these -->
</program>
...
</program-courses>
<program-certificates>
<program>
<program-name>Math</program-name>
<certificate/> <!-- There can 0 or more of these -->
</program>
...
</program-certificates>
</root>
Update: Answered using Paul's suggestion for using a mode this is what the relevant portion of the xslt became:
<xsl:template match="root">
<xsl:element name="root">
<xsl:element name="program-courses">
<xsl:apply-templates select="root/program-area" mode="course"/>
</xsl:element>
<xsl:element name="program-certificates">
<xsl:apply-templates select="root/program-area" mode="certificate"/>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="program-area" mode="course">
<xsl:element name="program-area">
<!-- Get the name here -->
<xsl:element name="course">
<xsl:apply-templates select="course"/>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="program-area" mode="certificate">
<xsl:element name="program-area">
<!-- Get the name here -->
<xsl:element name="course">
<xsl:apply-templates select="certificate"/>
</xsl:element>
</xsl:element>
</xsl:template>
Note that this solution is pared down from the actual one so it may not work as is against the original input.
Selecting program elements, You could use #mode (on apply-templates and a corresponding template) to differentiate between whether you are operating within the output of program-courses or program-certificates
From root, you could select program/course or program/certificate to generate the output program.
From root, you could use for-each select="program" and for the part that is intended to output program-courses only extract the program-name and course element, and perform the corresponding extraction in the part that outputs program-certificates.
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="program">
<xsl:apply-templates select="course[1]|certificate[1]"/>
</xsl:template>
<xsl:template match="course[1]">
<program-courses>
<program>
<xsl:apply-templates select="../*[not(self::certificate)]"
mode="copy"/>
</program>
</program-courses>
</xsl:template>
<xsl:template match="certificate[1]">
<program-certificates>
<program>
<xsl:apply-templates select="../*[not(self::course)]"
mode="copy"/>
</program>
</program-certificates>
</xsl:template>
<xsl:template match="node()" mode="copy">
<xsl:call-template name="identity"/>
</xsl:template>
</xsl:stylesheet>
Output:
<root>
<program-certificates>
<program>
<program-name>Math</program-name>
<certificate>...</certificate>
</program>
</program-certificates>
<program-courses>
<program>
<program-name>Math</program-name>
<course>...</course>
</program>
</program-courses>
</root>
EDIT: If you want something more "push style" like your posted solution:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()" mode="certificate">
<xsl:call-template name="identity"/>
</xsl:template>
<xsl:template match="node()" mode="course">
<xsl:call-template name="identity"/>
</xsl:template>
<xsl:template match="program">
<program-courses>
<program>
<xsl:apply-templates mode="course"/>
</program>
</program-courses>
<program-certificates>
<program>
<xsl:apply-templates mode="certificate"/>
</program>
</program-certificates>
</xsl:template>
<xsl:template match="course" mode="certificate"/>
<xsl:template match="certificate" mode="course"/>
</xsl:stylesheet>