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>
Related
If start with p element processing-instruction node then how to move processing-instruction position[1] before p element? - XSLT
Input
<root>
<p><?page 1?>aaaa bbbb <?page 2?>cccccc</p>
<p>aaaa <?page 3?>bbbb <?page 4?>ccccc</p>
</root>
Expected output
<root>
<?page 1?><p>aaaa bbbb <?page 2?>cccccc</p>
<p>aaaa <?page 3?>bbbb <?page 4?>ccccc</p>
</root>
XSLT
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p[count(processing-instruction()) > 1]">
<xsl:apply-templates select="processing-instruction()[1]"/>
<xsl:copy>
<xsl:apply-templates select="node() except processing-instruction()[1]"/>
</xsl:copy>
</xsl:template>
Solved
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p[count(processing-instruction()) > 1 and child::processing-instruction()[not(preceding-sibling::node())]]">
<xsl:apply-templates select="processing-instruction()[1]"/>
<xsl:copy>
<xsl:apply-templates select="node() except processing-instruction()[1]"/>
</xsl:copy>
</xsl:template>
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>
I'm quite new to XSLT and I was trying to copy an existent XML file I already have but with the elements reordered but I got stuck when trying to reorder grandchildren.
Let's say I have this input:
<grandParent>
<parent>
<c>789</c>
<b>
<b2>123</b2>
<b1>456</b1>
</b>
<a>123</a>
</parent>
....
</grandParent>
What I want to do is get the same XML file but changing the order of the tags to be a,b,c with b = b1, b2 in that order.
So I started with the XSLT file:
<xsl:template match="node()|#*"> <- This should copy everything as it is
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="grandParent/parent"> <- parent elements will copy in this order
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="a"/>
<xsl:copy-of select="b"/>
<xsl:copy-of select="c"/>
</xsl:copy>
</xsl:template>
But "xsl:copy-of select="b"" copies the elements as they are specified (b2, b1).
I tried using another xsl:template for "grandParent/parent/b" but wouldn't help.
Maybe I'm not doing things the correct way... Any tips?
Thanks!
SOLUTION - Thanks to Nils
Your solution works just fine Nils, I just customized it a bit more to fit in my current scenario where "b" is optional and the names of the tags might not be correlative.
The final code is like this:
<xsl:template match="node()|#*"> <- This should copy everything as it is
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="grandParent/parent"> <- parent elements will copy in this order
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="a"/>
<xslt:if test="b">
<b>
<xsl:copy-of select="b1"/>
<xsl:copy-of select="b2"/>
</b>
</xslt:if>
<xsl:copy-of select="b"/>
<xsl:copy-of select="c"/>
</xsl:copy>
</xsl:template>
I tried using another xsl:template for "grandParent/parent/b" but wouldn't help.
Since you have an identity template you should use <xsl:apply-templates> instead of <xsl:copy-of>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="grandParent/parent">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="a"/>
<xsl:apply-templates select="b"/>
<xsl:apply-templates select="c"/>
</xsl:copy>
</xsl:template>
Now you can add a similar template for the b elements
<xsl:template match="parent/b">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="b1"/>
<xsl:apply-templates select="b2"/>
</xsl:copy>
</xsl:template>
This will nicely handle the case where b doesn't exist - if the select="b" doesn't find any elements, then no templates will fire.
In fact, if the sort order is the same in both cases (alphabetically by element name) then you can combine the two templates into one which uses <xsl:sort>, giving a complete transformation of
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*" />
<xsl:output method="xml" indent="yes" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="grandParent/parent | parent/b">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="*">
<xsl:sort select="name()" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
(for the example XML you've given you don't actually need the #* bits because the XML doesn't include any attributes, but it won't do any harm to leave it there in case there are any in the real XML or you add any in future).
Using xsl:sort.
The following code is off the top of my head and might not work; the thought behind it should be clear though.
<xsl:template match="node()|#*"> <- This should copy everything as it is
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="grandParent/parent"> <- parent elements will copy in this order
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="a"/>
<b>
<xsl:for-each select="b/*">
<xsl:sort select="text()" />
<xsl:copy-of select="." />
</xsl:for-each>
</b>
<xsl:copy-of select="c"/>
</xsl:copy>
</xsl:template>
Here is a most generic solution -- using xsl:sort and templates -- no xsl:copy-of and no hardcoding of specific names:
<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:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*/*">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="node()">
<xsl:sort select="name()"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<grandParent>
<parent>
<c>789</c>
<b>
<b2>123</b2>
<b1>456</b1>
</b>
<a>123</a>
</parent>
....
</grandParent>
the wanted, correct result is produced:
<grandParent>
<parent>
<a>123</a>
<b>
<b1>456</b1>
<b2>123</b2>
</b>
<c>789</c>
</parent>
....
</grandParent>
Now, let's change all the names in the XML document -- note that none of the other answers works with this:
<someGrandParent>
<someParent>
<z>789</z>
<y>
<y2>123</y2>
<y1>456</y1>
</y>
<x>123</x>
</someParent>
....
</someGrandParent>
We apply the same transformation and it again produces the correct result:
<someGrandParent>
<someParent>
<x>123</x>
<y>
<y1>456</y1>
<y2>123</y2>
</y>
<z>789</z>
</someParent>
....
</someGrandParent>
I was looking for some guidance on how best to approach my issue.
I have an XML document like the following but on a larger scale.
<NewDataSet>
<Table Attri1="Attri1Val" Attri2="Attri2Val" Attri3="Attri3Val" Attri4="Attri4Val" Attri5="Attri5Val" Attri6="Attri6Val" Attri7="Attri7" />
</NewDataSet>
I need to move certain attributes from the Table node, for example Attri2 and Attri5 into elements within the Table node, however I need to leave the rest of the attributes as they are.
What would be the best way to approach this? The data scale is about 3-4 times that shown.
EDIT:
Expected output:
<NewDataSet>
<Table Attri1="Attri1Val" Attri3="Attri3Val" Attri4="Attri4Val" Attri6="Attri6Val" Attri7="Attri7">
<Attri2>Attri2Val</Attri2>
<Attri5>Attri5Val</Attri5>
</Table>
</NewDataSet>
The complexity is not really the issue, more the scale of the data and what is the best way to deal with it.
Use
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Table">
<xsl:copy>
<xsl:apply-templates select="#*[not(name() = 'Attri2') and not(name() = 'Attri5')]"/>
<xsl:apply-templates select="#Attri2 | #Attri5 | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Table/#Attri2 | Table/#Attri5">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
[edit]
The name comparison of the attributes is a bit ugly but will probably do for your sample. What we really need is #* execpt (#Attri2, #Attri5), only that is XPath 2.0. With XPath 1.0 the equivalent is
<xsl:template match="Table">
<xsl:copy>
<xsl:variable name="all-attributes" select="#*"/>
<xsl:variable name="to-be-transformed" select="#Attri2 | #Attri5"/>
<xsl:apply-templates select="$all-attributes[count(. | $to-be-transformed) != count($to-be-transformed)]"/>
<xsl:apply-templates select="$to-be-transformed | node()"/>
</xsl:copy>
</xsl:template>
This generic transformation can handle any set of attributes, with any length, whose names can be specified externally to the 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:param name="pToTransform" select="'|Attri2|Attri5|'"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Table">
<xsl:copy>
<xsl:apply-templates select=
"#*[not(contains($pToTransform, concat('|',name(),'|')))] | node()"/>
<xsl:apply-templates mode="makeElement"
select="#*[contains($pToTransform, concat('|',name(),'|'))]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*" mode="makeElement">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied to the provided XML document:
<NewDataSet>
<Table Attri1="Attri1Val" Attri2="Attri2Val"
Attri3="Attri3Val" Attri4="Attri4Val"
Attri5="Attri5Val" Attri6="Attri6Val"
Attri7="Attri7" />
</NewDataSet>
the wanted, correct result is produced:
<NewDataSet>
<Table Attri1="Attri1Val" Attri3="Attri3Val" Attri4="Attri4Val" Attri6="Attri6Val" Attri7="Attri7">
<Attri2>Attri2Val</Attri2>
<Attri5>Attri5Val</Attri5>
</Table>
</NewDataSet>
I have an XML file from which I need to delete an attribute with name "Id" (It must be deleted wherever it appears) and also I need to rename the parent tag, while keeping its attributes and child elements unaltered .. Can you please help me modifying the code. At a time, am able to achieve only one of the two requirements .. I mean I can delete that attribute completely from the document or I can change the parent tag ..
Here is my code to which removes attribute "Id":
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#Id[parent::*]">
</xsl:template>
Please help me changing the parent tag name from "Root" to "Batch".
None of the offered solutions really solves the problem: they simply rename an element named "Root" (or even just the top element), without verifying that this element has an "Id" attribute.
wwerner is closest to a correct solution, but renames the parent of the parent.
Here is a solution that has the following properties:
It is correct.
It is short.
It is generalized (the replacement name is contained in a variable).
Here is the code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="vRep" select="'Batch'"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#Id"/>
<xsl:template match="*[#Id]">
<xsl:element name="{$vRep}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()|text()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#Id" />
<xsl:template match="Root">
<Batch>
<xsl:copy-of select="#*|*|text()" />
</Batch>
</xsl:template>
This should do the job:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()|text()" />
</xsl:copy>
</xsl:template>
<xsl:template match="node()[node()/#Id]">
<batch>
<xsl:apply-templates select='#*|*|text()' />
</batch>
</xsl:template>
<xsl:template match="#Id">
</xsl:template>
</xsl:stylesheet>
I tested with the following XML input:
<root anotherAttribute="1">
<a Id="1"/>
<a Id="2"/>
<a Id="3" anotherAttribute="1">
<b Id="4"/>
<b Id="5"/>
</a>
I would try:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#Id">
</xsl:template>
<xsl:template match="/Root">
<Batch>
<xsl:apply-templates select="#*|node()"/>
</Batch>
</xsl:template>
The first block copies all that is not specified, as you use.
The second replaces #id with nothing wherever is occurs.
The third renames /Root to /Batch.