XSLT to copy one node value and create a new node - xslt

Can someone guide me on how to write a XSLT1.0 to create the output as below?
<csvImportSchema>
<payload>
<test>**COPY VALUE**</test>
<test2>2</test2>
<test3>3</test3>
<ean>1111111111</ean>
<productId/>
</payload>
</csvImportSchema>
to
<csvImportSchema>
<payload>
<test>COPY VALUE</test>
<test2>2</test2>
<test3>3</test3>
<ean>1111111111</ean>
<productId/>
**<copied>COPY VALUE</copied>**
</payload>
<csvImportSchema>

The following XSLT extracts the COPY VALUE string between ** and copies it to the end of the <payload> tag. Then the strings before and after the COPY VALUE are used as prefixes and postfixes of the <copied> tag.
<?xml version="1.0" encoding="UTF-8"?>
<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="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="payload">
<xsl:variable name="copyval" select="substring-before(substring-after(test/text(),'**'),'**')" />
<xsl:variable name="valBefore" select="substring-before(test/text(),$copyval)" />
<xsl:variable name="valAfter" select="substring-after(test/text(),$copyval)" />
<xsl:copy>
<test><xsl:value-of select="$copyval" /></test>
<xsl:apply-templates select="node()[not(self::test)]|#*" />
<xsl:value-of select="$valBefore" /><copied><xsl:value-of select="$copyval" /></copied><xsl:value-of select="$valAfter" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

XSLT Transformation that add namespace prefix doesn't copy attributes

I have a requirement to add a namespace prefix to all my xml elements.
I have a working xslt for this but unfortunately it doesn't copy the attributes. What do I need to change to also copy these attributes.
This my code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:ns0="http://ns.hr-xml.org/2007-04-15" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="*">
<xsl:element name="ns0:{local-name()}">
<xsl:apply-templates select="*|node()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The input XML is this:
<HumanResource schemaLocation="http://ns.hr-xml.org/2007-04-15 ../HumanResource.xsd">
<HumanResourceId idOwner="StaffingCompany">
<IdValue>123COMP</IdValue>
</HumanResourceId>
<HumanResourceId idOwner="StaffingCustomer">
<IdValue>456CUST</IdValue>
<!--double hre-id, due fms wnr-code swap-->
</HumanResourceId>
<HumanResourceStatus status="x:Assigned"/>
<ReferenceInformation>
<StaffingSupplierId idOwner="StaffingCustomer">
<IdValue>105964</IdValue>
</StaffingSupplierId>
<StaffingCustomerId idOwner="StaffingCompany">
<IdValue>MIN0000612</IdValue>
</StaffingCustomerId>
<StaffingCustomerId idOwner="StaffingCustomer">
<IdValue>ELI</IdValue>
</StaffingCustomerId>
<StaffingSupplierOrgUnitId idOwner="StaffingCompany">
<IdValue>2606379</IdValue>
</StaffingSupplierOrgUnitId>
<StaffingCustomerOrgUnitId idOwner="StaffingCustomer">
<IdValue>ELI</IdValue>
</StaffingCustomerOrgUnitId>
</ReferenceInformation>
<ResourceInformation>
<PersonName>
<FormattedName>J</FormattedName>
<LegalName>C</LegalName>
<GivenName>J</GivenName>
<PreferredGivenName>J</PreferredGivenName>
<MiddleName>C.J.</MiddleName>
<FamilyName primary="true"></FamilyName>
</PersonName>
<EntityContactInfo>
<EntityName></EntityName>
<PersonName>
<FormattedName>Liesbeth Kramer</FormattedName>
</PersonName>
<ContactMethod>
<Telephone>
<FormattedNumber>070-3102150</FormattedNumber>
</Telephone>
</ContactMethod>
</EntityContactInfo>
<PostalAddress>
<CountryCode>NL</CountryCode>
<PostalCode></PostalCode>
<Municipality></Municipality>
<DeliveryAddress>
<AddressLine></AddressLine>
<StreetName></StreetName>
<BuildingNumber></BuildingNumber>
</DeliveryAddress>
</PostalAddress>
</ResourceInformation>
<Profile/>
<Preferences/>
<UserArea>
<ns2:HumanResourceAdditionalNL xmlns:ns2="http://ns.setu.nl/2008-01">
<ns2:BirthDate>1961-07-13</ns2:BirthDate>
<ns2:Sex>female</ns2:Sex>
</ns2:HumanResourceAdditionalNL>
</UserArea>
</HumanResource>
My result with my code looks like this:
<?xml version='1.0' ?>
<ns0:HumanResource xmlns:ns0="http://ns.hr-xml.org/2007-04-15">
<ns0:HumanResourceId>
<ns0:IdValue>123COMP</ns0:IdValue>
</ns0:HumanResourceId>
<ns0:HumanResourceId>
<ns0:IdValue>456CUST</ns0:IdValue>
</ns0:HumanResourceId>
<ns0:HumanResourceStatus/>
<ns0:ReferenceInformation>
As you can see, the ns0: is added ok but I lost the attributes.
You have lost the attributes because you have no code to select them. You currently do <xsl:apply-templates select="*|node()" />, but * select elements, and node() selects elements, text nodes, comments and processing instructions. You need to change it to this...
<xsl:apply-templates select="#*|node()" />
You would then need a separate template to match attributes and copy them.
<xsl:template match="#*">
<xsl:copy />
</xsl:template>
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:ns0="http://ns.hr-xml.org/2007-04-15" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="*">
<xsl:element name="ns0:{local-name()}">
<xsl:apply-templates select="#*|node()" />
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:copy />
</xsl:template>
</xsl:stylesheet>
Alternatively, you could use xsl:copy-of to copy the attributes. Try this XSLT too
<xsl:stylesheet version="1.0" xmlns:ns0="http://ns.hr-xml.org/2007-04-15" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="*">
<xsl:element name="ns0:{local-name()}">
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Note that <xsl:apply-templates /> is short-hand for <xsl:apply-templates select="node()" />

