xsl implementation for an xml file - xslt

need to convert an xml file having a tag(dynamicVariable) that has an attribute(name).This xml file has to be converted using xsl into the same xml file such that the tag (dynamicVariable) should have the same structure along with it and its the tag-content also should be the value of the attribute.
need to convert the below xml file
<Content>
<alertHeader>
<text xmlns="http://abc.com" xmlns:w="http://def.com"> Claim
<dynamicVariable name="Claim_Reference" />: More Information Needed
</text>
<contactUs>false</contactUs>
</alertHeader>
<body>
<text> ATM/Debit Card Claim:
<strong><dynamicVariable name="Claim_Reference" /></strong>
</text>
</body>
</Content>
into the same format but the tag having 'name' attribute should appear in the output xml file as this format
<dynamicVariable name="Claim_Reference" />Claim_Reference</dynamicVariable>
Can anyone provide the necessary xsl file in converting the same. Hope that its done using
<xsl:copy></xsl:copy> or <xsl:copy-of /> tags .

As simple as this:
<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:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[name() = 'dynamicVariable']">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:value-of select="#name"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<Content>
<alertHeader>
<text xmlns="http://abc.com" xmlns:w="http://def.com"> Claim
<dynamicVariable name="Claim_Reference" />: More Information Needed
</text>
<contactUs>false</contactUs>
</alertHeader>
<body>
<text> ATM/Debit Card Claim:
<strong>
<dynamicVariable name="Claim_Reference" />
</strong>
</text>
</body>
</Content>
the wanted, correct result is produced:
<Content>
<alertHeader>
<text xmlns="http://abc.com" xmlns:w="http://def.com"> Claim
<dynamicVariable name="Claim_Reference">Claim_Reference</dynamicVariable>: More Information Needed
</text>
<contactUs>false</contactUs>
</alertHeader>
<body>
<text> ATM/Debit Card Claim:
<strong>
<dynamicVariable name="Claim_Reference">Claim_Reference</dynamicVariable>
</strong>
</text>
</body>
</Content>
Explanation:
The identity rule copies every node "as-is".
A single template overrides the identity template. It matches any that has athe name "dynamicVariable" regardless of namespace, and that is a child of strong (thus specifying more context helps us process only this occurence of dynamicVariable but leave the preceding one "as-is").
The overriding tempalte shallo-copies the current node, then copies its attributes, then finally creates a text-node child whose contents is the string value of the name attribute of the current (matched) element.

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://abc.com">
<xsl:output method="xml"/>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="dynamicVariable[#name]|t:dynamicVariable[#name]">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:value-of select="#name"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

How can I copy all content in a <p> element omitting <span> tags but not their content

I have some HTML that looks like this:
<div>
<p><span class="selected-for-presentation">This is a <u><em><strong>very cool</strong></em></u> sentence...</span></p>
<p>This is a <u><em><strong><span class="selected-for-presentation">very</span> cool</strong></em></u> sentence...</p>
</div>
and I am trying to write some XSLT that copies the full content of the paragraphs omitting the <span> tags but not their content. So the result should look something like this:
<div>
<p>This is a <u><em><strong>very cool</strong></em></u> sentence...</p>
<p>This is a <u><em><strong>very cool</strong></em></u> sentence...</p>
</div>
This is my XSLT so far, which works for the first paragraph but not the second one:
<xsl:template match="p">
<p>
<xsl:apply-templates mode="copy_span_content"/>
</p>
</xsl:template>
<xsl:template match="strong" mode="copy_span_content">
<xsl:apply-templates mode="copy_span_content"/>
</xsl:template>
<xsl:template match="em" mode="copy_span_content">
<xsl:apply-templates mode="copy_span_content"/>
</xsl:template>
<xsl:template match="u" mode="copy_span_content">
<xsl:apply-templates mode="copy_span_content"/>
</xsl:template>
<xsl:template match="span" mode="copy_span_content">
<xsl:copy-of select="./node()"/>
</xsl:template>
Any help would be appreciated.
There is a much simpler approach if you just want to remove the span and keep everything else, that is to use the Identity Template to handle copying everything, and have an overriding template to skip over the span but carry on processing its children.
Try this
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="span">
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
Thanks to the suggestion from Tim C (see below) I came up with a solution that works perfectly. I couldn't keep it as simple as the suggestion since the HTML file I'm working with is more complex than the extract I provided and my XSLT file is imported into a larger one and I don't want the Identity Template to apply to other elements.
The solution I came up with:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="p[descendant::span]">
<xsl:apply-templates select="." mode="copy_without_span_tags"/>
<!-- There is more stuff that goes in here in my actual template.-->
</xsl:template>
<xsl:template match="#*|node()" mode="copy_without_span_tags">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="copy_without_span_tags"/>
</xsl:copy>
</xsl:template>
<xsl:template match="span" mode="copy_without_span_tags">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>

