XSLT for-each namespace - xslt

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>

Related

How to remove looping elements using XSLT Transformations where some tag have no values

<DETAILS>
<PUT>
<RECORD>ABC_PQRST0123456-001_1</RECORD>
<NUMBER>4</NUMBER>
<INST>1,2</INST>
</PUT>
<PUT>
<RECORD>ABC_PQRST0123456-001_2</RECORD>
<NUMBER>1</NUMBER>
</PUT>
</DETAILS>
How to remove the other loop elements from where INST don't have values.Can someone help me with xslt Transformation code to sort this
<PUT>
<RECORD>ABC_PQRST0123456-001_2</RECORD>
<NUMBER>1</NUMBER>
</PUT>
This does what you describe:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:apply-templates select="*" />
</xsl:template>
<xsl:template match="PUT[not(INST)]">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<!-- match but dont do anything -->
<xsl:template match="PUT">
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Append new xml node under existing xml nodes using xslt

I am trying to append a new XML node under existing XML node but i wasn't able to achieve the desired result.
Please find my below XML,
<Root>
<Row>
<A1>0</A1>
<A2>1</A2>
<Preferred_First_Name>aaaa</Preferred_First_Name>
<Preferred_Last_Name>yyyy</Preferred_Last_Name>
<location>xxxx</location>
<ID>12345</ID>
</Row>
</Root>
I want to modify the above XML in such a way that Preferred_First_Name, Preferred_Last_Name and location node need to be under a new XML tag "Data".
The desired output should be like below,
<Root>
<Row>
<A1>0</A1>
<A2>1</A2>
<Data>
<Preferred_First_Name>aaaa</Preferred_First_Name>
<Preferred_Last_Name>yyyy</Preferred_Last_Name>
<location Descriptor="xxxx">
<ID type="ID">xxxx</ID>
<ID type="LocationID">xxxx</ID>
</location>
</Data>
<ID>12345</ID>
</Row>
</Root>
Can someone please help?
you can use below
<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet xmlns:xalan="http://xml.apache.org/xalan" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" version="1.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="Root">
<xsl:element name="Root">
<xsl:call-template name="Row" />
</xsl:element>
</xsl:template>
<xsl:template name="Row">
<xsl:element name="Row">
<xsl:copy-of select="Row/A1"/>
<xsl:copy-of select="Row/A2"/>
<xsl:element name="Data">
<xsl:copy-of select="Row/Preferred_First_Name"/>
<xsl:copy-of select="Row/Preferred_Last_Name"/>
<xsl:element name="location">
<xsl:attribute name="Descriptor">
<xsl:value-of select="Row/location"/>
</xsl:attribute>
<xsl:element name="ID">
<xsl:attribute name="type">
<xsl:value-of select="'ID'"/>
</xsl:attribute>
<xsl:value-of select="Row/location"/>
</xsl:element>
<xsl:element name="ID">
<xsl:attribute name="type">
<xsl:value-of select="'LocationID'"/>
</xsl:attribute>
<xsl:value-of select="Row/location"/>
</xsl:element>
</xsl:element>
<xsl:copy-of select="Row/ID"/>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Please let me know this helps you
If it is okay to put <ID> after <Data> mentioned by Tim C, then the optimized solution can be:
<?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"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Row">
<Row>
<xsl:apply-templates select="child::node()[not(self::Preferred_First_Name or self::Preferred_Last_Name
or self::location)]" />
<Data>
<xsl:apply-templates select="child::node()[self::Preferred_First_Name or self::Preferred_Last_Name
or self::location]"/>
</Data>
</Row>
</xsl:template>
</xsl:stylesheet>

Using apply-template instead of call-template

Is it possible to replace the call-template statement in the following stylesheet with a apply-statement? So that the structure of the templates are nearly the same. With structure I mean that I have a xpath to select a element form the source xml e.g. /shiporder/address/city and I have a target xpath for my output xml e.g. /root/Address/Country then I step reverse through the source path. All /shiporder/address/city goes under Country all /shiporder/address goes under Address and the root shiporder become the tag root.
Source XML:
<shiporder>
<shipto>orderperson1</shipto>
<shipfrom>orderperson2</shipfrom>
<address>
<city>London</city>
</address>
<address>
<city>Berlin</city>
</address>
</shiporder>
Stylesheet:
<xsl:template match="/">
<xsl:apply-templates select="shiporder"/>
</xsl:template>
<xsl:template match="/shiporder">
<root>
<xsl:apply-templates select="address/city"/>
<xsl:call-template name="Identity" />
</root>
</xsl:template>
<xsl:template name="Identity">
<Identity>
<xsl:call-template name="Name" />
</Identity>
</xsl:template>
<xsl:template name="Name">
<Name>
<xsl:apply-templates select="/shiporder/shipto"/>
</Name>
</xsl:template>
<xsl:template match="/shiporder/shipto">
<Last>
<xsl:apply-templates select="text()"/>
</Last>
</xsl:template>
<xsl:template match="/shiporder/address/city">
<Country>
<xsl:apply-templates select="text()"/>
</Country>
</xsl:template>
you can use the following:
<?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="/shiporder">
<root>
<xsl:apply-templates select="address/city"/>
<xsl:apply-templates select="shipto"/>
</root>
</xsl:template>
<xsl:template match="shipto">
<Identity>
<Name>
<Last><xsl:value-of select="."/></Last>
</Name>
</Identity>
</xsl:template>
<xsl:template match="/shiporder/address/city">
<Country>
<xsl:value-of select="."/>
</Country>
</xsl:template>
</xsl:stylesheet>
Generally speaking, <xsl:call-template name="..."/> can be turned into a <xsl:apply-templates select="current()" mode="..."/> and <xsl:template match="node()" mode="..."/> (as long as this mode is not used anywhere else).
But there, the upvoted answer is way more suited.

Call templates with same match string in different context

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.

Adding a namespace to just one element

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>