XSLT - Change value of element in xml based on some condition - xslt

I need to modify the content of xml based on value of some elemnt in the input xml.
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<Objects>
<Object class="Item" version="1.0" distName="A-1/B-1/Item-0">
<p name="sDate">2013-02-11T00:00:00+02:00:00</p>
<p name="present">1</p>
<p name="stopD">2013-02-21T00:00:00+02:00:00</p>
<p name="id">CPU</p>
</Object>
<Object class="Item" version="1.0" distName="A-1/B-1/Item-1">
<p name="sDate">2013-02-11T00:00:00+02:00:00</p>
<p name="present">1</p>
<p name="stopD">2013-02-21T00:00:00+02:00:00</p>
<p name="id">CPU</p>
</Object>
</Objects>
The XSL should change the value of an element based on its value.
For Eg:
In node Item, if the value of element sDate is 2013-02-11T00:00:00+02:00:00 , i need to make it empty like shown below.
Output XML:
<?xml version="1.0" encoding="UTF-8"?>
<Objects>
<Object class="Item" version="1.0" distName="A-1/B-1/Item-0">
<p name="sDate"></p>
<p name="present">1</p>
<p name="stopD">2013-02-21T00:00:00+02:00:00</p>
<p name="id">CPU</p>
</Object>
<Object class="Item" version="1.0" distName="A-1/B-1/Item-1">
<p name="sDate"></p>
<p name="present">1</p>
<p name="stopD">2013-02-21T00:00:00+02:00:00</p>
<p name="id">CPU</p>
</Object>
</Objects>
I tried some xsl but couldnt get the thing working.. Any leads?

