Trouble with inherent namespace XSLT - xslt

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.

Related

XSLT: Remove namespace prefix from elements after first tag<POSLog> in a xml

we have a requirement to remove the prefix <dsr:LineItemItems>6</dsr:LineItemItems> in XML content, when we used the below code it was successfully removing the prefix "dsr" from xml elements but we need the xmlns namespaces in the output xml payload which is present in the POSLog tag.
Need all the namespaces/content present in the input first tag <POSLog> in the output xml too.
Input xml:
<?xml version="1.0" encoding="utf-8"?>
<POSLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dsr="http://www.dsr.com/rsd/tlog/markup/poslog" xsi:schemaLocation="http://www.nrf-arts.org/IXRetail/namespace/ POSLog.xsd http://www.dsr.com/rsd/tlog/markup/poslog DSRPOSLog.xsd" xmlns="http://www.nrf-arts.org/IXRetail/namespace/">
<dsr:LineItemItems>6</dsr:LineItemItems>
XSL code:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dsr="http://www.dsr.com/rsd/tlog/markup/poslog" xsi:schemaLocation="http://www.nrf-arts.org/IXRetail/namespace/ POSLog.xsd
http://www.dsr.com/rsd/tlog/markup/poslog DSRPOSLog.xsd" xmlns="http://www.nrf-arts.org/IXRetail/namespace/" >
<xsl:output method="xml" version="1.0" encoding="UTF-8" />
<xsl:template match="*">
<xsl:element name="{local-name()}" >
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Output xml by the xsl code:
<?xml version="1.0" encoding="UTF-8"?>
<POSLog schemaLocation="http://www.nrf-arts.org/IXRetail/namespace/ POSLog.xsd http://www.dsr.com/rsd/tlog/markup/poslog DSRPOSLog.xsd">
<LineItemItems>6</LineItemItems>
Need the output as below with all the namespaces without change in the First Tag
<?xml version="1.0" encoding="UTF-8"?>
<POSLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dsr="http://www.dsr.com/rsd/tlog/markup/poslog" xsi:schemaLocation="http://www.nrf-arts.org/IXRetail/namespace/ POSLog.xsd http://www.dsr.com/rsd/tlog/markup/poslog DSRPOSLog.xsd" xmlns="http://www.nrf-arts.org/IXRetail/namespace/">
<LineItemItems>6</LineItemItems>
Thanks,
Ravi
Add an extra template to match the document element and copy it, rather than reconstructing a new element without namespaces. And in the existing template matching elements, add an attribute to indicate the namespace in the xsl:element constructor.
The following stylesheet ensures that all elements are bound to the namespace http://www.nrf-arts.org/IXRetail/namespace/ and that the namespace prefix dsr is retained:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dsr="http://www.dsr.com/rsd/tlog/markup/poslog" xsi:schemaLocation="http://www.nrf-arts.org/IXRetail/namespace/ POSLog.xsd
http://www.dsr.com/rsd/tlog/markup/poslog DSRPOSLog.xsd" >
<xsl:output method="xml" version="1.0" encoding="UTF-8" />
<!-- copy the document element, preserving it's namespaces -->
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--create elements using the local-name, but bound to the desired namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}" namespace="http://www.nrf-arts.org/IXRetail/namespace/" >
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
There are three ways of creating an element in the result tree, and they differ in how they handle namespaces:
xsl:element creates an element with no namespaces other than those used in the element name and in the names of its attributes
xsl:copy copies all the namespaces present on the source element you are copying whether or not they are actually used
a literal result element (e.g. <POSLog>) copies all the namespaces that are in scope for the element in the stylesheet, other than namespaces excluded using exclude-result-prefixes
So you want to create the outermost element of your result document using either xsl:copy or a literal result element, not using xsl:element.
The other problem you have is that you have renamed xsi:schemaLocation as schemaLocation. That happened because you used <xsl:attribute name="{local-name()}"/>. It would be better here to simply use xsl:copy-of to copy the attribute.

How to create template to match based upon an XSLT parameter

