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>
Related
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>
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.
In the following code, I need, in the place of the two xxxx's, to have the name of the element (name()), so h1, h2 or h3, whatever the match may be. So the second xxxx must be the count of the h1/h2/h3 in that file. The attribute will then look like "h1_4", or h3_15" etc.
How do I do that ?
<xsl:template match="h1[not(#id)] | h2[not(#id)] | h3[not(#id)]" >
<xsl:element name="{name()}" >
<xsl:attribute name="id">xxxx_<xsl:value-of><xsl:number count="xxxx" /></xsl:value-of></xsl:attribute>
</xsl:element>
<xsl:apply-templates/>
</xsl:template>
As I said, the request is ambiguous. The following stylesheet:
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="h1[not(#id)] | h2[not(#id)] | h3[not(#id)]" >
<xsl:variable name="name" select="name()" />
<xsl:copy>
<xsl:attribute name="id">
<xsl:value-of select="$name"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="count(preceding::*[name()=$name]) + 1"/>
</xsl:attribute>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when applied to the following test input:
<root>
<h1 id="h1_1"/>
<h2 type="abc"/>
<h3 type="xyz"/>
<h1>content</h1>
<h3 id="h3_2" type="efg"/>
<h2/>
</root>
will produce:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<h1 id="h1_1"/>
<h2 id="h2_1" type="abc"/>
<h3 id="h3_1" type="xyz"/>
<h1 id="h1_2">content</h1>
<h3 id="h3_2" type="efg"/>
<h2 id="h2_2"/>
</root>
You're on the right track using xsl:number, how about:
<xsl:template match="h1 | h2 | h3" >
<xsl:copy>
<xsl:attribute name="id">
<xsl:value-of select="name()"/>
<xsl:text>_</xsl:text>
<xsl:number level="any" />
</xsl:attribute>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
I'm assuming an identity template to copy the rest of the document as-is, and that being the case I've simplified the match pattern - you don't need to check for not(#id) as where there is an id attribute in the input it will overwrite the one being created by the xsl:attribute.
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"/>
I have an XML file from which I need to delete an attribute with name "Id" (It must be deleted wherever it appears) and also I need to rename the parent tag, while keeping its attributes and child elements unaltered .. Can you please help me modifying the code. At a time, am able to achieve only one of the two requirements .. I mean I can delete that attribute completely from the document or I can change the parent tag ..
Here is my code to which removes attribute "Id":
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#Id[parent::*]">
</xsl:template>
Please help me changing the parent tag name from "Root" to "Batch".
None of the offered solutions really solves the problem: they simply rename an element named "Root" (or even just the top element), without verifying that this element has an "Id" attribute.
wwerner is closest to a correct solution, but renames the parent of the parent.
Here is a solution that has the following properties:
It is correct.
It is short.
It is generalized (the replacement name is contained in a variable).
Here is the code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="vRep" select="'Batch'"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#Id"/>
<xsl:template match="*[#Id]">
<xsl:element name="{$vRep}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()|text()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#Id" />
<xsl:template match="Root">
<Batch>
<xsl:copy-of select="#*|*|text()" />
</Batch>
</xsl:template>
This should do the job:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()|text()" />
</xsl:copy>
</xsl:template>
<xsl:template match="node()[node()/#Id]">
<batch>
<xsl:apply-templates select='#*|*|text()' />
</batch>
</xsl:template>
<xsl:template match="#Id">
</xsl:template>
</xsl:stylesheet>
I tested with the following XML input:
<root anotherAttribute="1">
<a Id="1"/>
<a Id="2"/>
<a Id="3" anotherAttribute="1">
<b Id="4"/>
<b Id="5"/>
</a>
I would try:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#Id">
</xsl:template>
<xsl:template match="/Root">
<Batch>
<xsl:apply-templates select="#*|node()"/>
</Batch>
</xsl:template>
The first block copies all that is not specified, as you use.
The second replaces #id with nothing wherever is occurs.
The third renames /Root to /Batch.