I only have access to xpath 1.0 commands and functions. I need to move the namespace declaration from the root node to a child node where that namespace starts to be used.
Source XML:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Accounts xmlns:test="http:example.com/test1">
<ParentAccount>10113146</ParentAccount>
<test1>test1</test1>
<test2>test2</test2>
<test:Siblings>
<test:CustomerNumber>10113146</test:CustomerNumber>
<test:CustomerNumber>120051520</test:CustomerNumber>
</test:Siblings>
</Accounts>
Desired XML:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Accounts x>
<ParentAccount>10113146</ParentAccount>
<test1>test1</test1>
<test2>test2</test2>
<test:Siblings xmlns:test="http:example.com/test1">
<test:CustomerNumber>10113146</test:CustomerNumber>
<test:CustomerNumber>120051520</test:CustomerNumber>
</test:Siblings>
</Accounts>
Any bright ideas?
Here's one way to do it.
When this XSLT:
<?xml version="1.0"?>
<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="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Accounts">
<Accounts>
<xsl:apply-templates />
</Accounts>
</xsl:template>
</xsl:stylesheet>
...is applied against the provided XML:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Accounts xmlns:test="http:example.com/test1">
<ParentAccount>10113146</ParentAccount>
<test1>test1</test1>
<test2>test2</test2>
<test:Siblings>
<test:CustomerNumber>10113146</test:CustomerNumber>
<test:CustomerNumber>120051520</test:CustomerNumber>
</test:Siblings>
</Accounts>
...the wanted result is produced:
<?xml version="1.0"?>
<Accounts>
<ParentAccount>10113146</ParentAccount>
<test1>test1</test1>
<test2>test2</test2>
<test:Siblings xmlns:test="http:example.com/test1">
<test:CustomerNumber>10113146</test:CustomerNumber>
<test:CustomerNumber>120051520</test:CustomerNumber>
</test:Siblings>
</Accounts>
Explanation:
The explanation behind why this works starts with a section from the Namespaces in XML 1.0 spec:
The scope of a namespace declaration declaring a prefix extends from
the beginning of the start-tag in which it appears to the end of the
corresponding end-tag, excluding the scope of any inner declarations
with the same NSAttName part. In the case of an empty tag, the scope
is the tag itself.
Such a namespace declaration applies to all element and attribute
names within its scope whose prefix matches that specified in the
declaration.
In a nutshell, this means that when a namespace is declared on an element, it is, in effect, defined for use on all elements underneath that original scope. Additionally, should a namespace be used on an element without first being defined elsewhere, the appropriate definition occurs on that element.
So, using your document and my XSLT, let's see how this plays out:
The first template - The Identity Template - copies all nodes and attributes as-is from the source XML to the result XML.
The second template replaces the original <Accounts> element with a new one; incidentally, this new <Accounts> element does not define the http:example.com/test1 namespace. Finally, this template applies templates to all child elements of <Accounts>.
When the processor reaches <test:Siblings>, it sees a namespace that, although present in the original XML, has not been properly defined in the result document. As such, this definition is added to <test:Siblings>.
Related
I have XML which contains multiple elements:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<element>
<ip>192.168.188.101</ip>
</element>
<element>
<ip>192.168.188.100</ip>
</element>
</data>
I want to make it to this structure:
<SYNCDW>
<CIDWSet>
<CI>
<CINUM>192.168.188.101</CINUM>
</CI>
<CI>
<CINUM>192.168.188.100</CINUM>
</CI>
</CIDWSet>
</SYNCDW>
But always one element is processed, the first one, although I have for-each.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<SyncCIDW xmlns="http://www.ibm.com/maximo">
<xsl:attribute name="xsi:schemaLocation" namespace="http://www.w3.org/2001/XMLSchema-instance">http://www.ibm.com/maximo</xsl:attribute>
<CIDWSet>
<xsl:for-each select="*[local-name()='data' and namespace-uri()='']/*[local-name()='element' and namespace-uri()='']">
<CI>
<CINUM>
<xsl:value-of select="string(*[local-name()='data' and namespace-uri()='']/*[local-name()='element' and namespace-uri()='']/*[local-name()='ip' and namespace-uri()=''])"/>
</CINUM>
</CI>
</xsl:for-each>
</CIDWSet>
</SyncCIDW>
</xsl:template>
</xsl:stylesheet>
Why I am not getting processed all other elements but only the first one?
Thank you in advance for help
A couple of things:
Inside of the xsl:for-each, the context switches to the element that you are iterating over (in this case, /data/element), so to select the ip element your XPath is relative from the /data/element that you are "standing on" and would simply be ip. The way you had it, it would be looking for /data/element/data/element/ip inside of the xsl:for-each and would not produce any values inside of the <CINUM>.
You can simplify your XPath expressions. If the elements you are addressing are not bound to a namespace, rather than a generic match on any element and a predicate matching the local-name() and namespace-uri()='', just use the simplified XPath data/element.
If you are creating a statically known attribute xsi:schemaLocation with a statically known value, just use the literal declaration inside of the SyncCIDW element literal.
If you are using xsl:value-of it will yield the string value of the selected node. There is no need for the string() function.
Changes applied to your stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<SyncCIDW xmlns="http://www.ibm.com/maximo"
xsi:schemaLocation="http://www.ibm.com/maximo">
<CIDWSet>
<xsl:for-each select="data/element">
<CI>
<CINUM>
<xsl:value-of select="ip"/>
</CINUM>
</CI>
</xsl:for-each>
</CIDWSet>
</SyncCIDW>
</xsl:template>
</xsl:stylesheet>
I have a incoming SOAP message like below:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns3:GetImageResponse xmlns="urn:webservice/server/mobile/shoebox/types/v1/CustomImage" xmlns:ns2="urn:webservice/server/mobile/shoebox/types/v1/common/ShoeboxCommonArtifacts" xmlns:ns3="urn:webservice/server/mobile/shoebox/types/v1/Image" xmlns:ns4="urn:webservice/server/mobile/shoebox/types/v1/common/exceptions" xmlns:ns5="urn:webservice/server/mobile/shoebox/types/v1/GetThumbnailImage">
<ns3:returnCode>105</ns3:returnCode>
<ns3:errorText>Invalid Participant code/id.</ns3:errorText>
<ns3:shoeboxImage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</ns3:GetImageResponse>
</soap:Body>
</soap:Envelope>
Need to transform into another simple XML like Below: (Constraint - The root element under BODY of SOAP envelope (ex- if "GetImageResponse" coming we need construct "GetImage" element in output XML) and it is not constant it can be any element So need to construct XML based on the root element under BODY , Ex shown below)
<?xml version="1.0" encoding="UTF-8"?>
<tns:GetImage xmlns:bons1="http://highmark.com/rbssvc/messages/common" xmlns:tns="http://www.example.org/GetImageResponseMessage/" xmlns:tns1="http://www.example.org/GetImageResponse/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/GetImageResponseMessage/ ../xsd/GetImageResponseMessage.xsd ">
<payload>
<returnCode>returnCode</returnCode>
<errorText>errorText</errorText>
<imageData>MA==</imageData>
</payload>
I am using this below XSLT to transform:
<xsl:stylesheet extension-element-prefixes="dp" exclude-result-prefixes="dp regex" version="1.0" xmlns:dp="http://www.datapower.com/extensions" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:regex="http://exslt.org/regular-expressions">
<xsl:template match="/">
<GetImage>
<xsl:element name="{'Payload'}">
<xsl:copy-of select="/*/*[local-name()='Body']/*[local-name()='GetImageResponse']/*"/>
</xsl:element>
</GetImage>
</xsl:template>
</xsl:stylesheet>
But i am not getting the desired XML output shown above
The out put which i am getting is :
<GetImageResponse>
<Payload>
<ns3:returnCode xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns5="urn:webservice/server/mobile/shoebox/types/v1/GetThumbnailImage" xmlns:ns4="urn:webservice/server/mobile/shoebox/types/v1/common/exceptions" xmlns:ns3="urn:webservice/server/mobile/shoebox/types/v1/Image" xmlns:ns2="urn:webservice/server/mobile/shoebox/types/v1/common/ShoeboxCommonArtifacts" xmlns="urn:webservice/server/mobile/shoebox/types/v1/CustomImage">105</ns3:returnCode>
<ns3:errorText xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns5="urn:webservice/server/mobile/shoebox/types/v1/GetThumbnailImage" xmlns:ns4="urn:webservice/server/mobile/shoebox/types/v1/common/exceptions" xmlns:ns3="urn:webservice/server/mobile/shoebox/types/v1/Image" xmlns:ns2="urn:webservice/server/mobile/shoebox/types/v1/common/ShoeboxCommonArtifacts" xmlns="urn:webservice/server/mobile/shoebox/types/v1/CustomImage">Invalid Participant code/id.</ns3:errorText>
<ns3:shoeboxImage xsi:nil="true" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns5="urn:webservice/server/mobile/shoebox/types/v1/GetThumbnailImage" xmlns:ns4="urn:webservice/server/mobile/shoebox/types/v1/common/exceptions" xmlns:ns3="urn:webservice/server/mobile/shoebox/types/v1/Image" xmlns:ns2="urn:webservice/server/mobile/shoebox/types/v1/common/ShoeboxCommonArtifacts" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:webservice/server/mobile/shoebox/types/v1/CustomImage"/>
</Payload>
</GetImageResponse>
The issue is here like i am not able to copy the name space of incoming soap message Like element "GetImageResponse" and some extra namespace are also coming for element "payload" .enter code here
Any idea how i can transform from SOAP message to the desired XML output.
Quick reply appreciated.
Regards
Rj
Well the redundant xmlns declarations are visual noise, but functionally not a problem. However you can fix this by making sure you set the necessary xmlns declarations on the root element of the result that you generate. In your case this is: <GetImage>.
You'll note that in your stylesheet the GetImage element is in the default namespace of the document which contains the XSLT stylesheet, which is also not specified.
Example:
<!--
namespace GetImage and
set up additional namespace mapping for ns5 prefix
for any copied elements which may be injected
-->
<tns:GetImage xmlns:tns="tns-uri" xmlns:ns5="ns5-uri">
<!-- more stuff here -->
</tns:GetImage>
Next up your <xsl:element name="{'Payload'}"> call also does not inject a namespace. You can either use the namespace attribute of xsl:element to associate the generated element with the desired namespace (URI), or you can use the syntax {prefix}:{local-name} in the name attribute and add the appropriate xmlns:prefix declarations.
Examples:
<xsl:element name="foo" namespace="bar"/>
<!-- needs xmlns:ns declaration -->
<xsl:element name="ns:foo"/>
<!-- substantially the same, using 'expressions' instead of 'literals' -->
<xsl:element name="{$nsPrefix}:{local-name()}">
Your question is not entirely clear. I think you want to do something like this:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns3="urn:webservice/server/mobile/shoebox/types/v1/Image"
xmlns:my="http://www.example.com/my"
exclude-result-prefixes="soap ns3 my">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<my:ns-holder xmlns:bons1="http://highmark.com/rbssvc/messages/common" xmlns:tns1="http://www.example.org/GetImageResponse/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/GetImageResponseMessage/ ../xsd/GetImageResponseMessage.xsd "/>
<xsl:template match="/soap:Envelope/soap:Body/*">
<xsl:element name="tns:{local-name()}" xmlns:tns="http://www.example.org/GetImageResponseMessage/">
<xsl:copy-of select="document('')/xsl:stylesheet/my:ns-holder/namespace::*"/>
<payload>
<returnCode>
<xsl:value-of select="ns3:returnCode" />
</returnCode>
<errorText>
<xsl:value-of select="ns3:errorText" />
</errorText>
<imageData>MA==</imageData>
</payload>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
What this does is:
Create a root element whose local name is the same as the name of
the child of soap:Body, assign it a tns: prefix and bind the
prefix to the "http://www.example.org/GetImageResponseMessage/'
namespace. Note that this assumes there will be only one such child.
Add a bunch of namespaces to the above root element. Note that these
namespaces are not actually used in the result - and as such are
entirely redundant.
Create the payload and its value nodes. Note that you cannot use
xsl:copy here, because that would also copy the original node's
namespace (ns3 in this example).
Applied to your input example, the result here will be:
<?xml version="1.0" encoding="UTF-8"?>
<tns:GetImageResponse xmlns:tns="http://www.example.org/GetImageResponseMessage/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns3="urn:webservice/server/mobile/shoebox/types/v1/Image" xmlns:my="http://www.example.com/my" xmlns:bons1="http://highmark.com/rbssvc/messages/common" xmlns:tns1="http://www.example.org/GetImageResponse/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<payload>
<returnCode>105</returnCode>
<errorText>Invalid Participant code/id.</errorText>
<imageData>MA==</imageData>
</payload>
</tns:GetImageResponse>
I did not understand:
How exactly should GetImageResponse be tranformed into GetImage;
Where is the imageData value ("MA==") supposed to come from.
I have the following xml and want the output to not contain the xml declaration
i.e.
FROM
<?xml version="1.0" encoding="UTF-8"?>
<tns:MFTRNS xmlns:tns="MFTRNS" recordState="New" msgVersion="13.0">
<OSCONO>100</OSCONO>
<OSINOU>1</OSINOU>
<OSDLIX>155379</OSDLIX>
<OSPANR>AAG44780</OSPANR>
<OSWHLO>AAG</OSWHLO>
</tns:MFTRNS>
TO
<tns:MFTRNS xmlns:tns="MFTRNS" recordState="New" msgVersion="13.0">
<OSCONO>100</OSCONO>
<OSINOU>1</OSINOU>
<OSDLIX>155379</OSDLIX>
<OSPANR>AAG44780</OSPANR>
<OSWHLO>AAG</OSWHLO>
</tns:MFTRNS>
Can you get an xslt to do this and if so how?
The reason for doing this is that I want to wrap the xml in an envelope which cannot be done if the declaration is a part of the XML as it does not create a valid xml file
Thanks
If you only want to remove the declaration then a stylesheet as simple as this will do it:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:copy-of select="node()" />
</xsl:template>
</xsl:stylesheet>
But if your ultimate aim is to "wrap the xml in an envelope" then you might be better doing that directly in your XSLT, for example:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" />
<xsl:template match="/">
<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope">
<xsl:copy-of select="node()" />
</soap:Envelope>
</xsl:template>
</xsl:stylesheet>
which will be safer than trying to combine the two files using non-XML-aware textual operations. For example, if your envelope declares a default namespace xmlns="http://example.com" then simply inserting the text of another XML document inside the envelope would change the semantics as it would move the non-prefixed elements like OSCONO into the envelope's default namespace when they should really be in no namespace. XSLT will spot this case and add the necessary xmlns="" overrides.
It's simple: you have to set the omit-xml-declaration attribute of your xsl:output element to yes.
I'm getting a confusion on the way XSLT processors nodes, suppose I have an XML Doc like this:
<object>
<animal>
<man men="asd">man1</man>
<man>man2</man>
<man>man3</man>
<man>man4</man>
<cat>cat1</cat>
<cat>cat2</cat>
<cat>cat3</cat>
<cat>cat4</cat>
</animal>
<vehicule>
<car>car1</car>
<car>car2</car>
<car>car3</car>
<car>car4</car>
</vehicule>
</object>
When I have an XSLT without any template matching like the one below, it returns all text nodes and no attribute nodes, that's OK
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
</xsl:stylesheet>
But when I have one like the one below, it doesn't return anything:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="object">
</xsl:template>
</xsl:stylesheet>
Is it that if I have an explicit template for a parent node, I should have an explicit template for all child nodes of the parent nodes?
What you are seeing are simply the effects of the built-in rules, which output the text value of a node and apply the templates to all its children.
If you overwrite the built-in templates, well, your template takes effect. You want to apply the built-in rules for all children of object:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="object">
<xsl:apply-templates select="*" />
</xsl:template>
</xsl:stylesheet>
Your rule in #2 says to do nothing, so it did nothing. You need to write something in there. See xsl:copy and xsl:apply-templates.
http://cafeconleche.org/books/xmljava/chapters/ch17.html#d0e31297
As others have said, the XSLT default template rules are defined in such a way that, by default, they will match the top node of the document and then recursively process each of the child nodes, all the way to the bottom. Your template overrides the default rules for the root node, and doesn't have any instructions to do anything with it, so it doesn't go any further. If you instead had this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="object">
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
It would continue processing on downwards, using the default rules. You don't necessarily need to do anything specific to handle the child nodes as long as you send the processing their way.
I encountered a peculiar difference in xslt behavior when the root element has a default namespace attribute as opposed to when it does not.
I am wondering why this difference occurs.
The XML input is
<root>
<content>xxx</content>
</root>
When the following transformation is applied
<?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="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="content">
<w>x</w>
</xsl:template>
</xsl:stylesheet>
the result is the expected
<?xml version="1.0" encoding="UTF-8"?>
<root>
<w>x</w>
</root>
But when the same transformation is applied to
<root xmlns="http://test.com">
<content>xxx</content>
</root>
the result is different and based on the application of just default templates (which effectively output the text node value 'xxx'):
<?xml version="1.0" encoding="UTF-8"?>
<root>xxx</root>
Addition
When this is the expected behavior in this case, then what match attribute value is needed to match the content element in the second case?
This is the most FAQ in XPath / XSLT.
An unprefixed element name is treated by XPath as belonging to "no namespace".
The W3C Xpath specification says:
if the QName does not have a prefix, then the namespace URI is null.
Therefore, in a document with a default namespace a refernce to an element with unprefixed name (say "someName") selects nothing, because there isn't any element in "no namespace" in the XML document, but someName means an element with name "someName", belonging to "no namespace".
The Solution:
If we want to select an element by name, we must prefix that name and the prefix must be associated with the default namespace.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="http://test.com" exclude-result-prefixes="x">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="x:content">
<w>x</w>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document with default namespace:
<root xmlns="http://test.com">
<content>xxx</content>
</root>
produces the wanted, correct result:
<root>
<w>x</w>
</root>
so what exactly is your question? If you're simply looking for an explanation, following is a brief one. What you're observing is the proper behavior according to the specification. When you put a namespace on something, the parser essentially treats it as a different element entirely (than an element of the same name but no namespace). Therefore, in the second situation, when you say <xsl:template match="content">, it doesn't match the <content> element in the XML file because it falls under the http://test.com namespace (by way of the namespace declaration on its parent). Therefore, the default templates take over.