XSLT keep namespace on output - xslt

I need help preserving the namespace on XSLT transformation. I see other threads have solutions, but I don't understand them. When I use the XSLT transformation below, all the namespace disappears. Below is what I'm trying to accomplish. Any help is much appreciated! Thank you!
Before transform XML:
<?xml version='1.0' encoding='UTF-8'?>
<Worker
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:this="this.file/eib"
xmlns:is="java:com.workday.esb.intsys.xpath.ParsedIntegrationSystemFunctions"
xmlns:tv="java:com.workday.esb.intsys.TypedValue">
<Detail>
<EmployeeID>123456</EmployeeID>
<PayCode>Earning</PayCode>
<Amount>4243.20</Amount>
</Detail>
<Detail>
<EmployeeID>123456</EmployeeID>
<PayCode>Deduction</PayCode>
<Amount>2265.60</Amount>
</Detail>
</Worker>
My XSLT transformation:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" exclude-result-prefixes="xsl wd is xsd this env"
xmlns:wd="urn:com.workday/bsvc"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:is="java:com.workday.esb.intsys.xpath.ParsedIntegrationSystemFunctions"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:this="urn:this-stylesheet">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="/">
<xsl:copy>
<Worker>
<xsl:for-each select="Worker" >
<Detail>
<EmployeeID><xsl:value-of select="Detail[(PayCode ='Earning')]/EmployeeID"/></EmployeeID>
<PayCode><xsl:value-of select="Detail[(PayCode ='Earning')]/PayCode"/></PayCode>
<Amount><xsl:value-of select="Detail[(PayCode ='Earning') and (string-length(Amount) > 0)]/Amount"/></Amount>
</Detail>
</xsl:for-each>
</Worker>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Desired output:
<?xml version="1.0" encoding="UTF-8"?>
<Worker
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:this="this.file/eib"
xmlns:is="java:com.workday.esb.intsys.xpath.ParsedIntegrationSystemFunctions"
xmlns:tv="java:com.workday.esb.intsys.TypedValue">
<Detail>
<EmployeeID>123456</EmployeeID>
<PayCode>Earning</PayCode>
<Amount>4243.20</Amount>
</Detail>
</Worker>

At least in XSLT 2.0 (the version you used), namespaces disappear because you used exclude-result-prefixes attribute.
Just remove it and all namespaces included in your XSLT sheet, except for xsl, will be presented in the output, in the order of appearance.
But if you decided to omit all namespaces, it is easier to write exclude-result-prefixes="#all", instead of writing them again.

I can see no good reason to keep namespace declarations that are not used in your output. But if you really want to have them, why don't you simply copy them (as part of their parent element) - for example:
XSLT
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Worker">
<xsl:copy>
<xsl:copy-of select="Detail[PayCode ='Earning']"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this is applied to your input example, the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<Worker xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:this="this.file/eib"
xmlns:is="java:com.workday.esb.intsys.xpath.ParsedIntegrationSystemFunctions"
xmlns:tv="java:com.workday.esb.intsys.TypedValue">
<Detail>
<EmployeeID>123456</EmployeeID>
<PayCode>Earning</PayCode>
<Amount>4243.20</Amount>
</Detail>
</Worker>
Demo: http://xsltransform.net/gVhD8Qt

Related

Skip first node and Copy rest of the nodes using XSLT 3.0

I will need to skip first Worker node and copy rest of the nodes using XSLT 3.0
My source XML is
<?xml version="1.0" encoding="UTF-8"?>
<Workers>
<Worker>
<Employee>Emp</Employee>
<ID>Identifier</ID>
</Worker>
<Worker>
<Employee>12344</Employee>
<ID>1245599</ID>
</Worker>
<Worker>
<Employee>25644</Employee>
<ID>7823565</ID>
</Worker>
</Workers>
and desired output is
<?xml version="1.0" encoding="utf-8"?>
<Workers>
<Worker>
<Employee>12344</Employee>
<ID>1245599</ID>
</Worker>
<Worker>
<Employee>25644</Employee>
<ID>7823565</ID>
</Worker>
</Workers>
and XSLT that i have is
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="Worker[position() = 1]"/>
</xsl:stylesheet>
Above XSLT produces the output that I was expecting but I'm looking to see if there is a better way to skip the first node without using postion()as I'm not sure how efficient my current code is to process large files( approximately 800 MB)
I had to use following to remove white spaces from my result XML
<xsl:strip-space elements="*"/>
Can anyone check my code and provide any suggestion to improvise my code please?
===============
With Michael Kay's suggestion, my code looks like this
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<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> -->
<!-- Removed above Identity Transformation -->
<xsl:mode streamable="yes" on-no-match="shallow-copy"/>
<xsl:template match="Workers">
<xsl:copy>
<xsl:apply-templates select="tail(Worker)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I would write it
<xsl:template match="Worker[1]"/>
for readability, but it's all the same.
Match patterns with positional predicates can perform badly, so you're right to be cautious, but a simple one like this should be OK. In fact the main adverse consequence is probably that Saxon will allocate preceding-sibling pointers in the TinyTree, so that it can compute the node's sibling position.
Saxon effectively implements it as
<xsl:template match="Worker[not(preceding-sibling::Worker)]"/>
and you might prefer to write it that way. However, neither form is streamable.
To make it streamable, you could drop the unwanted nodes by not selecting them:
<xsl:template match="Workers">
<xsl:copy>
<xsl:apply-templates select="tail(Worker)"/>
</xsl:copy>
</xsl:template>
which might also be fractionally faster in the non-streaming case; and it saves memory because preceding-sibling pointers aren't needed.