Your question is not quite clear. The following stylesheet will do exactly what you ask for - but I am not sure it provides a general example:
XSLT 1.0
<xsl:stylesheet version="2.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="p[#name='sDate'][.='2013-02-11T00:00:00+02:00:00']">
<p name="sDate"/>
</xsl:template>
</xsl:stylesheet>
Note:
In node Item, if the value of element sDate is
2013-02-11T00:00:00+02:00:00
There is no Item node in your XML, nor a sDate element.

Related

XSLT: How to peek nested element

That's my xml I need to apply an xslt:
<document>
<component>
<structuredBody>
<component>
<section>
<identifier code="S001"/>
<...>
</section>
</component>
</structuredBody>
</component>
</document>
As you can see, here there's a lot of nested structure I don't need.
What I'm only need is to peek section element where section>identifier.code = "S001".
I'd like to peek my desired element without taking care of upper structure.
I'm using this xslt but it's not peeking my desired section element:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="//section[identifier/#code = 'S001']"/>
</xsl:template>
</xsl:stylesheet>
But I'm getting:
<?xml version="1.0" encoding="UTF-8"?>
Above example was a reduced effort to simplify my problem:
<document>
<component>
<structuredBody>
<component>
<section>
<identifier code="S001"/>
<table>
<tbody>
<tr>
<td>attribute1</td>
<td>value1</td>
</tr>
<tr>
<td>attribute2</td>
<td>value2</td>
</tr>
<tr>
<td>attribute3</td>
<td>value3</td>
</tr>
</tbody>
</table>
</section>
<section>
<identifier code="S002"/>
<table>
...
</table>
</section>
</component>
</structuredBody>
</component>
</document>
What I really need is to get something like:
<person> <!-- -> section-->
<attribute key="attribute1">value1</attribute>
<attribute key="attribute2">value2</attribute>
<attribute key="attribute3">value3</attribute>
</person>
Any ideas?
Try:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="//section[identifier/#code='S001']">
<person>
<xsl:for-each select="//td[1]">
<attribute key="{.}"><xsl:value-of select="../td[2]"/></attribute>
</xsl:for-each>
</person>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Remark: this is the solution to the edited post.

Flatten XML file with XML changing the labels according to their nesting position

I am new to XSL and I am trying to flatten an XML file with the following structure with the objective of using it in InDesign (the real XML structure is a lot more complex and actually follows the NLM schema but the example below should work to illustrate what I need):
Ex.
<section>
<p> just normal text here 1</p>
<section>
<p>just normal text for this section</p>
</section>
<p>just normal text here 2</p>
<section>
<p>just more text</p>
<section>
<p>this is the text in the deepest section </p>
</section>
<p>and even more text </p>
</section>
<p>just normal text here 3</p>
</section>
I have almost accomplished what I needed with the following XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="section">
<xsl:variable name="sectionlevel">
<xsl:value-of select="count(ancestor::section)" />
</xsl:variable>
<xsl:element name="s{$sectionlevel}">
<xsl:apply-templates select="descendant::section" />
<xsl:copy-of select="*[local-name() != 'section']"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The output I get with this code is the one shown below which would be fine but the problem is that I need to keep the order of the elements. I only need to change the section element names and keep everything else the same:
<?xml version="1.0" encoding="utf-8"?>
<s0>
<s1>
<p> just normal text for this section</p>
</s1>
<s1>
<s2>
<p>this is the text in the deepest section </p>
</s2>
<p>just more text</p>
<p>and even more text </p>
</s1>
<s2>
<p>this is the text in the deepest section </p>
</s2>
<p> just normal text here 1</p>
<p>just normal text here 2</p>
<p>just normal text here 3</p>
</s0>
As you can see the elements in this example are moved to the end inside the section element. How should I code the XSL transformation so that if keeps all the original XML order and structure and just changes the section labels?
Is this what you expect?
<?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" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- Identity to copy all elements -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="section" >
<xsl:element name="s{count(ancestor::section)}">
<xsl:apply-templates select="#*|node()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This would output:
<?xml version="1.0" encoding="UTF-8"?>
<s0>
<p> just normal text here 1</p>
<s1>
<p>just normal text for this section</p>
</s1>
<p>just normal text here 2</p>
<s1>
<p>just more text</p>
<s2>
<p>this is the text in the deepest section </p>
</s2>
<p>and even more text </p>
</s1>
<p>just normal text here 3</p>
</s0>

xslt compute value based on date

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.

Replace XML fragments using XSLT?

I've got this bit of XML:
<?xml version="1.0" encoding="UTF-8"?>
<photo-caption>
<p>
<?EM-dummyText caption?>
<ld pattern=" "/>
<s2>Photo </s2>
<source>
<?EM-dummyText photographer?>
</source>
</p>
</photo-caption>
I want this output:
<?xml version="1.0" encoding="UTF-8"?>
<photo-caption>
<p>
<s2><?EM-dummyText heading?></s2>
<?EM-dummyText caption?>
</p>
</photo-caption>
This is the XSLT I'm using at the moment:
<xsl:stylesheet version="1.0"
exclude-result-prefixes="subst"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:subst="http://tempuri.org">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<subst:photo-caption>
<p>
<s2>
<?EM-dummyText heading?>
</s2>
<?EM-dummyText caption?>
</p>
</subst:photo-caption>
<xsl:variable name="subst" select="document('')/*/subst:photo-caption"/>
<xsl:template match="photo-caption">
<xsl:copy-of select="$subst"/>
</xsl:template>
</xsl:stylesheet>
... which yields this the output:
<?xml version="1.0" encoding="utf-8"?>
<subst:photo-caption xmlns:subst="http://tempuri.org" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<p>
<s2><?EM-dummyText heading?></s2><?EM-dummyText caption?>
</p>
</subst:photo-caption>
How do I remove the prefixes and namespace attributes from the output? Or is there a better way to do this?
If you simply want to output that fragment then use
<xsl:template match="photo-caption">
<photo-caption>
<p>
<s2>
<xsl:processing-instruction name="EM-dummyText">heading</xsl:processing-instruction>
</s2>
<xsl:processing-instruction name="EM-dummyText">caption</xsl:processing-instruction>
</p>
</photo-caption>
</xsl:template>

XSLT node transformation in a predefined order

How do I make transformation follow xml node order?
The xml file is something like this
<root>
<paragraph>First paragraph</paragraph>
<paragraph>Second paragraph</paragraph>
<unordered_list>
<list_name>Unordered list name</list_name>
<list_element>First element</list_element>
<list_element>Second element</list_element>
</unordered_list>
<paragraph>Third paragraph</paragraph>
</root>
I would like to transform it to HTML
...
<p>First paragraph</p>
<p>Second Paragraph</p>
<h3>Unordered list name</h3>
<ul>
<li>First element</li>
<li>Second element</li>
</ul>
<p>Third paragraph</p>
...
When I use xsl:for-each
It outputs all paragraphs first and then the list, or the other way round.
I want to keep the order of the XML file.
I am aware this might be very basic but I seem to be getting nowhere using xsl:choose and xsl:if. So please help me someone.
Here is a sample xslt stylesheet that does exactly what you are looking for:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- iterate through all the child nodes,
and apply the proper template to them -->
<xsl:template match="/">
<!-- added an extra div tag, to create a correct xml
that contains only one root tag -->
<div>
<xsl:apply-templates />
</div>
</xsl:template>
<!-- create the **p** tags -->
<xsl:template match="paragraph">
<p>
<xsl:value-of select="text()" />
</p>
</xsl:template>
<!-- create the **ul** tags -->
<xsl:template match="unordered_list">
<h3>
<xsl:value-of select="list_name" />
</h3>
<ul>
<xsl:apply-templates select="list_element" />
</ul>
</xsl:template>
<!-- create the **li** tags -->
<xsl:template match="list_element">
<li>
<xsl:value-of select="text()" />
</li>
</xsl:template>
</xsl:stylesheet>
The output of this transformation will be:
<?xml version="1.0" encoding="UTF-8"?>
<div>
<p>First paragraph</p>
<p>Second paragraph</p>
<h3>Unordered list name</h3>
<ul>
<li>First element</li>
<li>Second element</li>
</ul>
<p>Third paragraph</p>
</div>
A shorter and more consize transformation:
<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="paragraph">
<p><xsl:apply-templates/></p>
</xsl:template>
<xsl:template match="unordered_list/list_name">
<h3><xsl:apply-templates/></h3>
</xsl:template>
<xsl:template match="unordered_list/list_element"/>
<xsl:template match="unordered_list/list_element[1]">
<ul>
<xsl:apply-templates mode="list"
select=".|following-sibling::*"/>
</ul>
</xsl:template>
<xsl:template mode="list" match="unordered_list/list_element">
<li><xsl:apply-templates/></li>
</xsl:template>
</xsl:stylesheet>