This question already has answers here:
XSLT with XML source that has a default namespace set to xmlns
(3 answers)
Closed 4 years ago.
I am new to XSLT transformation. I have namepace mapping issue in my output xml.
The input XML is
<m:class xmlns:m="http://www.NotRequirednamespace.com">
<queryDetails>hello</queryDetails>
</m:class>
My XSLT is look like
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.neededNamespace.com" xmlns:t="http://www.NotRequirednamespace.com" exclude-result-prefixes="t">
<xsl:output indent="yes" method="xml" encoding="utf-8" omit-xml-declaration="yes"/>
<!-- Stylesheet to remove all namespaces from a document -->
<!-- NOTE: this will lead to attribute name clash, if an element contains
two attributes with same local name but different namespace prefix -->
<!-- Nodes that cannot have a namespace are copied as such -->
<xsl:template match="/">
<school xmlns="http://www.neededNamespace.com">
<xsl:apply-templates/>
</school>
</xsl:template>
<!-- template to copy elements -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<!-- template to copy attributes -->
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<!-- template to copy the rest of the nodes -->
<xsl:template match="comment() | text() | processing-instruction()">
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>
The output XML is
<school xmlns="http://www.neededNamespace.com">
<class xmlns="">
<queryDetails>hello</queryDetails>
</class>
</school>
But I dont want the name default namespace (xmlns="") in the element class. I like to specify the default namespace for the class element as "http://www.neededNamespace.com". So that I need the output xml as follows.
<school xmlns="http://www.neededNamespace.com">
<class>
<queryDetails>hello</queryDetails>
</class>
</school>
I have tried all the options i know. Can you help in this. Thanks in advance.
Do you really need all that code? Or are you just using this as an incantation, in the hope it will somehow appease the evil spirits? Like what does xpath-default-namespace do in an XSLT 1.0 stylesheet? (Answer: either nothing, or produce a fatal error - depending on how tolerant your processor is).
Now, if your XML example is representative, then all you need to do is:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.neededNamespace.com">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<school>
<xsl:apply-templates/>
</school>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
What this does is:
Create a new root element named <school>;
Create a new element for each existing element, and name it with the local name of the existing element.
Since the stylesheet contains a declaration of a default namespace, ALL newly created elements (in both #1 and #2 above) will be placed in that namespace.
Since your input does not include any attributes, that should be all the code that's required.
In case you worry they may add some attributes in the future that you wish to pass to the output, just change the second template to:
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
This assumes the attributes will be in no namespace - and that's a very reasonable assumption. Attributes do not inherit their parent's namespace, and very rarely do you see authors place attributes in namespaces explicitly. Only if you really need to anticipate such possibility do you need additional code to handle the attributes.
So you don't want to remove the namespace of the "class" element, you want to change it. Use the "namespace" attribute of the xsl:element node.
Instead of
<xsl:element name="{local-name()}">
you want
<xsl:element name="{local-name()}" namespace="http://www.neededNamespace.com">
Related
I'm still pretty new to XSLT and I have the following XML which I need to remove the namespace. I also found the following XSLT which almost gets the job done with the exception that it will not retain the xmlns declaration.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<etd_ms:thesis xmlns:etd_ms="http://www.ndltd.org/standards/metadata/etdms/1.0/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ndltd.org/standards/metadata/etdms/1.0/ http://www.ndltd.org/standards/metadata/etdms/1.0/etdms.xsd">
<etd_ms:title>Aspects of negritude in the works of two Harlem renaissance authors : Claude McKay and Langston Hughes</etd_ms:title>
<etd_ms:creator>Charles, Asselin</etd_ms:creator>
<etd_ms:subject/>
<etd_ms:publisher>Concordia University</etd_ms:publisher>
<etd_ms:contributor role="advisor">Butovsky, M</etd_ms:contributor>
<etd_ms:date>1980</etd_ms:date>
<etd_ms:type>Electronic Thesis or Dissertation</etd_ms:type>
<etd_ms:identifier>TC-QMG-1</etd_ms:identifier>
<etd_ms:format>text</etd_ms:format>
<etd_ms:identifier>https://spectrum.library.concordia.ca/1/1/MK49585.pdf</etd_ms:identifier>
<etd_ms:language>en</etd_ms:language>
<etd_ms:degree>
<etd_ms:name>M.A.</etd_ms:name>
<etd_ms:level>masters</etd_ms:level>
<etd_ms:discipline>Dept. of English</etd_ms:discipline>
<etd_ms:grantor>Concordia University</etd_ms:grantor>
</etd_ms:degree>
</etd_ms:thesis>
and here's the XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" encoding="utf-8" omit-xml-declaration="no"/>
<!-- Stylesheet to remove all namespaces from a document -->
<!-- NOTE: this will lead to attribute name clash, if an element contains
two attributes with same local name but different namespace prefix -->
<!-- Nodes that cannot have a namespace are copied as such -->
<!-- template to copy elements -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<!-- template to copy attributes -->
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<!-- template to copy the rest of the nodes -->
<xsl:template match="comment() | text() | processing-instruction()">
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>
The results is the following:
<?xml version="1.0" encoding="UTF-8"?>
<thesis xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ndltd.org/standards/metadata/etdms/1.0/ http://www.ndltd.org/standards/metadata/etdms/1.0/etdms.xsd">
<title>Aspects of negritude in the works of two Harlem renaissance authors : Claude McKay and Langston Hughes</title>
<creator>Charles, Asselin</creator>
<subject/>
<publisher>Concordia University</publisher>
<contributor role="advisor">Butovsky, M</contributor>
<date>1980</date>
<type>Electronic Thesis or Dissertation</type>
<identifier>TC-QMG-1</identifier>
<format>text</format>
<identifier>https://spectrum.library.concordia.ca/1/1/MK49585.pdf</identifier>
<language>en</language>
<degree>
<name>M.A.</name>
<level>masters</level>
<discipline>Dept. of English</discipline>
<grantor>Concordia University</grantor>
</degree>
</thesis>
It's almost there, with the exception that I need to keep the xmlns declaration, so ultimatly the root element should be something like:
<thesis xmlns="http://www.ndltd.org/standards/metadata/etdms/1.0/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ndltd.org/standards/metadata/etdms/1.0/ http://www.ndltd.org/standards/metadata/etdms/1.0/etdms.xsd">
Can someone help me resolving this issue? Thanks.
Change
<xsl:element name="{local-name()}">
to
<xsl:element name="{local-name()}" namespace="http://www.ndltd.org/standards/metadata/etdms/1.0/">
My requirement is to convert the following array
<array>
<value>755</value>
<value>5861</value>
<value>4328</value>
</array>
to this array.
<array>
<int>755</int>
<int>5861</int>
<int>4328</int>
</array>
Following is my XSLT code to do the transformation & it works. Is it the correct way because in one post I saw the use of "identity template". but I haven't used it.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/array">
<xsl:element name="array">
<xsl:for-each select="value">
<xsl:element name="int">
<xsl:value-of select="." />
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Your current method works. It is more of a "pull" style stylesheet. The "push" style uses apply-templates.
You could shorten it a bit by using element literals, which makes it a little easier to read:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/array">
<array>
<xsl:for-each select="value">
<int>
<xsl:value-of select="." />
</int>
</xsl:for-each>
</array>
</xsl:template>
</xsl:stylesheet>
A solution using the identity template and a custom template for the value element:
<?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"/>
<!--identity template, which copies every attribute and
node(element, text, comment, and processing instruction)
that it matches and then applies templates to all of it's
attributes and child nodes (if there are any) -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--Specialized template that matches the value element.
Because it is more specific than the identity template above it has
a higher priority and will match when the value element is encountered.
It creates an int element and then applies templates to any attributes
and child nodes of the value element -->
<xsl:template match="value">
<int>
<xsl:apply-templates select="#*|node()"/>
</int>
</xsl:template>
</xsl:stylesheet>
You can do:
<xsl:template match="/array">
<array>
<xsl:for-each select="value">
<int>
<xsl:value-of select="." />
</int>
</xsl:for-each>
</array>
</xsl:template>
Here is a quote from the accepted answer of your link:
XSL cannot replace anything. The best you can do is to copy the parts you want to keep, then output the parts you want to change instead of the parts you don't want to keep.
That is where the identity template comes into play: it copies everything not targetted by another matching template. The upshot is, that if your base XML contains other content than just the array, then you should also include the identity template in your xslt. But if you are sure that your xml will contain no other content, then you don't need it.
The requirement is to find the duplicate element(BaseName) in XML and marked the parent element(Account) with isDuplicate attribute. The XSL is working correctly when the input XML RootElement has no namespaces. When the root element has namespace then I get empty object. I am not sure why the namespace is causing XSL to generate empty output. Any help to get the right output would be greatly appreciated.`
Input XML WITH NAMESPACE
<?xml version="1.0"?>
<objects xmlns="urn:s.sexmaple.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Account>
<Id>001A00F</Id>
<RecordTypeId>012A00</RecordTypeId>
<BaseName>EFGH</BaseName>
</Account>
<Account>
<Id>001A0</Id>
<RecordTypeId>012A0</RecordTypeId>
<BaseName>ABCD</BaseName>
</Account>
<Account>
<Id>001A</Id>
<RecordTypeId>012A</RecordTypeId>
<BaseName>ABCD</BaseName>
</Account>
</objects>
XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml"
version="1.0"
encoding="UTF-8"
indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="node()|#*">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="Accounts">
<objects>
<xsl:for-each select="//Account">
<xsl:sort select="BaseName" />
<xsl:apply-templates select="." />
</xsl:for-each>
</objects>
</xsl:variable>
<xsl:variable name="unqentity">
<objects>
<xsl:for-each select="$Accounts/objects/Account">
<xsl:choose>
<xsl:when test="not(following-sibling::Account/BaseName=./BaseName) and not(preceding-sibling::Account/BaseName=./BaseName) ">
<xsl:copy-of select="." />
</xsl:when>
<xsl:otherwise>
<Account>
<xsl:attribute name="isDuplicate">yes</xsl:attribute>
<xsl:for-each select="child::*">
<xsl:element name="{name()}">
<xsl:copy-of select="#*|node()" />
</xsl:element>
</xsl:for-each>
</Account>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</objects>
</xsl:variable>
<xsl:copy-of select="$unqentity" />
</xsl:template>
</xsl:stylesheet>
Output XML WHEN INPUT XML HAS NAMESPACE
<?xml version="1.0" encoding="UTF-8"?>
<objects/>
Output XML when Input has no Namespaces
<?xml version="1.0" encoding="UTF-8"?>
<objects>
<Account>
<Id>001A00F</Id>
<RecordTypeId>012A00</RecordTypeId>
<BaseName>EFGH</BaseName>
</Account>
<Account isDuplicate="yes">
<Id>001A0</Id>
<RecordTypeId>012A0</RecordTypeId>
<BaseName>ABCD</BaseName>
</Account>
<Account isDuplicate="yes">
<Id>001A</Id>
<RecordTypeId>012A</RecordTypeId>
<BaseName>ABCD</BaseName>
</Account>
</objects>
When you have a namespace, it means the element within the namespace is not the same as an element without a namespace (or indeed an element in a different name space).
This means when you do this in your XSLT...
<xsl:for-each select="//Account">
You are looking for an Account element with no namespace, and so it will not match the Account element in your source XML, which is in the amusingly titled "urn:s.sexmaple.com" (which I suspect is a misspelling)
As you are using XSLT2.0 though, there is a simple way to get around this, by specifying a default namespace for any xpath expressions, using the xpath-default-namespace. Normally, this may be enough, but you have slightly complicated matters by creating new elements within a variable, which you then later try to select.
<xsl:for-each select="$Accounts/objects/Account">
This means when you create the objects and Account elements in the $Accounts variable, they will need to be part of the namespace too.
To cut to the chase, this is what your xsl:stylesheet element needs to look like
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns="urn:s.sexmaple.com"
xpath-default-namespace="urn:s.sexmaple.com">
So, the xpath-default-namespace="urn:s.sexmaple.com" is used to match elements in your source XML, whilst the xmlns="urn:s.sexmaple.com" is used to ensure the elements you create in the variable have this namespace and can be matched later on.
Having said all that, you have rather over-complicated your whole XSLT. Are you simply trying to add an IsDuplicate attribute to Account elements with the same BaseName? Well, create a key to look up duplicates, like so
<xsl:key name="account" match="Account" use="BaseName" />
Then you can look up duplicates like so:
<xsl:if test="key('account', BaseName)[2]">
<xsl:attribute name="isDuplicate">Yes</xsl:attribute>
</xsl:if>
Try this XSLT, which should give the same results
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="urn:s.sexmaple.com">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="account" match="Account" use="BaseName" />
<xsl:template match="Account">
<xsl:copy>
<xsl:if test="key('account', BaseName)[2]">
<xsl:attribute name="isDuplicate">Yes</xsl:attribute>
</xsl:if>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note how this only needs to use xpath-default-namespace as it is not creating whole new elements, just copying existing ones (which get their namespace copied across too).
The reason your input XML without a namespace works were the one with a namespace doesn't, isn't because of the input XML, but because of the XSLT stylesheet.
When your XML file has a default namespace, that namespace will need to be declared in the stylesheet itself.
For example, with the following XML:
<test xmlns="test.xml.schema">
<element>Content</element>
</test>
When I apply the following XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<out>
<namespace>None</namespace>
<xsl:apply-templates />
</out>
</xsl:template>
<xsl:template match="test">
<test out="True">hello</test>
</xsl:template>
<xsl:template match="*"/>
</xsl:stylesheet>
The output is just:
<out><namespace>None</namespace></out>
The <xsl:template match="test"> can't match on the test element in the input xml, as it is actually test.xml.schema:test while the match in the stylesheet, with no namespace is actually :test. Thus no match is possible.
However, when we just add a namespace for the input document adn modify the template, like so:
<xsl:stylesheet xmlns:t="test.xml.schema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<out>
<namespace>test.xml.schema</namespace>
<xsl:apply-templates />
</out>
</xsl:template>
<xsl:template match="t:test">
<test out="True">hello</test>
</xsl:template>
<xsl:template match="*"/>
</xsl:stylesheet>
The output becomes:
<out xmlns:t="test.xml.schema">
<namespace>test.xml.schema</namespace>
<test out="True">hello</test>
</out>
Its important to note that the namespace abbreviation in the input document and XSL don't need to be the same (eg. blank vs. "t"), but the namespaces themselfs do: (e.g both blank and "t" must be bound to test.xml.schema).
Also note, that using a default namespace in XSLT can be fraught with issues. So its best to use declared namespaces in XSLT.
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.
I have a SVG file that I want to extend by adding onclick handlers to edges and nodes. I also want to add a script tag referring to a JavaScript. The problem is that the script tag gets an empty namespace attribute added to it.
I haven't found any information regarding this that I understand. Why does XSLT add an empty namespace?
XSL file:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<xsl:output method="xml" encoding="utf-8" />
<xsl:template match="/svg:svg">
<xsl:copy>
<script type="text/ecmascript" xlink:href="base.js" /> <!-- this tag gets a namespace attr -->
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<!-- Identity transform http://www.w3.org/TR/xslt#copying -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Check groups and add functions -->
<xsl:template match="svg:g">
<xsl:copy>
<xsl:if test="#class = 'node'">
<xsl:attribute name="onclick">node_clicked()</xsl:attribute>
</xsl:if>
<xsl:if test="#class = 'edge'">
<xsl:attribute name="onclick">edge_clicked()</xsl:attribute>
</xsl:if>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The unprefixed literal result element script is in the default namespace, which in this case is no namespace. In your result document, this element is explicitly placed in no namespace via xmlns="".
Section 6.2 of Namespaces in XML 1.0 says that:
The attribute value in a default
namespace declaration MAY be empty.
This has the same effect, within the
scope of the declaration, of there
being no default namespace.
If you want this to be a svg:script in the default namespace, make the svg namespace the default for your stylesheet. You will still need a namespace prefix for that namespace as well.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg">