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>
Related
<section>
<title>WHO applauds the efforts of test developers to <it>innovate and respond</it> to the needs of the population</title>
<xsl:template match="section/title">
<xsl:element name="head1">
<xsl:apply-templates select="upper-case(node())"/>
</xsl:element>
</xsl:template>
Use
<xsl:template match="section/title//text()">
<xsl:value-of select="upper-case(.)"/>
</xsl:template>
together with the identity transformation (e.g. declared by <xsl:mode on-no-match="shallow-copy"/> in XSLT 3 or spelled out as
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
in earlier versions).
You can then override it for any other transformations you want or need by adding specific templates e.g. <xsl:template match="section/title"><head1><xsl:apply-templates/></head1></xsl:template>.
I have given below the XSL codes i have tried and the part of input and output codes.If I use XSL1 to rename the namespace prefix values in the xml tags , unfortunately it collapses the attribute values and if I use XSL2 attributes are getting created a separate nodes. Can someone pls help to write an XSL to rename the prefixes but to keep the attributes in the same node.
XSL 1:
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ns0:*">
<xsl:element name="ubl:{local-name()}" namespace="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
XSL2:
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy></xsl:template>
<xsl:template match="#*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="ns0:*">
<xsl:element name="ubl:{local-name()}" namespace="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
Input:
<ns5:LegalMonetaryTotal>
<ns2:LineExtensionAmount currencyID="EUR">35.38</ns2:LineExtensionAmount>
<ns2:TaxExclusiveAmount currencyID="EUR">35.38</ns2:TaxExclusiveAmount>
<ns2:TaxInclusiveAmount currencyID="EUR">37.5</ns2:TaxInclusiveAmount>
<ns2:PrepaidAmount currencyID="EUR">37.5</ns2:PrepaidAmount>
<ns2:PayableAmount currencyID="EUR">0.00</ns2:PayableAmount>
</ns5:LegalMonetaryTotal>
Output1:
<cac:LegalMonetaryTotal >
<cbc:LineExtensionAmount >EUR35.38</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount >EUR35.38</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount >EUR37.5</cbc:TaxInclusiveAmount>
<cbc:PrepaidAmount >EUR37.5</cbc:PrepaidAmount>
<cbc:PayableAmount >EUR0.00</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
Output2:
<cac:LegalMonetaryTotal >
<cbc:LineExtensionAmount >
<currencyID>EUR</currencyID>
35.38
</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount >
<currencyID>EUR</currencyID>
35.38
</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount >
<currencyID>EUR</currencyID>
37.5
</cbc:TaxInclusiveAmount>
<cbc:PrepaidAmount >
<currencyID>EUR</currencyID>
37.5
</cbc:PrepaidAmount>
<cbc:PayableAmount>
<currencyID>EUR</currencyID>
0.00
</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
But Expected output is: I have tried multiple options guys. Please help me to get the below format.
<cbc:LegalMonetaryTotal>
<cac:LineExtensionAmount currencyID="EUR">35.38</cac:LineExtensionAmount>
<cac:TaxExclusiveAmount currencyID="EUR">35.38</cac:TaxExclusiveAmount>
<cac:TaxInclusiveAmount currencyID="EUR">37.5</cac:TaxInclusiveAmount>
<cac:PrepaidAmount currencyID="EUR">37.5</cac:PrepaidAmount>
<cac:PayableAmount currencyID="EUR">0.00</cac:PayableAmount>
</cbc:LegalMonetaryTotal>
Regards,
Indu
The reason why your first stylesheet does not output any attributes is that you have no template that handles attributes. Therefore, the instruction:
<xsl:apply-templates select="#* | node()"/>
does not do anything for attributes.
Try changing your first template:
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
to the standard identity transform template:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
That will probably make it work. I say probably, because your stylesheet does not fit your input nor the expected output. Your stylesheet looks for:
<xsl:template match="ns0:*">
but your input has no elements with a ns0 prefix. And your stylesheet outputs a ubl prefix, while your expected output uses cbc and cac prefixes. And - as already mentioned in the comments to your question - all these prefixes need to be bound to namespaces in order for the input and the input to be well-formed XML documents.
I've tried to combine solutions to the similar questions asked here but all of them work in very specific cases. My case is the following: I have an arbitrary xml document which contains some tags, let's say <separator/>. What I want is to split parent elements of these tags like that:
INPUT:
<some_tag some_attr="some_value">
some text
<some_other_tag>some another text</some_other_tag>
<separator/>
some other content
</some_tag>
OUTPUT:
<some_tag some_attr="some_value">
some text
<some_other_tag>some another text</some_other_tag>
</some_tag>
<separator/>
<some_tag some_attr="some_value">
some other content
</some_tag>
Also, I am limited to XSLT 1.0 since Xalan is used in the project
Either use sibling recursion or use a key to find the nodes "belonging" to a separator. Additional care is needed to copy stuff following the last separator:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="sep" match="*[separator]/node()[not(self::separator)]" use="generate-id(following-sibling::separator[1])"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[separator]">
<xsl:apply-templates select="separator" mode="split"/>
<xsl:if test="separator[last()]/following-sibling::node()">
<xsl:copy>
<xsl:apply-templates select="#* | separator[last()]/following-sibling::node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
<xsl:template match="separator" mode="split">
<xsl:apply-templates select=".." mode="split">
<xsl:with-param name="separator" select="."/>
</xsl:apply-templates>
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="*" mode="split">
<xsl:param name="separator"/>
<xsl:copy>
<xsl:apply-templates select="#* | key('sep', generate-id($separator))"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/pPqsHTc
Note that if you use Xalan Java then in the Java world this is much easier with Saxon 9 and XSLT 2/3's <xsl:for-each-group select="node()" group-adjacent="boolean(self::separator)"> or group-starting-with="separator".
Hi I have an xml document which look like that:
<a> <!-- Several nodes "a" with the same structure for children -->
<b>12</b>
<c>12</c>
<d>12</d>
<e>12</e>
<f>12</f>
<g>12</g>
</a>
I'm trying to obtain the following document using xslt 2.0
<a>
<b>12</b>
<c>12</c>
<wrap>
<d>12</d>
<e>12</e>
<f>12</f>
<g>12</g>
</wrap>
</a>
I started my xsl file with
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
And changed it for several cases like replacing a string part, filter some nodes, etc.
But I'm stuck with "selecting four consecutive nodes", any clue on how to achieve the wrapping?
If all your a elements are genuinely exactly the same structure then the simplest would be a brute-force
<xsl:template match="a">
<xsl:copy>
<xsl:apply-templates select="b | c" />
<wrap>
<xsl:apply-templates select="d | e | f | g" />
</wrap>
</xsl:copy>
</xsl:template>
or if you want to be a bit cleverer
<wrap>
<xsl:apply-templates select="* except (b | c)" />
</wrap>
If you want to always "wrap" the last four child elements of a, then how about
<xsl:template match="a">
<xsl:variable name="lastFour" select="*[position() > (last() - 4)]" />
<xsl:copy>
<xsl:apply-templates select="* except $lastFour" />
<wrap>
<xsl:apply-templates select="$lastFour" />
</wrap>
</xsl:copy>
</xsl:template>
With XSLT 2.0 you can also make use of for-each-group group-adjacent:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a">
<xsl:copy>
<xsl:for-each-group select="*" group-adjacent="boolean(self::d | self::e | self::f | self::g)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<wrap>
<xsl:apply-templates select="current-group()"/>
</wrap>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</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>