XSLT node transformation in a predefined order - xslt

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>

Related

Add parent node to a specific element xslt

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

XPath: Appending text only to the last text() occurrence (regardless of how deeply nested) while copying all XML.

Using XSLT on an XML file, my goal is to copy all XML while appending a snippet of text ("(PDF)" in this example) to only the last bit of text present in a particular tag regardless of how deeply nested that next is. I've managed to take care of most of the edge cases and gotten it close, but there's still one instance that's giving me trouble. I'm sure there's also a way to do this more efficiently so any tips are appreciated.
XML
<links>
This is a PDF file
<a href="something.PDF">
<span>
<b>This</b> is a PDF file
</span>
</a>
<a href="something.pdF">
<div>
<span>
This is a PDF file
</span>
</div>
</a>
<a href="something.pdf">
<div class="something">
<span>
This is a <i>PDF</i> file
</span>
</div>
</a>
<a href="something.pDf">
<div class="something">
<div>
<div>
Test Text
<div>
This is a <i>PDF</i>
</div>
</div>
</div>
</div>
</a>
</links>
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" method="xml"/>
<xsl:strip-space elements="*" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:param name="pdf-append" select="'(PDF)'"/>
<xsl:template match="a['.pdf' = substring(translate(#href,'PDF','pdf'), string-length(#href) - 3)]/text()">
<xsl:value-of select="concat(current(),' ', $pdf-append)"/>
</xsl:template>
<xsl:template match="a['.pdf' = substring(translate(#href,'PDF','pdf'), string-length(#href) - 3)]//node()[last()]/text()[last()]">
<xsl:value-of select="concat(current(),' ', $pdf-append)"/>
</xsl:template>
</xsl:stylesheet>
Current Result
<links>
This is a PDF file (PDF)
<a href="something.PDF">
<span>
<b>This</b> is a PDF file
(PDF)</span>
</a>
<a href="something.pdF">
<div>
<span>
This is a PDF file
(PDF)</span>
</div>
</a>
<a href="something.pdf">
<div class="something">
<span>
This is a <i>PDF</i> file
(PDF)</span>
</div>
</a>
<a href="something.pDf">
<div class="something">
<div>
<div>
Test Text
(PDF)<div>
This is a (PDF)<i>PDF (PDF)</i>
</div>
</div>
</div>
</div>
</a>
</links>
The last <a> is the problem: ideally "(PDF)" would only appear within the the last <i> tag (e.g. This is a <i>PDF (PDF)</i>). So the questions is: how can I fix that last instance?
Thanks.
How about:
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="*"/>
<xsl:param name="pdf-append" select="' (PDF)'"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="."/>
<xsl:variable name="a" select="ancestor::a" />
<xsl:variable name="href" select="$a/#href" />
<xsl:variable name="extension" select="substring(translate($href,'PDF','pdf'), string-length($href) - 3)" />
<xsl:variable name="last-text" select="$a/descendant::text()[last()]" />
<xsl:if test="$extension='.pdf' and generate-id()=generate-id($last-text) ">
<xsl:value-of select="$pdf-append"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

How to move an element by attribute value to the last sibling position with xsl?

I need to move (not copy) an element with a given value in attribute id to the last position of its siblings, e.g. //ul/li[#id='b']:
Input:
<ul>
<li id="a">a</li>
<li id="b">b</li>
<li id="c">c</li>
<li id="d">d</li>
...
</ul>
Output:
<ul>
<li id="a">a</li>
<li id="c">c</li>
<li id="d">d</li>
...
<li id="b">b</li>
</ul>
If your input XML is as simple as that in the question, you can use this:
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="ul">
<ul>
<xsl:apply-templates select="li[#id != 'b']"/>
<xsl:apply-templates select="li[#id = 'b']"/>
</ul>
</xsl:template>
<xsl:template match="li">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:transform>
This may help:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" indent="yes" encoding="UTF-8" standalone="yes"/>
<xsl:param name="id" select="string('b')"/>
<xsl:template match="ul">
<xsl:element name="ul">
<xsl:apply-templates select="li[#id!=$id]"/>
<xsl:apply-templates select="li[#id=$id]"/>
</xsl:element>
</xsl:template>
<xsl:template match="li">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>