XSLT: why the XSLT is not generating output when there are only outer XSL tags

My real code is not this, but the problem that i am stating here applies to my real code.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book.child.1>
<title>charithram</title>
<author>sarika</author>
</book.child.1>
<book.child.2>
<title>doublebell</title>
<author>psudarsanan</author>
</book.child.2>
</books>
XSLT 1:
<?xml version="1.0" encoding="ISO-8859-1"?>
<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:template match="/">
<xsl:for-each select="books/*">
<newbook>
<title>
<xsl:value-of select="title" />
</title>
</newbook>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
There in no output generated for this XSLT. I am trying using online tool: http://www.freeformatter.com/xsl-transformer.html
I could not understand what was wrong, Finally when I modified the XSLT like as written below,
XSLT 2:
<?xml version="1.0" encoding="ISO-8859-1"?>
<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:template match="/">
<mytag>
<xsl:for-each select="books/*">
<newbook>
<title>
<xsl:value-of select="title" />
</title>
</newbook>
</xsl:for-each>
</mytag>
</xsl:template>
</xsl:stylesheet>
The output is generated in this case:
outputXML:
<?xml version="1.0" encoding="UTF-8"?>
<mytag>
<newbook>
<title>charithram</title>
</newbook>
<newbook>
<title>doublebell</title>
</newbook>
</mytag>
Can you please explain why is this behavior?
Also I don't know how exactly to ask this question, so please edit or let me know if i need to change the question title.
Your first XSLT will theoretically produce the output
<?xml version="1.0" encoding="UTF-8"?>
<newbook>
<title>charithram</title>
</newbook>
<newbook>
<title>doublebell</title>
</newbook>
But that output is not valid XML, because it has 2 root tags, which is not well-formed XML.
In this situation, you have probably the following choices
specify a root element like you did in XSLT 2
change the output from XML to TEXT, but be aware that any XML program will not be able to read the output

xslt and SOAP namespace: how to add

I have this basic SOAP response:
<?xml version="1.0" encoding="WINDOWS-1252" standalone="yes"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body xmlns="http://www.xxx.com/dotnet/types/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<a>
<b>
<c>
c-value
</c>
b-value
</b>
a-value
</a>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
and I want to output just the value of the c node: c-value.
I don't understand why this xsl is NOT working:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
>
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select="//c"/>
</xsl:template>
</xsl:stylesheet>
It seems that the problem is in the name space at <SOAP-ENV:Body xmlns="http://www.xxx.com/dotnet/types/"
If I remove it, it works.
I guess I should change the xpath in select="//c" or add somewhere in the xls the needed namespace, but I can't get it right!
ok, this link was helpful: How to 'select' from XML with namespaces?
I added the namespace like this:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xxx="http://www.xxx.com/dotnet/types/"
>
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select="//xxx:c"/>
</xsl:template>
</xsl:stylesheet>
First the xmlns:xxx is added to the xlst and then the XPath becomes //xxx:c.
Writing questions in SO, solves problems...

xslt transform xml to xml how to get attibutes of an entity?

I want to transform an xml to naother xml, with xstl
I have an entity <AAA id="e1" ts=" bb">
How can i get this entity with the attributes in my new xml file?
The <xsl:copy-of> element can be used to accomplish what you're getting after.
When this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:copy-of select="AAA" />
</xsl:template>
</xsl:stylesheet>
...is run against the following sample XML:
<t>
<AAA id="e1" ts=" bb" />
</t>
...the wanted result is produced:
<?xml version="1.0" encoding="UTF-8"?>
<AAA id="e1" ts=" bb" />

XSLT remove unwanted elements

I have XML
<getInquiryAboutListReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<inquiryAbouts>
<inquiryAbout>
<code>Code</code>
<nameKk>Something</nameKk>
<nameRu>Something</nameRu>
<documents xsi:nil="true"/>
</inquiryAbout>
</inquiryAbouts>
</getInquiryAboutListReturn>
And I want to process it with XSLT to copy all XML
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" />
<xsl:template match="/">
<xsl:copy-of select="//getInquiryAboutListReturn/inquiryAbouts"/>
</xsl:template>
</xsl:stylesheet>
How could I copy all XML without <documents xsi:nil="true"/> or without xsi:nil="true"?
Desired output XML
<getInquiryAboutListReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<inquiryAbouts>
<inquiryAbout>
<code>Code</code>
<nameKk>Something</nameKk>
<nameRu>Something</nameRu>
</inquiryAbout>
</inquiryAbouts>
</getInquiryAboutListReturn>
This simple XSLT:
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="1.0">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- TEMPLATE #1 -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- TEMPLATE #2 -->
<xsl:template match="*[#xsi:nil = 'true']" />
</xsl:stylesheet>
...when applied to the OP's source XML:
<?xml version="1.0"?>
<getInquiryAboutListReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<inquiryAbouts>
<inquiryAbout>
<code>Code</code>
<nameKk>Something</nameKk>
<nameRu>Something</nameRu>
<documents xsi:nil="true"/>
</inquiryAbout>
</inquiryAbouts>
</getInquiryAboutListReturn>
...produces the expected result XML:
<?xml version="1.0"?>
<getInquiryAboutListReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<inquiryAbouts>
<inquiryAbout>
<code>Code</code>
<nameKk>Something</nameKk>
<nameRu>Something</nameRu>
</inquiryAbout>
</inquiryAbouts>
</getInquiryAboutListReturn>
EXPLANATION:
The first template -- the Identity Template -- copies all nodes and attributes from the source XML document as-is.
The second template, which matches all elements with the specified, namespaced attribute equalling "true", effectively removes those elements.