xsl:template match attribute: how related to default namespace - xslt

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.

Related

XSLT Transformation from SOAP

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.

Use XSLT to copy XML without the xml declaration

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.

XSLT 1.0 to move namespace to child node

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>.

XSLT not matching element - namespace declarations

I'm sure this is a very simple fix, but I'm stumped. I've got input XML with the following root element, and repeating child elements:
<modsCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.loc.gov/mods/v3"
xsi:schemaLocation="
http://www.loc.gov/mods/v3
http://www.loc.gov/standards/mods/v3/mods-3-4.xsd">
<mods version="3.4">
...
I've got an XSLT sheet with the following to match each <mods> node, and kick it out as a separate file named by an <identifier type="local"> element.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.loc.gov/mods/v3">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/modsCollection">
<xsl:for-each select="mods">
<xsl:variable name="filename"
select="concat(normalize-space(
identifier[#type='local']),
'.xml')" />
<xsl:result-document href="{$filename}">
<xsl:copy-of select="."></xsl:copy-of>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This works if the XML input does not have the xmlns:xsi, xmlns, or xsi:schemaLoaction attributes in the root element. So, for example, it works on the following:
<modsCollection>
<mods version="3.4">
...
I know that some of our MODS files have had the prefix included but I'm unclear why this won't work without the prefix if our XSLT matching is not looking for the prefix. Any thoughts or advice would be greatly appreciated.
<xsl:template match="/modsCollection">
matches modsCollection in no namespace. You want
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.loc.gov/mods/v3"
xmlns:m="http://www.loc.gov/mods/v3">
then
<xsl:template match="/m:modsCollection">
To match modsCollection in the mods namespace, and similarly use the m: prefix in all xslt patterns and xpath expressions in the stylesheet.

Problem with XSLT getting values from tags with namespace prefixes

I have a specific problem getting values for width and height out of some XML that has namespace prefixes defined. I can get other values such as SomeText from RelatedMaterial quite easily using normal xpath with namespace "n:" but unable to get values for width and height.
Sample XML:
<Description>
<Information>
<GroupInformation xml:lang="en">
<BasicDescription>
<RelatedMaterial>
<SomeText>Hello</SomeText>
<t:ContentProperties>
<t:ContentAttributes>
<t:Width>555</t:Width>
<t:Height>444</t:Height>
</t:ContentAttributes>
</t:ContentProperties>
</RelatedMaterial>
</BasicDescription>
</GroupInformation>
</Information>
</Description>
Here is an extract from the XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:n="urn:t:myfoo:2010" xmlns:tva2="urn:t:myfoo:extended:2008"
<xsl:apply-templates select="n:Description/n:Information/n:GroupInformation"/>
<xsl:template match="n:GroupInformation">
<width>
<xsl:value-of select="n:BasicDescription/n:RelatedMaterial/t:ContentProperties/t:ContentAttributes/t:Width"/>
</width>
</xsl:template>
The above XSLT does not work for getting the width. Any ideas?
I'm not sure you have realised that both your input and XSLT is invalid, it's always better to provide working examples.
Anyway, if we look at the XPath expression n:BasicDescription/n:RelatedMaterial/t:ContentProperties/t:ContentAttributes/t:Width you're using a prefix n which is mapped to urn:t:myfoo:2010 but when the data infact is in the default namespace. The same goes for the t prefix which isn't defined at all in neither the input data nor XSLT.
You need to define the namespaces on "both sides", in the XML data and the XSLT transformation and they need to be the same, not the prefixes, but the URI.
Somebody else could probably explain this better than me.
I've corrected your example and added a few things to make this work.
Input:
<?xml version="1.0" encoding="UTF-8"?>
<Description
xmlns="urn:t:myfoo:2010"
xmlns:t="something...">
<Information>
<GroupInformation xml:lang="en">
<BasicDescription>
<RelatedMaterial>
<SomeText>Hello</SomeText>
<t:ContentProperties>
<t:ContentAttributes>
<t:Width>555</t:Width>
<t:Height>444</t:Height>
</t:ContentAttributes>
</t:ContentProperties>
</RelatedMaterial>
</BasicDescription>
</GroupInformation>
</Information>
</Description>
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:n="urn:t:myfoo:2010"
xmlns:t="something...">
<xsl:template match="/">
<xsl:apply-templates select="n:Description/n:Information/n:GroupInformation"/>
</xsl:template>
<xsl:template match="n:GroupInformation">
<xsl:element name="width">
<xsl:value-of select="n:BasicDescription/n:RelatedMaterial/t:ContentProperties/t:ContentAttributes/t:Width"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="UTF-8"?>
<width>555</width>