Need XSLT Transformation to get Un-ordered List of HTML like structure

I am new to XSLT.
I need to transform the below input xml format to the desired output format which is under it (O/P Format is an unorderedList in HTML) using XSLT to use this in a JQuery plugin. I have tried with the below XSLT code myself but i need to add more to it. I am finding hard time to get this transformation done, can any one please help me on this.
Input Format
<Unit id = "2000001">
<Unit id = "2000002">
<Unit id = "2000006">
<Unit id = "2000032">
<Data>
<PartyId>2000032</PartyId>
<PartyTypeCode>DEPT</PartyTypeCode>
<PartyName>2017964 SM Retirement Party</PartyName>
</Data>
</Unit>
<Unit id = "2000033">
<Data>
<PartyId>2000033</PartyId>
<PartyTypeCode>DEPT</PartyTypeCode>
<PartyName>2018370 2012 Director's Ornament</PartyName>
</Data>
</Unit>
<Data>
<PartyId>2000006</PartyId>
<PartyTypeCode>DEPT</PartyTypeCode>
<PartyName>Projects Executive</PartyName>
</Data>
</Unit>
<Data>
<PartyId>2000002</PartyId>
<PartyTypeCode>SEG</PartyTypeCode>
<PartyName>Tres Aguilas Management</PartyName>
</Data>
</Unit>
<Data>
<PartyId>2000001</PartyId>
<PartyTypeCode>SEG</PartyTypeCode>
<PartyName>Tres Aguilas Enterprise</PartyName>
</Data>
</Unit>
Output Format:
<ul>
<li id = "2000001">
<span>Tres Aguilas Enterprise</span>
<ul>
<li id = "2000002">
<span>Tres Aguilas Management</span>
<ul>
<li id = "2000006">
<span>Projects Executive</span>
<ul>
<li id = "2000032">
<span>2017964 SM Retirement Party</span>
</li>
<li id = "2000033">
<span>2018370 2012 Director's Ornament</span>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
XSLT Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="//Unit">
<ul>
<li><xsl:value-of select="Data/PartyName"/></li>
</ul>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This is a "push style" stylesheet that achieves what you want.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<!--identity template-->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--convert every <Unit> into a <UL>,
then "push" the attributes(i.e. #id),
and then "push" any <Unit> children-->
<xsl:template match="Unit">
<ul>
<xsl:apply-templates select="#*"/>
</ul>
</xsl:template>
<!--Create an <li> and copy the #id attribute,
then "push" the Data/PartyName that are children of this <Unit>-->
<xsl:template match="Unit/#id">
<li>
<xsl:copy/>
<xsl:apply-templates select="../Data/PartyName"/>
<xsl:apply-templates select="../Unit"/>
</li>
</xsl:template>
<!--convert <PartyName> into <span> -->
<xsl:template match="Data/PartyName">
<span><xsl:value-of select="."/></span>
</xsl:template>
</xsl:stylesheet>
:) Thanks a lot Mads Hansen, for contributing to my question. I finally did changes to the XSLT you gave and succeeded in achieving the Transformation to required Format.
Here is the final XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<!--identity template-->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--convert every <Unit> into a <UL>,
then "push" the attributes(i.e. #id),
and then "push" any <Unit> children-->
<xsl:template match="Unit">
<xsl:apply-templates select="#*"/>
</xsl:template>
<!--Create an <li> and copy the #id attribute,
then "push" the Data/PartyName that are children of this <Unit>-->
<xsl:template match="Unit/#id">
<li>
<xsl:copy/>
<xsl:apply-templates select="../Data/PartyName"/>
<xsl:if test= "../Unit">
<ul>
<xsl:apply-templates select="../Unit"/>
</ul>
</xsl:if>
</li>
</xsl:template>
<!--convert <PartyName> into <span> -->
<xsl:template match="Data/PartyName">
<span>
<xsl:value-of select="."/>
</span>
</xsl:template>
</xsl:stylesheet>

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.