move attribute to parent - xslt

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

Related

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>

How do I build an id and count at the same time?

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.

xsl merge same elements

Following big problem:
<root>
<div>
<programm></programm>
<systemes><p></p></systemes>
<systemes><table>.1.</table></systemes>
<systemes><table>.2.</table></systemes>
<systemes><p></p></systemes>
<requirements></requirements>
</div>
<div>
<programm></programm>
<systemes><table>.1.</table></systemes>
<systemes><p></p></systemes>
<requirements></requirements>
</div>
<div>
<programm></programm>
<systemes><table>.1.</table></systemes>
<systemes><table>.2.</table></systemes>
<systemes><p></p></systemes>
<requirements></requirements>
</div>
</root>
I need the output to be this:
<root>
<div>
<programm></programm>
<systemes><p></p><table>.1.</table><table>.2.</table><p></p></systemes>
<requirements></requirements>
</div>
<div>
<programm></programm>
<systemes><table>.1.</table><p></p></systemes>
<requirements></requirements>
</div>
<div>
<programm></programm>
<systemes><table>.1.</table><table>.2.</table><p></p></systemes>
<requirements></requirements>
</div>
</root>
I hope someone can help me with this problem. I know the Muenchian Method but dont get it to work properly. Thank you very much!
This is what I tried so far:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*"/>
<xsl:key name="systemsKey" match="//systemes" use="name()"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="systemes[generate-id()=generate-id(key('systemesKey', name())[1])]">
<xsl:copy>
<xsl:apply-templates select="#*|key('systemesKey', name())/node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="systemes[not(generate-id()=generate-id(key('systemesKey', name())[1]))]"/>
</xsl:stylesheet>
This produces exactly the output you described (except for the order of childs inside systemes elements).
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="requirements|programm|p">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="div">
<xsl:copy>
<xsl:element name="systemes">
<xsl:for-each select="systemes">
<xsl:apply-templates/>
</xsl:for-each>
</xsl:element>
<xsl:apply-templates select="requirements|programm"/>
</xsl:copy>
</xsl:template>
<xsl:template match="table">
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You do not need to use grouping at all, Muenchian or not. I would not advise you use something as complex as keys if it is not necessary for the goal you want to achieve.
EDIT: I have used XSLT 2.0 but there is nothing in it which cannot be done in 1.0.

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