How do I only add a namespace to the root element?
My XML:
<Envelope>
<from>
<contents />
</from>
</Envelope>
My desired output:
<Envelope xmlns:tns="Foo">
<from>
<contents />
</from>
</Envelope>
I can only get "xmlns='Foo'" using this, not "xmlns:tns=..":
<xsl:element name="{local-name()}" namespace="Foo" >
<xsl:copy-of select="attribute::*"/>
<xsl:apply-templates />
</xsl:element>
Here is a complete transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tns="Foo">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:element name="{name()}">
<xsl:copy-of select=
"document('')/*/namespace::*[name()='tns']"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<Envelope>
<from>
<contents />
</from>
</Envelope>
the wanted, correct result is produced:
<Envelope xmlns:tns="Foo">
<from>
<contents/>
</from>
</tns:Envelope>
Related
I'm using a for-each in my XSLT template.
This is my example input XML:
<products>
<data>
<label_1>some_label1</label_1>
<label_2>some_label2</label_2>
<values>
<a>a</a>
<b>b</b>
</values>
</data>
<data>
<label_1>some_label1</label_1>
<label_2>some_label2</label_2>
<values>
<c>c</c>
<d>d</d>
</values>
</data>
</products>
Now based on my template:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="http:/example.com/ns">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="data">
<data>
<xsl:variable name="values" select="values" />
<xsl:for-each select="$values">
<xsl:apply-templates select="#*|node()" />
</xsl:for-each>
</data>
</xsl:template>
</xsl:stylesheet>
I get only <values></values> and that is ok for me.
That's my output:
<products>
<data>
<a>a</a>
<b>b</b>
</data>
<data>
<c>c</c>
<d>d</d>
</data>
</products>
What i need in my output is namespace like this:
<products>
<data>
<ns:a>a</ns:a>
<ns:b>b</ns:b>
</data>
<data>
<ns:c>c</ns:c>
<ns:d>d</ns:d>
</data>
</products>
So what i understand is "each element of values is applied by template". How can I add namespace ?
You can get output similar to what you show (albeit well-formed) by using:
XSLT 1.0
<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:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="data">
<xsl:copy>
<xsl:apply-templates select="values/*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="values/*">
<xsl:element name="ns:{local-name()}" namespace="http:/example.com/ns">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Or, if you prefer:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns="http:/example.com/ns">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/products">
<products>
<xsl:for-each select="data">
<xsl:copy>
<xsl:for-each select="values/*">
<xsl:element name="ns:{local-name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:copy>
</xsl:for-each>
</products>
</xsl:template>
</xsl:stylesheet>
Replace http:/example.com/ns with your own namespace URI.
Credits
This answer follows the technique used in this SO answer to a similar problem.
Solution
Add namespace information to all descendants of specific elements. Augment the stylesheet by a template matching this set of nodes:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns="http://my.ns.uri"
>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="data">
<data>
<xsl:variable name="values" select="values" />
<xsl:for-each select="$values">
<xsl:apply-templates select="#*|node()" />
</xsl:for-each>
</data>
</xsl:template>
<!--
Added template.
-->
<xsl:template match="data//*">
<xsl:element name="ns:{name()}" namespace="http://my.ns.uri">
<xsl:for-each select=".">
<xsl:apply-templates select="#*|node()" />
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I want to transform a source xml into a target xml where certain matches from the source xml are included in different context in the target xml. For example I have a source xml like:
<shiporder>
<shipto>orderperson1</shipto>
<shipto>orderperson1</shipto>
<city>London</city>
</shiporder>
On this source xml I apply the following stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:call-template name="root" />
</xsl:template>
<xsl:template name="root">
<root>
<xsl:apply-templates select="/shiporder"/>
<xsl:call-template name="Customer"/>
</root>
</xsl:template>
<xsl:template name="Customer">
<Customer>
<!--<xsl:apply-templates select="/shiporder"/>-->
</Customer>
</xsl:template>
<xsl:template match="/shiporder">
<xsl:apply-templates select="shipto"/>
</xsl:template>
<xsl:template match="/shiporder/shipto">
<Address>
<xsl:apply-templates select="text()"/>
</Address>
</xsl:template>
</xsl:stylesheet>
In the template of name Customer I like to apply a template like:
<xsl:template match="/shiporder">
<xsl:apply-templates select="city"/>
</xsl:template>
<xsl:template match="/shiporder/city">
<City>
<xsl:apply-templates select="text()"/>
</City>
</xsl:template>
But I already defined a template with match /shiporder. So I don't know how to design a stylesheet where both templates with the same match exists in their own context?
If you use mode, like #michael.hor257k suggested you can differentiate between two or more templates that match on the same element but with different results.
In your case that could end up looking like this:
<?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" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:call-template name="root" />
</xsl:template>
<xsl:template name="root">
<root>
<xsl:apply-templates select="/shiporder" mode="root"/>
<xsl:call-template name="Customer"/>
</root>
</xsl:template>
<xsl:template name="Customer">
<Customer>
<xsl:apply-templates select="/shiporder" mode="customer"/>
</Customer>
</xsl:template>
<xsl:template match="/shiporder" mode="root">
<xsl:apply-templates select="shipto"/>
</xsl:template>
<xsl:template match="/shiporder" mode="customer">
<xsl:apply-templates select="city"/>
</xsl:template>
<xsl:template match="shipto">
<Address>
<xsl:apply-templates select="text()"/>
</Address>
</xsl:template>
<xsl:template match="city">
<City>
<xsl:apply-templates select="text()"/>
</City>
</xsl:template>
</xsl:stylesheet>
Obviously all credits here go to Michael for pointing this out first.
I am transforming XML using XSLT and facing issue while namespace removal. If I remove xmlns it works fine.
Issue 1. It doesn't delete namespace from transformed XML
Issue 2. Doesn't implements other template I have in transformation.
My Input XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Catalog xmlns="http://example.com">
<Books>
<book1>Wise1 Otherwise</book1>
<book2>Great Expectations</book2>
</Books>
<library>
<Name> Forsyth </Name>
<city> Cumming </city>
</library>
</Catalog>
Expected Result
<?xml version="1.0" encoding="UTF-8"?>
<Import>
<Books>
<book1>Wise1 Otherwise</book1>
<book2>Great Expectations</book2>
</Books>
<elab>
<Name> Forsyth </Name>
<city> Cumming </city>
</elab>
</Import>
XSL
<?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:strip-space elements="*"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#xmlns">
<xsl:element name="{local-name()}" namespace="http://example.com">
<xsl:apply-templates select="node() | #*"/>
</xsl:element>
</xsl:template>
<xsl:template match="library">
<elab>
<xsl:apply-templates />
</elab>
</xsl:template>
<xsl:template match="Catalog">
<Import>
<xsl:apply-templates />
</Import>
</xsl:template>
</xsl:stylesheet>
I think you are missing the namespace declaration in the XSLT templates to match elements:
This is my try:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="http://example.com">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<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:template match="ns:library">
<elab>
<xsl:apply-templates />
</elab>
</xsl:template>
<xsl:template match="ns:Catalog">
<Import>
<xsl:apply-templates />
</Import>
</xsl:template>
</xsl:stylesheet>
Input file:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:root xmlns:ns0="http://xyz.com/separate">
<ns0:root1>
<ns3:Detail xmlns:ns3="http://POProject/Details">
<DetailLines>
<ItemID>
<ItemDescription/>
</DetailLines>
</ns3:Detail>
</ns0:root1>
</ns0:root>
Output file:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Detail xmlns:ns0="http://POProject/Details">
<DetailLines>
<ItemID>
<ItemDescription/>
</DetailLines>
</ns0:Detail>
Question: I have to remove the root1 and root nodes and need to do small
changes in Detail node. How to write a xslt code to achieve this?
This...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://xyz.com/separate"
xmlns:ns3="http://POProject/Details">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="*/*/ns3:Detail" />
</xsl:template>
<xsl:template match="ns3:Detail">
<xsl:apply-templates select="." mode="copy-sans-namespace" />
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-sans-namespace" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
...will yield this...
<?xml version="1.0" encoding="utf-8"?>
<ns3:Detail xmlns:ns3="http://POProject/Details">
<DetailLines>
<ItemID />
<ItemDescription />
</DetailLines>
</ns3:Detail>
I'm not sure it is possible to control the prefix. The XDM data model does not consider it to be significant information.
UDPATE
To get the prefix rename, I thought you would have to go to an XML 1.1 supporting XSLT processor (allowing prefix undefine), but I found a way to do it with XML 1.0 . Try this ...
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://xyz.com/separate">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/" xmlns:ns3="http://POProject/Details">
<xsl:apply-templates select="*/*/ns3:Detail" />
</xsl:template>
<xsl:template match="ns0:Detail" xmlns:ns0="http://POProject/Details">
<ns0:Detail xmlns:ns0="http://POProject/Details">
<xsl:apply-templates select="*" mode="copy-sans-namespace" />
</ns0:Detail>
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-sans-namespace" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I want to following xml file output:
<?xml version="1.0" encoding="ISO-8859-1" ?>
- <T0020 xsi:schemaLocation="http://www.safersys.org/namespaces/T0020V1 T0020V1.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.safersys.org/namespaces/T0020V1">
- <INTERFACE>
<NAME>SAFER</NAME>
<VERSION>04.02</VERSION>
</INTERFACE>
for that i have following xslt file:
<xsl:template match="T0020" >
<xsl:copy>
<xsl:attribute name="xsi:schemaLocation" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://www.safersys.org/namespaces/T0020V1 T0020V1.xsd </xsl:attribute>
//some code here...............//
<xsl:copy>
so i add xmlns="http://www.safersys.org/namespaces/T0020V1" attribute under <T0020> tag??
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vDefaultNS"
select="'http://www.safersys.org/namespaces/T0020V1'"/>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="{$vDefaultNS}">
<xsl:copy-of select="namespace::* | #*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<T0020 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.safersys.org/namespaces/T0020V1 T0020V1.xsd"
>
<INTERFACE>
<NAME>SAFER</NAME>
<VERSION>04.02</VERSION>
</INTERFACE>
</T0020>
produces the wanted result:
<T0020 xmlns="http://www.safersys.org/namespaces/T0020V1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.safersys.org/namespaces/T0020V1 T0020V1.xsd">
<INTERFACE>
<NAME>SAFER</NAME>
<VERSION>04.02</VERSION>
</INTERFACE>
</T0020>
Do note that xmlns is not an attribute, but denotes a namespace declaration.
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="http://www.safersys.org/namespaces/T0020V1">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="T0020">
<xsl:element name="{name()}" namespace="http://www.safersys.org/namespaces/T0020V1">
<xsl:attribute name="xsi:schemaLocation">http://www.safersys.org/namespaces/T0020V1 T0020V1.xsd</xsl:attribute>
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
With this input:
<T0020>
<INTERFACE>
<NAME>SAFER</NAME>
<VERSION>04.02</VERSION>
</INTERFACE>
</T0020>
Output:
<T0020 xsi:schemaLocation="http://www.safersys.org/namespaces/T0020V1 T0020V1.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.safersys.org/namespaces/T0020V1">
<INTERFACE>
<NAME>SAFER</NAME>
<VERSION>04.02</VERSION>
</INTERFACE>
</T0020>
Note: Namespace node are not attributes nodes. If you want that elements in no namespace gets output under some namespace you need the xsl:element/#namespace.