I'm trying to create a standard-use XSLT that will perform a given task based upon a user-provided XPATH expression as an XSLT parameter.
That is, I need something like this:
<xsl:template match="$paramContainingXPATH">
<!-- perform the task on the node(s) in the given xpath -->
</xsl:template>
For example, suppose I have some XML:
<xml>
<nodeA>whatever</nodeA>
<nodeB>whatever</nodeB>
<nodeC>whatever</nodeC>
<nodeD>whatever</nodeD>
<nodeE>whatever</nodeE>
</xml>
The XSLT needs to transform just a node or nodes matching a provided XPATH expression. So, if the xslt parameter is "/xml/nodeC", it processes nodeC. If the xslt parameter is "*[local-name() = 'nodeC' or local-name() = 'nodeE']", it processes nodeC and nodeE.
This should work for absolutely any XML message. That is, the XSLT cannot have any direct knowledge of the content of the XML. So, it could be a raw XML, or a SOAP Envelope.
I was guessing I might need to grab all the nodes matching the xpath, and then looping over them calling a named template, and using the standard identity template for all other nodes.
All advice is appreciated.
If you really need that feature with XSLT 1.0 or 2.0 then I think you should consider writing one stylesheet that takes that string parameter with the XPath expression and then simply generates the code of a second stylesheet where the XPath expression is used as a match pattern and the other needed templates like the identity template are included statically. Dynamic XPath evaluation is only available in XSLT 3.0 or in earlier versions as a proprietary extension mechanism.
You cannot match a template using a parameter - but you can traverse the tree and compare the path of each node with the given path. Here's a simple example:
XSLT 1.0
<?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" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="path" select="'/world/America/USA/California'"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="*"/>
</root>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="path-to-me">
<xsl:for-each select="ancestor-or-self::node()">
<xsl:value-of select="name()" />
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:if test="$path=$path-to-me">
<xsl:call-template name="action"/>
</xsl:if>
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template name="action">
<return>
<xsl:value-of select="." />
</return>
</xsl:template>
</xsl:stylesheet>
Applied to a slightly more ambitious test input of:
<world>
<Europe>
<Germany>1</Germany>
<France>2</France>
<Italy>3</Italy>
</Europe>
<America>
<USA>
<NewYork>4</NewYork>
<California>5</California>
</USA>
<Canada>6</Canada>
</America>
</world>
the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<return>5</return>
</root>
This could be made more efficient by passing the accumulated path as a parameter of the recursive template, so that each node needs only to add its own name to the chain.
Note:
The given path must be absolute;
Predicates (including positional predicates) and attributes are not implemented in this. They probably could be, with a bit more effort;
Namespaces are ignored (I don't see how you could pass an XPath as a parameter and include namespaces anyway).
If your processor supports an evaluate() extension function, you could forgo the calculated text path and test for intersection instead.
Edit:
Here's an example using EXSLT dyn:evaluate() and set:intersection():
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dyn="http://exslt.org/dynamic"
xmlns:set="http://exslt.org/sets"
extension-element-prefixes="dyn set">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="path" select="'/world/America/USA/California'"/>
<xsl:variable name="path-set" select="dyn:evaluate($path)" />
<xsl:template match="/">
<root>
<xsl:apply-templates select="*"/>
</root>
</xsl:template>
<xsl:template match="*">
<xsl:if test="set:intersection(. , $path-set)">
<xsl:call-template name="action"/>
</xsl:if>
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template name="action">
<return>
<xsl:value-of select="." />
</return>
</xsl:template>
</xsl:stylesheet>
Note that this will also work with with paths like:
/world/America/USA/*[2]
//California
and many others that the text comparison method could not accommodate.
I'm sending the element name as a param to the XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:output method="xml"/>
<xsl:param name="user"/>
<xsl:template match="/">
<xsl:call-template name="generic" />
</xsl:template>
<xsl:template name="generic">
<count><xsl:value-of select="count(.//*[local-name()=$user])"/></count>
</xsl:template>
</xsl:stylesheet>
I hope this could help!

How too make if else check using boolen in XSLT (transunion)

I have xml like this
<fileSummary>
<fileHitIndicator>regularHit</fileHitIndicator>
<ssnMatchIndicator>noMatch</ssnMatchIndicator>
<consumerStatementIndicator>true</consumerStatementIndicator>
<market>32</market>
<submarket>QU</submarket>
<creditDataStatus>
<suppressed>false</suppressed>
<doNotPromote>
<indicator>false</indicator>
</doNotPromote>
<freeze>
<indicator>false</indicator>
</freeze>
<minor>false</minor>
<disputed>false</disputed>
</creditDataStatus>
<inFileSinceDate estimatedCentury="false" estimatedDay="false" estimatedMonth="false" estimatedYear="false">2004-02-02</inFileSinceDate>
</fileSummary>
I want to make check if indicator value is true then show some text otherwise hide it.
<freeze>
<indicator>false</indicator>
</freeze>
I am new to XSLT, please let me know.
If the description of what you want to achieve includes an "otherwise", then you're not in need of an xsl:if, but an xsl:choose.
In the example below it is crucial that the template matches the freeze element, i.e. that the context of the xsl:choose is the freeze element.
EDIT: Added a complete example based on your updated input.
Stylesheet
<?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="freeze">
<result>
<xsl:choose>
<xsl:when test="indicator = 'false'">
<xsl:text>Indicator is false!</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>Indicator is not false!</xsl:text>
</xsl:otherwise>
</xsl:choose>
</result>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="utf-8"?>
<result>Indicator is false!</result>
The stylesheet below:
<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()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="indicator">
<xsl:choose>
<xsl:when test=".='true'">
<xsl:text>some text</xsl:text>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when applied to a your input (slightly modified)
<?xml version="1.0" encoding="UTF-8"?>
<fileSummary>
<fileHitIndicator>regularHit</fileHitIndicator>
<ssnMatchIndicator>noMatch</ssnMatchIndicator>
<consumerStatementIndicator>true</consumerStatementIndicator>
<market>32</market>
<submarket>QU</submarket>
<creditDataStatus>
<suppressed>false</suppressed>
<doNotPromote>
<indicator>true</indicator>
</doNotPromote>
<freeze>
<indicator>false</indicator>
</freeze>
<minor>false</minor>
<disputed>false</disputed>
</creditDataStatus>
<inFileSinceDate estimatedCentury="false" estimatedDay="false" estimatedMonth="false" estimatedYear="false">2004-02-02</inFileSinceDate>
</fileSummary>
produces:
<?xml version="1.0" encoding="utf-8"?><fileSummary>
<fileHitIndicator>regularHit</fileHitIndicator>
<ssnMatchIndicator>noMatch</ssnMatchIndicator>
<consumerStatementIndicator>true</consumerStatementIndicator>
<market>32</market>
<submarket>QU</submarket>
<creditDataStatus>
<suppressed>false</suppressed>
<doNotPromote>
some text
</doNotPromote>
<freeze>
</freeze>
<minor>false</minor>
<disputed>false</disputed>
</creditDataStatus>
<inFileSinceDate estimatedCentury="false" estimatedDay="false" estimatedMonth="false" estimatedYear="false">2004-02-02</inFileSinceDate>
</fileSummary>

Multiple Namespaces on an element with XSLT 1.0

I am using Microsoft's XSLT processor (1.0 only)
XML opening lines:
<?xml version="1.0" encoding="utf-8"?>
<Header xmlns="http:\\OldNameSpace.com">
<Detail>
Have the following XSLT template to pick up the <Header> element of my document and change its namespace.
<xsl:template match="*">
<xsl:element name="{name()}" xmlns="http:\\NewNameSpace.com">
<xsl:copy-of select="#*"/>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
Which turns <Header xmlns="http:\\OldNameSpace.com"> Into <Header xmlns="http:\\NewNameSpace.com">
However I now need to add a second namespace to this so that I get the following output:
<Header xmlns="NewNameSpace.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
I have tried using:
<xsl:template match="*">
<xsl:element name="{name()}" xmlns="NewNameSpace.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:copy-of select="#*"/>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
However I still only get the same output as the original XSLT template.
Can anyone enlighten to me as to why this is?
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:old="http:\\OldNameSpace.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="old xsi">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pNewNamespace" select="'http:\\NewNameSpace.com'"/>
<xsl:variable name="vXsi" select="document('')/*/namespace::*[name()='xsi']"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="old:*">
<xsl:element name="{local-name()}" namespace="{$pNewNamespace}">
<xsl:copy-of select="$vXsi"/>
<xsl:copy-of select="#*"/>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document:
<Header xmlns="http:\\OldNameSpace.com">
<Detail/>
</Header>
produces (what I guess is) the wanted, correct result:
<Header xmlns="http:\\NewNameSpace.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Detail/>
</Header>
xsl:element (unlike literal result elements) does not copy all in scope namespaces to the result, just the namespace required for the element name 9either implicitly from its name or as specified with the namespace argument).
xslt2 adds an xsl:namespace instruction for this case but in xslt1 the easiest thing to do is
where
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
somewhere on an ancestor (eg on xsl:stylesheet.)
that will add a spurious xsi:tmp="" to the output but also then a namespace declaration,
If you actually need an attribute in this namespace eg xsi:type use that instead of tmp in the above and you are done. If you don't mind the extra, possibly invalid attribute in the xsi namespace you are done. Otherwise do the above in a variable, then use msxsl:node-set to query in to the variable and remove the spurious extra attribute.
If you know statically what namespace you want to generate, then the easiest way to do it in XSLT 1.0 is using xsl:copy-of. Create a source document <dummy xmlns:xsi="http://whatever"/>,
and then do <xsl:copy-of select="document('dummy.xml')/*/namespace::xsi"/> inside your call of xsl:element.