How to transform and modify (!) nested tags with XSLT?

Take this XML as an example...
<list>
<header>
something
</header>
<main>
<p>
(1) nothing <b>special</b> at all.
</p>
<p>
(1a) and <b>another</b> thing.
</p>
</main>
</list>
Should be transformed to...
<list>
<header>
something
</header>
<aside>
<para nr="(1)">
nothing <u>special</u> at all.
</para>
<para nr="(1a)">
and <u>another</u> thing.
</para>
</aside>
</list>
This Stackoverflow answer was my starting point...
At the moment I have no real approach to solve the problem. I would prefer not to cite my previous failures...
I don't remember answering that referenced question, but the answer I gave is the approach to take. You just need to implement a number rules...
Convert main to aside
For each p tag, add nr attribute to the newly created para tag based on the value in brackets in the first child text element
Convert b tags under the p element to u
It is the second one is a bit tricky, but can be achieved with this template, which makes use of some string manipulation to extract the number in brackets
<xsl:template match="p">
<para nr="{substring-before(substring-after(text()[1], '('), ')')}">
<xsl:apply-templates select="#*|node()"/>
</para>
</xsl:template>
(Also note the use of Attribute Value Templates to create the attribute)
You would also need an associated template to remove the number from the first text node
<xsl:template match="p/text()[1]">
<xsl:value-of select="substring-after(., ')')" />
</xsl:template>
Converting b to u is much easier though (This is assuming only b elements under p need to be changed).
<xsl:template match="p/b">
<u>
<xsl:apply-templates select="#*|node()"/>
</u>
</xsl:template>
There would be a similar template for changing main to aside
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<!-- This is the Identity Transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="main">
<aside>
<xsl:apply-templates select="#*|node()"/>
</aside>
</xsl:template>
<xsl:template match="p">
<para nr="{substring-before(substring-after(text()[1], '('), ')')}">
<xsl:apply-templates select="#*|node()"/>
</para>
</xsl:template>
<xsl:template match="p/text()[1]">
<xsl:value-of select="substring-after(., ')')" />
</xsl:template>
<xsl:template match="p/b">
<u>
<xsl:apply-templates select="#*|node()"/>
</u>
</xsl:template>
</xsl:stylesheet>

Remove namespaces for all except for one node

Given the following source xml:
<?xml version="1.0" encoding="UTF-8"?>
<Test xmlns="http://someorg.org">
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>Some text</p>
<p>Some text</p>
</div>
</text>
</Test>
I would like to have the same output as the above source xml (the source xml contains many other xml nodes but for this section, I want to output it as it is, with no changes.) I have the following xslt (see below) which strips the elements of their namespaces as desired. Unfortunately, it also strips the div elements of their name spaces but I want to retain them. The closest I got to achieving my aim is the following xslt but it outputs the div element twice because of the apply-templates but I only want the div element once with its namespace.
This is my xslt:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://someorg.org"
xmlns="http://someorg.org"
exclude-result-prefixes="f xsl">
<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="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match = "f:text/f:status">
<status value ="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<xsl:apply-templates/>
</div>
</xsl:template>
</xsl:stylesheet>
Instead of trying to remove namespaces for all elements (as handled by your <xsl:template match="*"> template, you can only target elements in the "http://someorg.org" namespace. Simply change the template match to this
<xsl:template match="f:*">
For the elements in the "http://www.w3.org/1999/xhtml" namespace, you could use the identity template to pick up everything else
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:f="http://someorg.org">
<xsl:output method="xml" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="f:*">
<xsl:element name="{local-name(.)}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
add a template
<xsl:template match="*[local-name()='div'
and namespace-uri() = 'http://www.w3.org/1999/xhtml']">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
to perform a copy instead of creating a namespace-stripped element.