XSLT Tags attributes are getting lost

Input XML
<?xml version="1.0" encoding="UTF-8"?>
<web-inf metadata-complete="true">
<A>
<A1>DGDDG</A1>
<A1>TYTY</A1>
</A>
</web-inf>
When i am applying my transforms then the O/P XML is just dumping the <web-inf> tag without the metadata-complete="true" i.e as below
<?xml version="1.0" encoding="UTF-8"?>
<web-inf>
<A>
<A1>DGDDG</A1>
<A1>TYTY</A1>
</A>
</web-inf>
My XSLT Transform file has below in the beginning.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.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="web-inf[not(A/A1='hello')]">
<xsl:copy>
<xsl:call-template name="XXX"/>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Not sure what going wrong here.
Any suggestions?
<xsl:copy> copies only the current node, but not any attributes or child nodes. You are already catering for the child nodes with <xsl:apply-templates /> (which is equivalent to <xsl:apply-templates select="node()" />), but you also need to handle selecting attributes separately.
<xsl:template match="web-inf[not(A/A1='hello')]">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:call-template name="XXX"/>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>

xsl:strip-space combined with xsl:text messes up automatic indentation

Using XSLT 1.0 I would like to comment out certain XML elements and replace other XML elements, while keeping the XML nicely formatted.
For example, the following XML document
<doc>
<e1>foo</e1>
<e2>bar</e2>
</doc>
should be converted to
<doc>
<!--<e1>foo</e1>-->
<e3>foobar</e3>
<e4>foobar</e4>
</doc>
I am using the following XSL transformation and xsltproc for testing it:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*" />
<xsl:output indent="yes" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="e1">
<xsl:text disable-output-escaping="yes"><!--</xsl:text> <!--*-->
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
<xsl:text disable-output-escaping="yes">--></xsl:text> <!--*-->
</xsl:template>
<xsl:template match="e2">
<e3>foobar</e3><e4>foobar</e4>
</xsl:template>
</xsl:stylesheet>
But what I get is this:
<doc><!--<e1>foo</e1>--><e3>foobar</e3><e4>foobar</e4></doc>
The problem seems to be caused by the lines marked with '*' in my transformation; more specifically from inserting <!-- and -->. When I remove these two elements, the result is indented as expected.
Is there a way to wrap elements in comments while still keeping the output document nicely formatted?
Try whether outputting a comment with the serialization of the element, as in
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="http://lenzconsulting.com/xml-to-string/xml-to-string.xsl"/>
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="e1">
<xsl:comment>
<xsl:call-template name="xml-to-string"></xsl:call-template>
</xsl:comment>
</xsl:template>
<xsl:template match="e2">
<e3>foobar</e3><e4>foobar</e4>
</xsl:template>
</xsl:stylesheet>
gives you a better result.