How to copy a certain node (with children) from a XML with XSLT in Biztalk specifying a custom namespace?

I need to copy a subnode from a XML to a certain node of a new XML in a Biztalk Map using XSLT.
Consider the following input XML:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:root xmlns:ns0="http://not/useful/data/">
<ns0:notuseful>
<ns0:foo></ns0:foo>
<ns0:foo2></ns0:foo2>
<ns0:blabla></ns0:blabla>
</ns0:notuseful>
<ns0:data>
<ns1:usefulDataList xmlns:ns1="http://useful/data/">
<ns1:usefulData>
<ns1:usefulChild1></ns1:usefulChild1>
<ns1:usefulChild2></ns1:usefulChild2>
<ns1:usefulChild3></ns1:usefulChild3>
<ns1:usefulChild4></ns1:usefulChild4>
<ns1:usefulChild5></ns1:usefulChild5>
</ns1:usefulData>
</ns1:usefulDataList>
</ns0:data>
<ns0:root>
What I need is to extract the node called "usefulDataList", so I need to copy it in a new XML like this one:
<?xml version="1.0" encoding="UTF-8"?>
<ns2:root2 xmln:ns2="http://new/xml">
<ns2:blabla>
<ns2:stuff />
</ns2:blabla>
<ns2:data>
<ns2:usefulDataList>
<ns2:usefulData>
<ns2:usefulChild1></ns2:usefulChild1>
<ns2:usefulChild2></ns2:usefulChild2>
<ns2:usefulChild3></ns2:usefulChild3>
<ns2:usefulChild4></ns2:usefulChild4>
<ns2:usefulChild5></ns2:usefulChild5>
</ns2:usefulData>
</ns2:usefulDataList>
</ns2:data>
</ns2:root2>
This should be done inside a Biztalk Functoid, as you see namespaces from source and target are diferent.
I'm an absolute beginner with XSLT, and I've been doing some tests, but I've something wrong with my XSLT expressions:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns2="http://new/xml">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template name="testTemplate" match="//*[local-name() = 'usefulDataList ']">
<xsl:element name="ns0:usefulDataList " namespace="">
<xsl:apply-templates mode="copy-no-ns" select="usefulDataList"/>
</xsl:element>
</xsl:template>
<xsl:template mode="copy-no-ns" match="*">
<xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-no-ns"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I'd appreciate any tip, with XSLT or Biztalk mapper. I don't like linking a huge amount of fields one by one if I can solve it with a XSLT expression.
Greetings.
Beware you had a space in *[local-name() = 'usefulDataList ']" so that would never match. this works:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns2="http://new/xml">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<ns2:root>
<ns2:blabla>
<ns2:stuff />
</ns2:blabla>
<ns2:data>
<xsl:apply-templates mode="copy-no-ns" select="//*[local-name() = 'usefulDataList']"/>
</ns2:data>
</ns2:root>
</xsl:template>
<xsl:template mode="copy-no-ns" match="*">
<xsl:element name="ns2:{local-name(.)}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-no-ns"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>