After a day's research into XSLT, I am admitting defeat!
This is my input:
<div class="a" >
<div class="b">b1</div>
<div class="c">b1c1</div>
<div class="d">b1d1</div>
<div class="d">b1d2</div>
<div class="b">b2</div>
<div class="c">b2c1</div>
<div class="d">b2d1</div>
<div class="d">b2d2</div>
<div class="d">b2d3</div>
<div class="b">b3</div>
<div class="c">b3c1</div>
<div class="d">b3d1</div>
</div>
And this is the output I would like to get:
<div class="a" >
<div class="b">b1
<div class="c">b1c1</div>
<div class="d">b1d1</div>
<div class="d">b1d2</div>
</div>
<div class="b">b2
<div class="c">b2c1</div>
<div class="d">b2d1</div>
<div class="d">b2d2</div>
<div class="d">b2d3</div>
</div>
<div class="b">b3
<div class="c">b3c1</div>
<div class="d">b3d1</div>
</div>
</div>
This is the xslt that I am using:
<?xml version="1.0"?>
<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="*"/>
<!-- Identity template, copies everything as is -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Override for target element -->
<xsl:template match="div[#class='a']">
<!-- Copy the element -->
<xsl:copy>
<!-- And everything inside it -->
<xsl:copy-of select="#*|node()"/>
<!-- Move nodes -->
<xsl:apply-templates select="div[#class='c']"/>
<xsl:apply-templates select="div[#class='d']"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
But it's giving me the wrong output:
<div class="a">
<div class="b">b1</div>
<div class="c">b1c1</div>
<div class="d">b1d1</div>
<div class="d">b1d2</div>
<div class="b">b2</div>
<div class="c">b2c1</div>
<div class="d">b2d1</div>
<div class="d">b2d2</div>
<div class="d">b2d3</div>
<div class="b">b3</div>
<div class="c">b3c1</div>
<div class="d">b3d1</div>
<div class="c">b1c1</div>
<div class="c">b2c1</div>
<div class="c">b3c1</div>
<div class="d">b1d1</div>
<div class="d">b1d2</div>
<div class="d">b2d1</div>
<div class="d">b2d2</div>
<div class="d">b2d3</div>
<div class="d">b3d1</div>
</div>
I understand why it's giving me this output, but I cannot find a way to modify it and get the correct output.
Thank you in advance.
PullingHair
It looks like you are grouping the "c" and "d" classes by the first preceding "b" class. To do this in XSLT 1.0, you could define a key to capture this grouping.
<xsl:key name="b" match="div[#class!='b']" use="generate-id(preceding-sibling::div[#class='b'][1])" />
So, rather than selecting all child nodes in the template that matches the "a" class, you just select the "b" ones
<xsl:apply-templates select="div[#class='b']" />
Then, in the template that matches the "b" class, you can use the key to get the associated "c" and "d" elements
<xsl:template match="div[#class='b']">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:apply-templates select="key('b', generate-id())" />
</xsl:copy>
</xsl:template>
Try this XSLT
<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:key name="b" match="div[#class!='b']" use="generate-id(preceding-sibling::div[#class='b'][1])" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="div[#class='a']">
<xsl:copy>
<xsl:apply-templates select="#*|div[#class='b']" />
</xsl:copy>
</xsl:template>
<xsl:template match="div[#class='b']">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
<xsl:apply-templates select="key('b', generate-id())" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
In XSLT 2.0, you can make use of xsl:for-each-group instead
<xsl:stylesheet version="2.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="div[#class='a']">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:for-each-group select="div" group-starting-with="div[#class='b']">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
<xsl:apply-templates select="current-group()[position() > 1]" />
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Related
I would like to select Parent node without Child node.
Example:
<root>
<p>some text</p>
<ol>
<li>
http://cowherd.com
</li>
</ol>
<p> http://cowherd.com </p>
http://cowherd.com
</root>
Desired Output: I want to add a parent <p> tag to those <a> tags which don't have any parent except <root>.
<root>
<p>some text</p>
<ol>
<li>
http://cowherd.com
</li>
</ol>
<p> http://cowherd.com </p>
<p> http://cowherd.com <p>
</root>
I tried thi but it doesn't work. It adds the <p> tag around all <a> tags.
<xsl:template match="a">
<xsl:if test="parent::*">
<p><a>
<!-- do not forget to copy possible other attributes -->
<xsl:apply-templates select="#* | node()"/>
</a></p>
</xsl:if>
</xsl:template>
I want to add a parent <p> tag to those <a> tags which don't have any parent except <root>.
I believe that boils down to:
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"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/root/a">
<p>
<xsl:copy-of select="."/>
</p>
</xsl:template>
</xsl:stylesheet>
Here's a way this could be done :
<?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:template match="a[not(parent::p)]">
<p>
<xsl:copy-of select="."/>
</p>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
See it working here : https://xsltfiddle.liberty-development.net/93F8dVt
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"/>
I want to create a list by grouping my xml entries by attribute using.
Here's my xml:
<Content>
<contenItem ProductType="Breakfast" name="Eggs" />
<contenItem ProductType="Breakfast" name="Bacon" />
<contenItem ProductType="Lunch" name="Fish" />
<contenItem ProductType="Dinner" name="Steak" />
</Content>
I'm trying to get this result but couldn't figure out how
<ul>
<li>Breakfast
<ul>
<li>Eggs</li>
<li>Bacon</li>
</ul>
</li>
<li>Lunch
<ul>
<li>Fish</li>
</ul>
</li>
<li>Dinner
<ul>
<li>Steak</li>
</ul>
</li>
</ul>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:key name="kProductType" match="contenItem" use="#ProductType" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<ul>
<xsl:apply-templates select="contenItem[
generate-id() = generate-id(key('kProductType',#ProductType)[1])]" />
</ul>
</xsl:template>
<xsl:template match="contenItem">
<li>
<xsl:value-of select="#ProductType" />
<ul>
<xsl:apply-templates select="key('kProductType',#ProductType)" mode="sublist"/>
</ul>
</li>
</xsl:template>
<xsl:template match="contenItem" mode="sublist">
<li>
<xsl:value-of select="#name" />
</li>
</xsl:template>
</xsl:stylesheet>
Good Day,
I have an xml file that looks like:
<albums xmlns="http://www.someurl.com/schema">
<album>
<artist>Rush</artist>
<name>Moving Pictures</name>
<releaseDate>05-31-1981</releaseDate>
<album>
</albums>
what I want is to use xlst to display the artist, name, and how many years is been since the release date.
<div id="recordInfo">
<div class="col"><xsl:value-of select="/t:albums/t:album/t:artist"></div>
<div class="col"><xsl:value-of select="/t:albums/t:album/t:name"></div>
<!-- I want the value of 31 here -->
</div>
Does anyone have any idea of how to do that in XSLT?
TIA,
coson
This transformation:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vTokens" select="tokenize(/*, '-')"/>
<xsl:variable name="vDate" select=
"string-join(($vTokens[3], $vTokens[1], $vTokens[2]), '-')"/>
<xsl:template match="/*">
<xsl:sequence select=
"floor((current-date() - xs:date($vDate)) div xs:dayTimeDuration('P365D')) "/>
</xsl:template>
</xsl:stylesheet>
When applied on this XML document:
<t>05-31-1981</t>
produces the wanted, correct result:
31
Here's an XSLT 2.0 option...
XML Input
<albums xmlns="http://www.someurl.com/schema">
<album>
<artist>Rush</artist>
<name>Moving Pictures</name>
<releaseDate>05-31-1981</releaseDate>
</album>
</albums>
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://www.someurl.com/schema" xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="albums">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="t:album">
<div id="recordInfo">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="t:artist|t:name">
<div class="col"><xsl:value-of select="."/></div>
</xsl:template>
<xsl:template match="t:releaseDate">
<xsl:variable name="vOrigDate" select="tokenize(.,'-')"/>
<xsl:variable name="vDate" select="xs:date(concat($vOrigDate[3],'-',$vOrigDate[1],'-',$vOrigDate[2]))" as="xs:date"/>
<div class="col"><xsl:value-of select="floor(days-from-duration(current-date() - $vDate) div 365)"/></div>
</xsl:template>
</xsl:stylesheet>
Output
<div id="recordInfo">
<div class="col">Rush</div>
<div class="col">Moving Pictures</div>
<div class="col">31</div>
</div>
This XSLT 1.0 template...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://www.someurl.com/schema"
exclude-result-prefixes="xsl t" >
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="current-year" select="2012" />
<xsl:template match="/">
<r>
<xsl:apply-templates select="t:albums/t:album" />
</r>
</xsl:template>
<xsl:template match="t:album">
<div id="recordInfo">
<div class="col"><xsl:value-of select="t:artist" /></div>
<div class="col"><xsl:value-of select="t:name" /></div>
<div class="col"><xsl:value-of select="$current-year - substring(t:releaseDate,7)" /></div>
</div>
</xsl:template>
</xsl:stylesheet>
...will yield this output...
<r>
<div id="recordInfo">
<div class="col">Rush</div>
<div class="col">Moving Pictures</div>
<div class="col">31</div>
</div>
</r>
I've used a variable to store the date. In practice you will use a function to get the current date. Which function depends on XSLT version and engine.
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"/>