XSL Use a specific template inside another one - templates

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"/>

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>

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.

copy from child node to parent

I have this structure
<a href="xxx" class="box">
<img src="1.jpg" alt="CBA" title="ABC" class="imgresp" height="204" width="307">
</a>
What I want:
If an A-tag has class "box"
then copy the title attribute from img to parent A-tag
Expected output:
<a href="xxx" class="box" title="ABC">
<img src="1.jpg" alt="CBA" title="ABC" class="imgresp" height="204" width="307">
</a>
Firstly, the XML needs to be well-formed, so the img tag needs to be closed, otherwise it won't be possible to use XSLT on it.
<a href="xxx" class="box">
<img src="1.jpg" alt="CBA" title="ABC" class="imgresp" height="204" width="307" />
</a>
Assuming that is really the case, you then need read up on the XSLT identity transform
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
On it's own, it just copies all the nodes and attributes from the input XML to the output XML as-is, which in your case, brings you 99% of the way there.
Then, all you need is a template to copy the extra attribute you need. In this case, you need a template that matches the a tag, which copies it, but adds the extra attribute on too.
<xsl:template match="a[#class='box']">
<xsl:copy>
<xsl:copy-of select="img/#title" />
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
Or maybe, like this, using Attribute Value Templates to create the attribute
<xsl:template match="a[#class='box']">
<a title="{img/#title}">
<xsl:apply-templates select="#*|node()"/>
</a>
</xsl:template>
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a[#class='box']">
<a title="{img/#title}">
<xsl:apply-templates select="#*|node()"/>
</a>
</xsl:template>
</xsl:stylesheet>
you can create a template that matches the nodes 'a' and 'img' and their attributes. This template copies the nodes, do a test if there is an attributes 'class' in order to add the title attributes
<xsl:template match="a/#* |img/#* | a | img">
<xsl:copy>
<xsl:if test="#class = 'box' ">
<xsl:attribute name="title" select="img/#title"/>
</xsl:if>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>

move attribute to parent

I'm trying to restructure some HTML using XSLT. How can I transform this:
<div>
<h2 class="foo">...</h2>
<p>bar</p>
</div>
into:
<div class="foo">
<h2>...</h2>
<p>bar</p>
</div>
In case you're using XSLT 1.0, the following XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="div">
<xsl:copy>
<xsl:attribute name="class">
<xsl:value-of select="h2/#class"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="h2/#class"/>
</xsl:stylesheet>
when applied to your input has the output
<div class="foo">
<h2>...</h2>
<p>bar</p>
</div>
The first template <xsl:template match="#*|node()">is an identity transformation - matching all attributes and other nodes, copying them and applying the identiy transformation to all child nodes and attributes of the current context node.
The empty template <xsl:template match="h2/#class"/> removes the class attribute from the h2, while the template matching the div copies the div and adds the class attribute of the h2 using
<xsl:attribute name="class">
<xsl:value-of select="h2/#class"/>
</xsl:attribute>
If you're using XSLT 2.0, this could be adjusted to
<xsl:attribute name="class" select="h2/#class"/>

XSLT - Dealing with multiple child-nodes

I've got to write and XSLT to deal with the following XML:
<Attachments>
<string>http://lurl/site/Lists/Note/Attachments/image1.jpg</string>
<string>http://lurl/site/Lists/Note/Attachments/image3.jpg</string>
</Attachments>
I need to output the 2 strings, although for some records there are more then 2 strings to output.
e.g.
<ul>
<li>http://lurl/site/Lists/Note/Attachments/image1.jpg</li>
<li>http://lurl/site/Lists/Note/Attachments/image3.jpg</li>
</ul>
Do i need a for each or a while?
A simple apply-templates should do it.
<xsl:template match="Attachments">
<ul>
<xsl:apply-templates/>
</ul>
</xsl:template>
<xsl:template match="string">
<li><xsl:value-of select="."/></li>
</xsl:template>
You don't need any kind of iteration. Use the Identity transformation and override:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Attachments">
<ul>
<xsl:apply-templates select="node()|#*"/>
</ul>
</xsl:template>
<xsl:template match="string">
<li><xsl:value-of select="."/></li>
</xsl:template>
</xsl:stylesheet>
One approach would be to use an xsl:template
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:template match="/">
<ul>
<xsl:apply-templates />
</ul>
</xsl:template>
<xsl:template match="/Attachments/string">
<li>
<xsl:value-of select="." />
</li>
</xsl:template>
</xsl:stylesheet>
<ul>
<xsl:for-each select="//Attachments/string">
<li>
<xsl:value-of select="text()" />
</li>
</xsl:for-each>
</ul>