I'm transforming an XML to another XML inside wso2esb (uses saxon). There I have following input example:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<root>
<MyElement>content</MyElement>
</root>
</soapenv:Body>
</soapenv:Envelope>
My Problem is now that the my new content <NEW> should not get any namespace. But I get the following output after my XSLT:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<root>
<NEW xmlns="http://ws.apache.org/ns/synapse">content</NEW>
</root>
</soapenv:Body>
</soapenv:Envelope>
I do not want the xmlns="http://ws.apache.org/ns/synapse" declaration in the new Element . When I test with oXygen it does work, but when I run it inside wso2esb I get the I guess default namespace "http://ws.apache.org/ns/synapse".
So I have tried out 3 different ways of creating the NEW element, and
<xsl:stylesheet xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:avintis="http://www.avintis.com/esb" extension-element-prefixes="avintis" version="2.0" exclude-result-prefixes="#all" xpath-default-namespace="">
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/soapenv:Envelope|soapenv:Body">
<xsl:copy>
<xsl:apply-templates select="*|text()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<!-- FIRST METHOD - THE METHOD I want to use! this will receive the xmlns="http://ws.apache.org/ns/synapse" -->
<NEW>
<xsl:value-of select="MyElement"/>
</NEW>
<!-- Second methodworks - but I need to add xmlns="" to every element and there are a lot-->
<NEW xmlns="">
<xsl:value-of select="MyElement"/>
</NEW>
<!-- Third method works: But not very readable - I would prefer the first method -->
<xsl:element name="NEW">
<xsl:value-of select="MyElement"/>
</xsl:element>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Is there a way to remove this xmlns attribute when using the first method of element creation?
Thanks
The problem is not that there is an unwanted namespace declaration in the serialized output; the problem is that the NEW element is in the wrong namespace. You need to think about what the (expanded) names of the elements and attributes are, and the namespace declarations will look after themselves.
I strongly suspect that the code you are executing is different from what you have shown us. I think it must have the default namespace declaration xmlns="http://ws.apache.org/ns/synapse" on some ancestor of the <NEW> literal result element, probably on the xsl:stylesheet element itself. If that's not the case, then wso2esb is doing something very strange when it runs the code.
Just found the solution by adding a xmlns="" to the <xsl:stylesheet. Seems like WSO2 gives a default namespace when none is specified.
<xsl:stylesheet xmlns="" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:avintis="http://www.avintis.com/esb" extension-element-prefixes="avintis" version="2.0" exclude-result-prefixes="#all" xpath-default-namespace="">
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/soapenv:Envelope|soapenv:Body">
<xsl:copy>
<xsl:apply-templates select="*|text()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<!-- FIRST METHOD - THE METHOD I want to use! this will receive the xmlns="http://ws.apache.org/ns/synapse" -->
<NEW>
<xsl:value-of select="MyElement"/>
</NEW>
<!-- Second methodworks - but I need to add xmlns="" to every element and there are a lot-->
<NEW xmlns="">
<xsl:value-of select="MyElement"/>
</NEW>
<!-- Third method works: But not very readable - I would prefer the first method -->
<xsl:element name="NEW">
<xsl:value-of select="MyElement"/>
</xsl:element>
</xsl:copy>
</xsl:template>
Related
I have trouble with namespace. I need this output:
<xyt:arguments xmlns:xyt="urn:xytechsystems.com/XytechAPI"
xmlns:NS1="http://schemahost.amcnetworks.com:8080/amcnesb/schemas">
but have trouble with add this namespace xmlns:xyt="urn:xytechsystems.com/XytechAPI" to the argument
I tried used xsl:namesapce, but think trouble in inherit with node
my xslt:
?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:amc="http://schemahost.amcnetworks.com:8080/amcnesb/schemas/adam"
xmlns:my="http://tempuri.org/dummy"
xmlns:i ="http://www.w3.org/2001/XMLSchema-instance"
xmlns:NS1="http://schemahost.amcnetworks.com:8080/amcnesb/schemas" exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="xml" encoding="UTF-8" version="1.0" indent="yes"/>
<xsl:element name="soapenv:Envelope" >
<xsl:namespace name="i" select="'http://www.w3.org/2001/XMLSchema-instance'"/>
<xsl:namespace name="xyt1"
select="'http://schemas.datacontract.org/2004/07/Xytech.MP.API'"/>
<xsl:namespace name="xyt" select="'urn:xytechsystems.com/XytechAPI'"/>
<xsl:element name="soapenv:Header"/>
<xsl:element name="soapenv:Body"/>
<xsl:element name="xyt:Upsert">
<xsl:element name="xyt:credentials">
</xsl:element>
<xsl:element name="xyt:arguments">
<xsl:namespace name="xyt" select="urn:xytechsystems.com/XytechAPI">
<xsl:namespace name="NS1"
select="'http://schemahost.amcnetworks.com:8080/amcnesb/schemas'"/>
my output:
<soapenv:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xyt1="http://schemas.datacontract.org/2004/07/Xytech.MP.API"
xmlns:xyt="urn:xytechsystems.com/XytechAPI"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body/>
<xyt:Upsert>
<xyt:credentials/>
<xyt:arguments xmlns:NS1="http://schemahost.amcnetworks.com:8080/amcnesb/schemas">
<xsl:namespace name="xyt" select="'urn:xytechsystems.com/XytechAPI'"/>
You've tagged your question xslt-1.0, but xsl:namespace is an XSLT 2.0 instruction.
Given that you're creating elements whose names are known statically, it's easiest to do this using literal result elements. Change:
<xsl:element name="soapenv:Envelope" >
<xsl:namespace name="i" select="'http://www.w3.org/2001/XMLSchema-instance'"/>
<xsl:namespace name="xyt1"
select="'http://schemas.datacontract.org/2004/07/Xytech.MP.API'"/>
<xsl:namespace name="xyt" select="'urn:xytechsystems.com/XytechAPI'"/>
<xsl:element name="soapenv:Header"/>
<xsl:element name="soapenv:Body"/>
<xsl:element name="xyt:Upsert">
<xsl:element name="xyt:credentials">
</xsl:element>
to
<soapenv:Envelope
xmlns:i='http://www.w3.org/2001/XMLSchema-instance'
xmlns:xyt1='http://schemas.datacontract.org/2004/07/Xytech.MP.API'
xmlns:xyt='urn:xytechsystems.com/XytechAPI'>
<soapenv:Header/>
<soapenv:Body/>
<xyt:Upsert>
<xyt:credentials/>
Anyone who has to read your code will be eternally grateful to you.
If you want XSLT to output an element like e.g.
<xyt:arguments xmlns:xyt="urn:xytechsystems.com/XytechAPI"
xmlns:NS1="http://schemahost.amcnetworks.com:8080/amcnesb/schemas">
</xyt:arguments>
then the easiest way and the straight-forwards one is to write it as a literal result element i.e.
<xyt:arguments xmlns:xyt="urn:xytechsystems.com/XytechAPI"
xmlns:NS1="http://schemahost.amcnetworks.com:8080/amcnesb/schemas">
</xyt:arguments>
All use of xsl:element is only needed if you want to compute the name and/or namespace (or at least parts of them) during the execution of the XSLT, based on some input data.
I'm agreeing with #mads-hansen's comment:
The namespace URI is already declared with prefix xyt in an ancestor element hence will not be re-declared in the child element for the same prefix. (That's the namespace-fixup mechanism in XSLT.)
You can change the parent prefixes to be xyt1 so that the xyt prefix is new in the xyt:arguments element, even though it is the same namespace URI.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:NS1="http://schemahost.amcnetworks.com:8080/amcnesb/schemas"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xyt1="urn:xytechsystems.com/XytechAPI"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="xml" encoding="UTF-8" version="1.0" indent="yes"/>
<xsl:template name="xsl:initial-template">
<xsl:element name="soapenv:Envelope" >
<xsl:element name="soapenv:Header"/>
<xsl:element name="soapenv:Body"/>
<xsl:element name="xyt1:Upsert">
<xsl:element name="xyt1:credentials" />
<xsl:element name="xyt:arguments" namespace="urn:xytechsystems.com/XytechAPI">
<xsl:namespace name="NS1" select="'http://schemahost.amcnetworks.com:8080/amcnesb/schemas'"/>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
gives
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xyt1="http://schemas.datacontract.org/2004/07/Xytech.MP.API">
<soapenv:Header/>
<soapenv:Body/>
<xyt1:Upsert xmlns:xyt1="urn:xytechsystems.com/XytechAPI">
<xyt1:credentials/>
<xyt:arguments xmlns:NS1="http://schemahost.amcnetworks.com:8080/amcnesb/schemas" xmlns:xyt="urn:xytechsystems.com/XytechAPI"/>
</xyt1:Upsert>
</soapenv:Envelope>
but I don't see why that is preferable to using the same xyt prefix everywhere in the document.
I have an incoming simple xml request below and need to transform to below SOAP message with correct name space.and in the incoming XML request the name space are not coming so while forming the SOAP message we need to take care of the name space also. Is there any XSLT code snippet which will help me to achieve that.
Note - We need to do this XSLT transformation dynamically like the incoming request can be any element like "GetImageRequest" so based on this element need to construct the name space. (probably we can keep all the name space in one xml file and need to construct the SOAP message)
Incoming XML request:
<request>
<payload>
<GetImageRequest>
<participantId>1191152220010</participantId>
<participantCode>131029</participantCode>
<groupCode>027198</groupCode>
<userType>EE</userType>
<clientName>Test</clientName>
<shoeboxID>123444</shoeboxID>
<imageID>45235</imageID>
</GetImageRequest>
</payload>
</request>
==================
Need to Construct below SOAP message with proper namespace.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
</soapenv:Header>
<soapenv:Body>
<get:GetShoeboxItemRequest xmlns:get="urn:webservice/server/mobile/shoebox/types/v1/GetShoeboxItem">
<get:participantId>1060687620010</get:participantId>
<get:participantCode>1060687620010</get:participantCode>
<get:groupCode>027198</get:groupCode>
<get:userType>EE</get:userType>
<get:clientName>Test</get:clientName>
<get:shoeboxID>123444</get:shoeboxID>
</get:GetShoeboxItemRequest>
</soapenv:Body>
</soapenv:Envelope>
Need a quick help on this . XSLT code snippet would be helpful.
This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pRequestedItemName" select="'Shoebox'"/>
<xsl:variable name="vUpper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="vLower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="vLcItemName" select=
"translate($pRequestedItemName, $vUpper, $vLower)"/>
<xsl:variable name="vDynNamespace" select=
"concat('urn:webservice/server/mobile/', $vLcItemName, '/types/v1/Get', 'Item')"/>
<xsl:template match="/">
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
</soapenv:Header>
<soapenv:Body>
<xsl:element name="get:Get{$pRequestedItemName}ItemRequest"
namespace="{$vDynNamespace}">
<xsl:apply-templates select="/*/*/GetImageRequest/node()"/>
</xsl:element>
</soapenv:Body>
</soapenv:Envelope>
</xsl:template>
<xsl:template match="*">
<xsl:element name="get:{name()}" namespace="{$vDynNamespace}">
<xsl:copy-of select="namespace::*|#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When applied on the provided source XML document:
<request>
<payload>
<GetImageRequest>
<participantId>1191152220010</participantId>
<participantCode>131029</participantCode>
<groupCode>027198</groupCode>
<userType>EE</userType>
<clientName>Test</clientName>
<shoeboxID>123444</shoeboxID>
<imageID>45235</imageID>
</GetImageRequest>
</payload>
</request>
produces the wanted, correct result:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<get:GetShoeboxItemRequest
xmlns:get="urn:webservice/server/mobile/shoebox/types/v1/GetItem">
<get:participantId>1191152220010</get:participantId>
<get:participantCode>131029</get:participantCode>
<get:groupCode>027198</get:groupCode>
<get:userType>EE</get:userType>
<get:clientName>Test</get:clientName>
<get:shoeboxID>123444</get:shoeboxID>
<get:imageID>45235</get:imageID>
</get:GetShoeboxItemRequest>
</soapenv:Body>
</soapenv:Envelope>
Explanation:
The full power of the <xsl:element> instruction is used.
Use of AVT (Attribute Value Templates)
The only variable part of the dynamic namespace should be provided as a global parameter by the invoker of the transformation.
If you need to construct a completely dynamic namespace (such as passed in a global parameter by the invoker of the transformation), see this answer.
UPDATE:
The OP clarified in a comment that he can have all request-specific namespaces collected in a separate XML document or in a global parameter.
Here is the solution to this clarified problem:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pRequestData">
<r name="GetImageRequest"
ns="urn:webservice/server/mobile/shoebox/types/v1/GetShoeboxItem"/>
<r name="SaveShoeBoxitemRequest"
ns="urn:webservice/server/mobile/shoebox/types/v1/SaveShoeboxItem"/>
<r name="SaveClaimWithReceiptRequest"
ns="urn:webservice/server/mobile/shoebox/types/v1/SaveClaimAndReceipt"/>
<r name="GetThumbNailImageRequest"
ns="urn:webservice/server/mobile/shoebox/types/v1/GetThumbnail"/>
<r name="AttachReceiptwithExistingClaimRequest"
ns="urn:webservice/server/mobile/shoebox/types/v1/AttachClaimAndReceipt"/>
</xsl:param>
<xsl:variable name="vParams" select="document('')/*/xsl:param[#name='pRequestData']"/>
<xsl:template match="/">
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
</soapenv:Header>
<soapenv:Body>
<xsl:apply-templates select="/*/*/*"/>
</soapenv:Body>
</soapenv:Envelope>
</xsl:template>
<xsl:template match="*">
<xsl:param name="pKey" select="local-name()"/>
<xsl:element name="get:{local-name()}" namespace="{$vParams/*[#name = $pKey]/#ns}">
<xsl:copy-of select="namespace::*|#*"/>
<xsl:apply-templates>
<xsl:with-param name="pKey" select="$pKey"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document (the provided one with a second, differently named child of payload):
<request>
<payload>
<GetImageRequest>
<participantId>1191152220010</participantId>
<participantCode>131029</participantCode>
<groupCode>027198</groupCode>
<userType>EE</userType>
<clientName>Test</clientName>
<shoeboxID>123444</shoeboxID>
<imageID>45235</imageID>
</GetImageRequest>
<SaveShoeBoxitemRequest>
<participantId>1191152220010</participantId>
<participantCode>131029</participantCode>
<groupCode>027198</groupCode>
<userType>EE</userType>
<clientName>Test</clientName>
<shoeboxID>123444</shoeboxID>
<imageID>45235</imageID>
</SaveShoeBoxitemRequest>
</payload>
</request>
The wanted, correct (unlike "solutions" in other answers) result is produced:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<get:GetImageRequest
xmlns:get="urn:webservice/server/mobile/shoebox/types/v1/GetShoeboxItem">
<get:participantId>1191152220010</get:participantId>
<get:participantCode>131029</get:participantCode>
<get:groupCode>027198</get:groupCode>
<get:userType>EE</get:userType>
<get:clientName>Test</get:clientName>
<get:shoeboxID>123444</get:shoeboxID>
<get:imageID>45235</get:imageID>
</get:GetImageRequest>
<get:SaveShoeBoxitemRequest
xmlns:get="urn:webservice/server/mobile/shoebox/types/v1/SaveShoeboxItem">
<get:participantId>1191152220010</get:participantId>
<get:participantCode>131029</get:participantCode>
<get:groupCode>027198</get:groupCode>
<get:userType>EE</get:userType>
<get:clientName>Test</get:clientName>
<get:shoeboxID>123444</get:shoeboxID>
<get:imageID>45235</get:imageID>
</get:SaveShoeBoxitemRequest>
</soapenv:Body>
</soapenv:Envelope>
So if i know what all namespace for those 5 elements i can put in one
xml file and can retrieve from that XML file. or as you mentioned can
define those name space in global document.
If you have a map of which namespace to use with which "root" element name, you could just as well keep it in the stylesheet itself.
Here's an example (using made-up namespaces, since you did not specify your own):
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://www.example.com/my"
exclude-result-prefixes="my">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<my:ns-map>
<ns root="GetImageRequest">urn:webservice/a/b/GetShoeboxItem</ns>
<ns root="SaveShoeBoxitemRequest">urn:webservice/c/d/SaveShoeBoxitemRequest</ns>
<ns root="SaveClaimWithReceiptRequest">urn:webservice/e/f/SaveClaimWithReceiptRequest</ns>
<ns root="GetThumbNailImageRequest">urn:webservice/g/h/GetThumbNailImageRequest</ns>
<ns root="AttachReceiptwithExistingClaimRequest">urn:webservice/i/k/AttachReceiptwithExistingClaimRequest</ns>
</my:ns-map>
<xsl:variable name="root" select="name(/request/payload/*)"/>
<xsl:variable name="ns" select="document('')/xsl:stylesheet/my:ns-map/ns[#root=$root]"/>
<xsl:template match="/">
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<xsl:apply-templates select="request/payload/*" />
</soapenv:Body>
</soapenv:Envelope>
</xsl:template>
<xsl:template match="*" >
<xsl:element name="get:{local-name()}" namespace="{$ns}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The below xsl works fine if I do not bring in the "other_location_postal_code" field, which is commented here.
This is because there are multiple records if I bring in that field.
How can I have this xsl evaluate each record so it would write this record twice, once for the one "other location postal code" and the other?
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:e="http://www.taleo.com/ws/tee800/2009/01" xmlns:fct="http://www.taleo.com/xsl_functions" exclude-result-prefixes="e fct">
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="no"/>
<xsl:param name="OUTBOUND_FOLDER"/>
<xsl:template match="/">
<source>
<xsl:apply-templates select="//e:Requisition"/>
</source>
</xsl:template>
<xsl:template match="e:Requisition">
<xsl:variable name="job_id" select="e:ContestNumber"/>
<xsl:variable name="other_location_postal_code" select="e:JobInformation/e:JobInformation/e:OtherLocations/e:Location/e:NetworkLocation/e:NetworkLocation/e:ZipCode"/>
<job>
<job_id>
<xsl:value-of select="concat('<','![CDATA[',$job_id,']]','>')"/>
</job_id>
<other_location_postal_code>
<xsl:value-of select="concat('![CDATA[',$other_location_postal_code,']]')"/>
</other_location_postal_code>
</job>
</xsl:template>
</xsl:stylesheet>
I want it to come out like so:
<?xml version="1.0" encoding="UTF-8"?>
<source>
<job>
<job_id><![CDATA[15000005]]></job_id>
<other_location_postal_code><![CDATA[77382]]></other_location_postal_code>
</job>
<job>
<job_id><![CDATA[15000005]]></job_id>
<other_location_postal_code><![CDATA[37567]]></other_location_postal_code>
</job>
</source>
The initial XML looks like so:
<?xml version="1.0" encoding="UTF-8"?>
-<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
-<soapenv:Body>
-<ns1:getDocumentByKeyResponse xmlns:ns1="http://www.taleo.com/ws/integration/toolkit/2005/07" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
-<Document xmlns="http://www.taleo.com/ws/integration/toolkit/2005/07">
-<Attributes>
<Attribute name="count">1</Attribute>
<Attribute name="duration">0:00:00.088</Attribute>
<Attribute name="entity">Requisition</Attribute>
<Attribute name="mode">T-XML</Attribute>
<Attribute name="version">http://www.taleo.com/ws/tee800/2009/01</Attribute>
</Attributes>
-<Content>
-<ExportTXML xmlns="http://www.taleo.com/ws/integration/toolkit/2005/07" xmlns:e="http://www.taleo.com/ws/tee800/2009/01">
-<e:Requisition>
<e:ContestNumber>15000005</e:ContestNumber>
-<e:JobInformation>
-<e:JobInformation>
-<e:OtherLocations>
-<e:Location>
<e:ZipCode>77002</e:ZipCode>
</e:Location>
-<e:Location>
<e:ZipCode>77050</e:ZipCode>
</e:Location>
</e:OtherLocations>
</e:JobInformation>
</e:JobInformation>
</e:Requisition>
</ExportTXML>
</Content>
</Document>
</ns1:getDocumentByKeyResponse>
</soapenv:Body>
</soapenv:Envelope>
The error message simply says that an argument to the concat function is a sequence of more than one node. So some of your variable selects two or more nodes and concat expects as single node for each of its arguments. You will need to decide what you want to output, either only the first node with $var[1] or the concatenation with string-join($var, ' ').
Note that you don't need all those attempts to output CDATA section, you can tell the XSLT processor the cdata-section-elements on the xsl:output direction.
As you have now posted more details here is one suggestion to map each ZipCode element to job element:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:e="http://www.taleo.com/ws/tee800/2009/01"
exclude-result-prefixes="e">
<xsl:output indent="yes" cdata-section-elements="job_id other_location_postal_code"/>
<xsl:template match="/">
<source>
<xsl:apply-templates select="//e:OtherLocations/e:Location/e:ZipCode"/>
</source>
</xsl:template>
<xsl:template match="e:ZipCode">
<xsl:apply-templates select="ancestor::e:Requisition">
<xsl:with-param name="zc" select="current()"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="e:Requisition">
<xsl:param name="zc"/>
<job>
<job_id><xsl:value-of select="e:ContestNumber"/></job_id>
<other_location_postal_code><xsl:value-of select="$zc"/></other_location_postal_code>
</job>
</xsl:template>
</xsl:stylesheet>
I have the following xml:
<RootNode xmlns="http://someurl/path/path/path">
<Child1>
<GrandChild1>Value</GrandChild1>
<!-- Lots more elements in here-->
</Child1>
</RootNode>
I have the following xslt:
<xsl:stylesheet version="1.0" xmlns="http://someurl/path/path/path" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<xsl:for-each select="RootNode/Child1">
<NewNodeNameHere>
<xsl:value-of select="GrandChild1"/>
</NewNodeNameHere>
<!-- lots of value-of tags in here -->
</xsl:for-each>
</NewChild1>
</NewRootNode >
</xsl:template>
</xsl:stylesheet>
The problem: this is the my result:
<?xml version="1.0" encoding="utf-8"?>
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1 />
</NewRootNode>
I am expecting to see:
<?xml version="1.0" encoding="utf-8"?>
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<NewNodeNameHere>Value</NewNodeNameHere>
<!-- Other new elements with values from the xml file -->
</NewChild1>
</NewRootNode>
I am missing of the information inside of NewChild1 that should be there.
I think my for-each select is correct, so the only thing I can think of is that there is a problem with the namespace in the Xml and the namespace in the xslt. Can anybody see what I'm doing wrong?
The problem is caused by the namespaces.
Since the xml defines xmlns="http://someurl/path/path/path", it is not in the default namespace anymore.
You can define that namespace with an name like xmlns:ns="http://someurl/path/path/path" in the xsl, and then use that name in the XPath expression.
The following works for me:
<xsl:stylesheet version="1.0" xmlns:ns="http://someurl/path/path/path" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<xsl:for-each select="ns:RootNode/ns:Child1">
<NewNodeNameHere>
<xsl:value-of select="ns:GrandChild1"/>
</NewNodeNameHere>
<!-- lots of value-of tags in here -->
</xsl:for-each>
</NewChild1>
</NewRootNode >
</xsl:template>
</xsl:stylesheet>
The stylesheet namespace should be http://www.w3.org/1999/XSL/Transform instead of http://someurl/path/path/path.
Also, since the input XML uses a namespace all your XPath expressions should be namespace-qualified:
<xsl:template match="/" xmlns:ns1="http://someurl/path/path/path">
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<xsl:for-each select="ns1:RootNode/ns1:Child1">
<NewNodeNameHere>
<xsl:value-of select="ns1:GrandChild1"/>
</NewNodeNameHere>
<!-- lots of value-of tags in here -->
</xsl:for-each>
</NewChild1>
</NewRootNode>
</xsl:template>
I have looked at XSL xsl:template match="/" but the match pattern that triggered my question is not mentioned there.
I have a rather complex XML structure:
<?xml version="1.0" encoding="UTF-8"?>
<MATERIAL_DATA>
<LOG>
<USER>Peter</USER>
<DATE>2011-02-18</DATE>
<MATERIALS>
<item>
<MATNR>636207</MATNR>
<TEXTS>
<item>
<TEXT>granola bar 40gx24</TEXT>
</item>
</TEXTS>
<PRICES>
<item>
<MATNR>636207</MATNR>
<COST>125.78</COST>
</item>
</PRICES>
<SALESPRICES>
<item>
<B01>
<MATNR>636207</MATNR>
<CURR>CZK</CURR>
<DATBI>9999-12-31</DATBI>
<DATAB>2010-10-05</DATAB>
</B01>
<B02>
<item>
<PRICE>477.60</PRICE>
<KUNNR>234567</KUNNR>
</item>
</B02>
</item>
</SALESPRICES>
</item>
</MATERIALS>
</LOG>
</MATERIAL_DATA>
Now if I apply the following XSLT, my output looks correct:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:template match="node() | #*">
<xsl:apply-templates select="* | #*" />
</xsl:template>
<xsl:template match="B02">
<xsl:element name="Mi">
<xsl:value-of select="item/KUNNR"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I get the output:
<?xml version="1.0" encoding="UTF-8"?>
<Mi>234567</Mi>
But if I apply the XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:template match="/*">
<xsl:element name="MenuItems">
<xsl:apply-templates select="LOG/MATERIALS/item/SALESPRICES/item"/>
</xsl:element>
</xsl:template>
<xsl:template match="B02">
<xsl:element name="Mi">
<xsl:value-of select="item/KUNNR"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
the output looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<MenuItems>
636207
CZK
9999-12-31
2010-10-05
<Mi>234567</Mi>
</MenuItems>
All values from the element <B01> are in the output! But why - I am not matching <B01>!?
How does
<xsl:template match="node() | #*">
<xsl:apply-templates select="* | #*" />
</xsl:template>
make the output come out correctly? All I am doing with this is match all nodes or attributes and apply-templates to everything or all attributes.
But in my opinion it should not make a difference to when I exactly match <B01>!
Why is this happening?
XSLT includes the following default templates (among others):
<!-- applies to both element nodes and the root node -->
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
<!-- copies values of text and attribute nodes through -->
<xsl:template match="text()|#*">
<xsl:value-of select="."/>
</xsl:template>
In your first stylesheet you're implicitly matching all text nodes with node(), thus overriding the default action. Then, in the B2 template, you output your target value and apply no further templates, which stops processing.
In the second stylesheet, you explicitly apply templates to all children of LOG/MATERIALS/item/SALESPRICES/item, causing the default templates to process the nodes you don't explicitly handle. Because you explicitly handle B2 without applying templates to its children, the default templates are never invoked for those nodes. But the default templates are applied to the children of B1.
Adding the following template to your second stylesheet would override the default action for text nodes:
<xsl:template match="text()|#*"></xsl:template>
With the following result:
<?xml version="1.0" encoding="UTF-8"?>
<MenuItems><Mi>234567</Mi></MenuItems>
More:
http://www.w3.org/TR/xslt#built-in-rule
Looks like you are running into the built in template rules.
Specifically the text rule - this will copy text nodes if not overridden.