xslt extract and sort leaf nodes by name - unexpected result

I wanted to extract leaf nodes and have them sorted.
My XSL gives unexpected results. How can I solve this?
Input
<root>
<b>
<b33 zzz="2" fff="3"></b33>
<b11></b11>
<b22></b22>
</b>
<a>
<a27></a27>
<a65 fff="0" eee="2" zzz="10"></a65>
<a11></a11>
</a>
</root>
Xsl
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<root>
<xsl:call-template name="leafnodes"/>
</root>
</xsl:template>
<xsl:template match="*[not(*)]|#*" name="leafnodes">
<xsl:copy>
<xsl:apply-templates select="node()">
<xsl:sort select="name()"/>
</xsl:apply-templates>
<xsl:apply-templates select="#*">
<xsl:sort select="name()"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output (I would expected it to be sorted, it is not)
<root>
<b33 fff="3" zzz="2" />
<b11 />
<b22 />
<a27 />
<a65 eee="2" fff="0" zzz="10" />
<a11 />
</root>
I would expect the nodes in the order a11, a27, a65, b11, b22, b33.
If I leave out the '[not(*)]', the xsl takes all nodes and sorts them properly.
How can this be solved?
To output all element which have no child sorted by name and the attributes also sorted by name. Try this;
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="//*[not(*)]">
<xsl:sort select="name()"/>
</xsl:apply-templates>
</root>
</xsl:template>
<xsl:template match="*|#*">
<xsl:copy>
<xsl:apply-templates select="#*" >
<xsl:sort select="name()"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Which will generate following output:
<root>
<a11/>
<a27/>
<a65 eee="2" fff="0" zzz="10"/>
<b11/>
<b22/>
<b33 fff="3" zzz="2"/>
</root>

How to remove the rootnodes using XSLT?

Input file:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:root xmlns:ns0="http://xyz.com/separate">
<ns0:root1>
<ns3:Detail xmlns:ns3="http://POProject/Details">
<DetailLines>
<ItemID>
<ItemDescription/>
</DetailLines>
</ns3:Detail>
</ns0:root1>
</ns0:root>
Output file:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Detail xmlns:ns0="http://POProject/Details">
<DetailLines>
<ItemID>
<ItemDescription/>
</DetailLines>
</ns0:Detail>
Question: I have to remove the root1 and root nodes and need to do small
changes in Detail node. How to write a xslt code to achieve this?
This...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://xyz.com/separate"
xmlns:ns3="http://POProject/Details">
<xsl:output method="xml" 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:apply-templates select="*/*/ns3:Detail" />
</xsl:template>
<xsl:template match="ns3:Detail">
<xsl:apply-templates select="." mode="copy-sans-namespace" />
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-sans-namespace" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
...will yield this...
<?xml version="1.0" encoding="utf-8"?>
<ns3:Detail xmlns:ns3="http://POProject/Details">
<DetailLines>
<ItemID />
<ItemDescription />
</DetailLines>
</ns3:Detail>
I'm not sure it is possible to control the prefix. The XDM data model does not consider it to be significant information.
UDPATE
To get the prefix rename, I thought you would have to go to an XML 1.1 supporting XSLT processor (allowing prefix undefine), but I found a way to do it with XML 1.0 . Try this ...
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://xyz.com/separate">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/" xmlns:ns3="http://POProject/Details">
<xsl:apply-templates select="*/*/ns3:Detail" />
</xsl:template>
<xsl:template match="ns0:Detail" xmlns:ns0="http://POProject/Details">
<ns0:Detail xmlns:ns0="http://POProject/Details">
<xsl:apply-templates select="*" mode="copy-sans-namespace" />
</ns0:Detail>
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-sans-namespace" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>