How to parse nested tags using XSLT in sequence?

I have below scenario for my XML.
<content>
<para>text-1 <emphasis type="bold">text-2</emphasis> text-3</para>
</content>
I want to parse it like below
<content>
<p>text-1 <b>text-2</b> text-3</p>
</content>
I have created my XSLT as below
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" encoding="ISO-8859-1" indent="no"/>
<xsl:template name="para">
<p>
<xsl:value-of select="text()" disable-output-escaping="yes"/>
<xsl:for-each select="child::*">
<xsl:if test="name()='emphasis'">
<xsl:call-template name="emphasis"/>
</xsl:if>
</xsl:for-each>
</p>
</xsl:template>
<xsl:template name="emphasis">
<xsl:if test="attribute::type = 'bold'">
<b>
<xsl:value-of select="text()" disable-output-escaping="yes"/>
</b>
</xsl:if>
</xsl:template>
<xsl:template match="/">
<content>
<xsl:for-each select="content/child::*">
<xsl:if test="name()='para'">
<xsl:call-template name="para"/>
</xsl:if>
</xsl:for-each>
</content>
</xsl:template>
</xsl:stylesheet>
XSLT provided above is generating output like below
<content>
<p>text-1 text-3<b>text-2 </b></p>
</content>
Please guide me with your suggestions, how can I get my desire output?
To do this, you just need to extend the standard Identity Transform with special cases for matching your para and emphasis elements. For example, for para elements you would the following to replace para with p and then continue matching all the child nodes
<xsl:template match="para">
<p>
<xsl:apply-templates select="#*|node()"/>
</p>
</xsl:template>
So, given the following XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<!-- This is the Identity Transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Replace para with p -->
<xsl:template match="para">
<p>
<xsl:apply-templates select="#*|node()"/>
</p>
</xsl:template>
<!-- Replace emphasis with b -->
<xsl:template match="emphasis[#type='bold']">
<b>
<xsl:apply-templates select="node()"/>
</b>
</xsl:template>
</xsl:stylesheet>
When applied to the following input XML
<content>
<para>text-1 <emphasis type="bold">text-2</emphasis> text-3</para>
</content>
The following is output
<content>
<p>text-1 <b>text-2</b> text-3</p>
</content>
You should be able to see how easy it is to extend to other cases should you input XML have more tags to transform.
do it like this ;)
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="content">
<content><xsl:apply-templates select="para" /></content>
</xsl:template>
<xsl:template match="emphasis [#type='bold']">
<b><xsl:value-of select="." /></b>
</xsl:template>
</xsl:stylesheet>
when you do it like this the default template will catch text-1 and text-3

XSL Use a specific template inside another one

I've got some problems with XSL : is it possible to use a template from another one, when it uses an apply-templates to print childs ? I don't want to use current node, but really create a new element matching the template.
Example of what I'm searching :
XML file :
<root>
<toto name="foo">
<b>hello</b>
</toto>
</root>
XSL stylesheet:
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="tata" name="tata">
<div class="tata">
<xsl:apply-templates />
</div>
</xsl:template>
<xsl:template match="toto" name="toto">
<tata>
<xsl:value-of select="#name" />
</tata>
<tata>
<xsl:apply-templates />
</tata>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
Expected output :
<div class="tata">foo</div>
<div class="tata">
<b>hello</b>
</div>
If I understand you correctly you are looking for the
<xsl:call-template name="tata" />
element.
There is no need to call other templates in your problem. You can do practically all the required processing in template match="toto" In fact in your example code, the <xsl:template match="tata"> is never used (with that given input XML). Creating a literal element in a template doesn't result in calling another template matching that element.
This stylesheet
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes"/>
<xsl:template match="root">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="toto">
<div class="tata">
<xsl:value-of select="#name"/>
</div>
<div class="tata">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
with this input
<root>
<toto name="foo">
<b>hello</b>
</toto>
</root>
produces the required result
<?xml version="1.0" encoding="UTF-8"?>
<div class="tata">foo</div>
<div class="tata">
<b>hello</b>
</div>
Like Dennis answered, if you want to use a template from another one, use the <xsl:call-template/> element. If you also want to change the current node (context node), you can use
<xsl:apply-templates select="path/to/new/context"/>