How to Import stylesheets in xslt conditionally? - xslt

Is there any way to import stylesheets after checking some conditions?
Like,if the value of variable $a="1" then import 1.xsl or else import 2.xsl.

Hi All, Is there any way to import
stylesheets after checking some
conditions?
Like,if the value of variable $a="1"
then import 1.xsl or else import
2.xsl.
No, the <xsl:import> directive is only compile-time.
In XSLT 2.0 one can use the use-when attribute for a limited conditional compilation.
For example:
<xsl:import href="module-A.xsl"
use-when="system-property('xsl:vendor')='vendor-A'"/>
The limitations of the use-when attribute are that there is no dynamic context when the attribute is evaluated -- in particular that means that there are no in-scope variables defined.
A non-XSLT solution is to dynamically change the href attribute of the <xsl:import> declaration before the transformation is invoked:
Parse the xsl stylesheet as an XML file
Evaluate the condition that determines which stylesheet should be imported.
Set the value of the href attribute of the <xsl:import> declaration to the URI of the dynamically determined stylesheet-to-be-imported.
Invoke the transformation with the in-memory xsl stylesheet that was just modified.

I know this post is old, but I want to share my opinion.
Each display could use one template instead of two. The value display will be change with a VB application.
breakfast_menu.xml:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="conditionDisplay.xsl" ?>
<data>
<breakfast_menu>
<food>
<name>Belgian Waffles</name>
<price>$5.95</price>
<description>Two of our famous Belgian Waffles with plenty of real maple syrup</description>
<calories>650</calories>
</food>
<food>
<name>Strawberry Belgian Waffles</name>
<price>$7.95</price>
<description>Light Belgian waffles covered with strawberries and whipped cream</description>
<calories>900</calories>
</food>
<food>
<name>Homestyle Breakfast</name>
<price>$6.95</price>
<description>Two eggs, bacon or sausage, toast, and our ever-popular hash browns</description>
<calories>950</calories>
</food>
</breakfast_menu>
<display>1</display>
</data>
In this file, I imported my displays and with a condition I tell the template what I need.
conditionDisplay.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:import href="display1.xsl"/>
<xsl:import href="display2.xsl"/>
<xsl:template match="/">
<xsl:variable name="display"><xsl:value-of select= "data/display"/></xsl:variable>
<xsl:choose>
<xsl:when test="$display='1'">
<xsl:call-template name="display1" />
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="display2 />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
display1.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="display1">
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<body style="font-family:Arial;font-size:12pt;background-color:#EEEEEE">
<xsl:for-each select="data/breakfast_menu/food">
<div style="background-color:teal;color:white;padding:4px">
<span style="font-weight:bold"><xsl:value-of select="name"/> - </span>
<xsl:value-of select="price"/>
</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>
<xsl:value-of select="description"/>
<span style="font-style:italic"> (<xsl:value-of select="calories"/> calories per serving)</span>
</p>
</div>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
display2.xsl:
<?xml version="1.0" encoding="UTF-8"?>futur
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="display2">
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<body style="font-family:Arial;font-size:12pt;background-color:#222222">
<xsl:for-each select="data/breakfast_menu/food">
<div style="background-color:teal;color:white;padding:4px">
<span style="font-weight:bold"><xsl:value-of select="name"/> - </span>
<xsl:value-of select="price"/>
</div>
<div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
<p>
<xsl:value-of select="description"/>
<span style="font-style:italic"> (<xsl:value-of select="calories"/> calories per serving)</span>
</p>
</div>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
I genuinely apologize for my terrible English. It will be better to the next post and I hope will help someone as I think it's not the best solution.

Related

XSLT: Iterating through all value of a xsl:key map

XML
<books>
<book title="XML Today" author="David Perry" release="2016"/>
<book title="XML and Microsoft" author="David Perry" release="2015"/>
<book title="XML Productivity" author="Jim Kim" release="2015"/>
</books>
The following XSL code iterates through all books by David Perry.
XSL
<xsl:key name="title-search" match="book" use="#author"/>
<xsl:template match="/">
<HTML>
<BODY>
<xsl:for-each select="key('title-search', 'David Perry')">
<DIV>
<xsl:value-of select="#title"/>
</DIV>
</xsl:for-each>
</BODY>
</HTML>
</xsl:template>
HTML output
<HTML>
<BODY>
<DIV>XML Today</DIV>
<DIV>XML and Microsoft</DIV>
</BODY>
</HTML>
Now I would like to iterate not only through all books by David Perry but through all books by any author.
How would a corresponding outer loop look like?
Or in other words: How do I iterate through all values of my title-search key.
The output should be something like this:
<HTML>
<BODY>
<H1>David Perry</H1>
<DIV>XML Today</DIV>
<DIV>XML and Microsoft</DIV>
<H1>Jim Kim</H1>
<DIV>XML Productivity</DIV>
</BODY>
</HTML>
This should do the job:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="title-search" match="book" use="#author"/>
<xsl:template match="/books">
<HTML>
<BODY>
<xsl:apply-templates select="book" />
</BODY>
</HTML>
</xsl:template>
<xsl:template match="book">
<xsl:variable name="author" select="#author" />
<xsl:if test="generate-id(.) = generate-id(key('title-search', $author)[1])">
<H1><xsl:value-of select="#author" /></H1>
<xsl:apply-templates select="//book[#author = $author]" mode="titles"/>
</xsl:if>
</xsl:template>
<xsl:template match="book" mode="titles">
<DIV>
<xsl:value-of select="#title"/>
</DIV>
</xsl:template>
</xsl:stylesheet>
It uses a technique called Muenchian grouping. Each element in an XML document implicitly has a unique ID assigned to it by the XSLT processor (it can also be explicitly assigned with the id attribute in the document itself). This part:
generate-id(.) = generate-id(key('title-search', $author)[1])
basically tests if the ID of the current book element is the same as that of the first book element with the same author. The variable $author is taken from the current book, The key is used to look up the <book> elements with that same author, the [1] predicate takes the first one. As a result, the <H1> is only generated for the first occurrence of that specific author, and in that same if element we're then applying the template for listing the books of that author. The mode is used to avoid a clash between these templates. There's no doubt a solution that doesn't use modes, but this works too. You could also do a lot of this with <xsl:for-each> but I made separate templates because XSLT is declarative and works best when treating it as such.
Grouping is a lot easier in XSLT 2, but when stuck with XSLT 1, the Muenchian grouping technique often provides a solution once you grok it.
Try this:
XSLT2:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<HTML>
<BODY>
<xsl:for-each-group select="books/book" group-by="#author">
<H1><xsl:value-of select="current-grouping-key()"/></H1>
<xsl:for-each select="current-group()">
<DIV><xsl:value-of select="#title"/></DIV>
</xsl:for-each>
</xsl:for-each-group>
</BODY>
</HTML>
</xsl:template>
</xsl:stylesheet>

XSLT, I'm studying with my own XSLT example, but it doesn't work as I expected

First, my code is..
<?xml version="1.0" encoding="UTF-8"?>
<mpml>
<problem>
<context>
<p>두 다항식 $A=x^2 - xy + 2y^2$, $B=3x^2 + 2xy - y^2$에 대하여 $A-B$를 계산한 식이 $ax^2 +bxy + cy^2$일 때, 상수 $a+b+c$의 값은?</p>
</context>
<answerlist>
<i>-4</i>
<i>-2</i>
<i>0</i>
<i>2</i>
<i>4</i>
</answerlist>
</problem>
<problem>
<context>
<p>연립방정식 $\begin{cases} x+y+z=30 \\ 2x+3y+4z=93 \\ y=z+3 \end{cases}$의 해를 $x=a$, $y=b$, $z=c$라 할 때, $a-2b+3c$의 값은? (단, $a$, $b$, $c$는 실수.)</p>
</context>
<answerlist>
<i>7</i>
<i>9</i>
<i>11</i>
<i>13</i>
<i>15</i>
</answerlist>
</problem>
</mpml>
and it's xsl code is..
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<h2> Testing.. </h2>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="problem">
<div class="problem">
<xsl:apply-templates select="context"/>
<xsl:apply-templates select="answerlist"/>
</div>
</xsl:template>
<xsl:template match="context">
<div class="context">
</div>
</xsl:template>
<xsl:template match="answerlist">
<div class="answerlist">
test answerlist
</div>
</xsl:template>
</xsl:transform>
In XSLT Tryit editor (thanks to w3school), above XSLT work as I expected. However when I tried this in my server, there is an 'extra content at the end of the document.' error and nothings shown except very first template.
I think the problem is that your XSLT does not produce well-formed XML in output: in the template <xsl:template match="/">, you should wrap content within a single root tag.
E.g.:
<xsl:template match="/">
<body>
<h2> Testing.. </h2>
<xsl:apply-templates/>
</body>
</xsl:template>

Template declared to match element, but never triggered

I've the following XML.
<?xml version="1.0" encoding="UTF-8"?>
<docs>
<biblos>
<texto xmlns="http://www.aranzadi.es/namespace/contenido/biblos/texto" idioma="spa">
<parrafo>
<en-origen estilo-fuente="cursiva">This is cursive text.</en-origen>
</parrafo>
</texto>
</biblos>
</docs>
and the following XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<body>
<section class="chapter">
<xsl:apply-templates select="docs"/>
</section>
</body>
</html>
</xsl:template>
<xsl:template match="docs">
<div class="chapter">
<xsl:text>Docs Block</xsl:text>
<xsl:apply-templates select="biblos"/>
</div>
</xsl:template>
<xsl:template match="biblos">
<xsl:text>biblos block</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="texto">
<xsl:text>Text To Block</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="parrafo">
<div class="para">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="parrafo">
<span class="format-smallcaps">
<xsl:apply-templates/>
</span>
</xsl:template>
<xsl:template match="en-origen">
<xsl:variable name="fontStyle">
<xsl:choose>
<xsl:when test="./#estilo-fuente">
<xsl:value-of select="concat('font-style-',#estilo-fuente)"/>
</xsl:when>
<xsl:when test="./#format">
<xsl:value-of select="concat('format-',#format)"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
<span class="{$fontStyle}">
<xsl:value-of select="."/>
<xsl:apply-templates select="para"/>
</span>
</xsl:template>
</xsl:transform>
when i run this, I'm getting the below output.
<!DOCTYPE html
PUBLIC "XSLT-compat">
<html>
<body>
<section class="chapter">
<div class="chapter">Docs Blockbiblos block
This is cursive text.
</div>
</section>
</body>
</html>
Here the problem is, though I've declared texto and child nodes of it in my XSLT, it is not getting called, but the text is directly getting printed.
Please let me know where I'm going wrong and how I can fix it.
Good question (thanks for providing a complete, working example!). Often, if elements are not matched, the cause lies in missing namespaces:
You have the following in your input XML:
<texto xmlns="http://www.aranzadi.es/namespace/contenido/biblos/texto" idioma="spa">
In other words, the texto element is in a namespace. In your XSLT you have the following:
<xsl:template match="texto">
Since no namespace is declared for XPath (xpath-default-namespace on the containing xsl:template or xsl:stylesheet), this will operate on elements texto in no namespace, meaning, as written, it will not match texto from your source.
You can solve this by:
<xsl:transform
xmlns:tto="http://www.aranzadi.es/namespace/contenido/biblos/texto"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
and:
<xsl:template match="tto:texto">
Now your template will be matched.
Remember that element names can be in a namespace if the namespace is declared on that element, but the attributes, unless prefixes, are in no nammespace, so this solution is only required (given your example input) on matching or selecting elements.
Also, it is important to realize that prefixes do not matter, they do not need to match the prefix (or absence thereof) from the source document. What matters, is that the namespace bound to the prefix matches.
If there are child elements, in this case parrafo and en-origen, these inherit the namespace given on their parent element. So if you want to match these elements as well, you should adjust their names to tto:paraffo and tto:en-origin in patterns and XPath expressions.

Find and replace a string inside an image path

I have a rss xml file that looks like below with the tag looping many times. I would like to replace the letter 's' with 'm' in the url inside the tag, So http://farm4.staticflickr.com/3802/9593294742_a38fca47c7_s.jpg becomes http://farm4.staticflickr.com/3802/9593294742_a38fca47c7_m.jpg
<rss xmlns:media="http://search.yahoo.com/mrss/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:creativeCommons="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html" xmlns:flickr="urn:flickr:user" version="2.0">
<channel>
<title>CCGalleria Pool</title>
<link>http://www.flickr.com/groups/ccgalleria/pool/</link>
<item>
<title>Hampton Court Palace Gardens</title>
<link>http://www.flickr.com/photos/dksesh/9593294742/in/pool-1540822#N20</link>
<description>
<p>
dksesh
has added a photo to the pool:</p>
<p>
<a href="http://www.flickr.com/photos/dksesh/9593294742/" title="Hampton Court Palace Gardens">
<img src="http://farm4.staticflickr.com/3802/9593294742_a38fca47c7_m.jpg" width="240" height="107" alt="Hampton Court Palace Gardens"/>
</a>
</p>
</description>
<pubDate>Sun, 25 Aug 2013 10:33:09 -0700</pubDate>
<dc:date.Taken>2013-08-10T16:49:05-08:00</dc:date.Taken>
<author flickr:profile="http://www.flickr.com/people/dksesh/">nobody#flickr.com (dksesh)</author>
<guid isPermaLink="false">tag:flickr.com,2004:/grouppool/1540822#N20/photo/9593294742</guid>
<media:content url="http://farm4.staticflickr.com/3802/9593294742_a38fca47c7_b.jpg" type="image/jpeg" height="456" width="1024"/>
<media:title>Hampton Court Palace Gardens</media:title>
<media:thumbnail url="http://farm4.staticflickr.com/3802/9593294742_a38fca47c7_s.jpg" HEIGHT="75" WIDTH="75"/>
<media:credit ROLE="photographer">dksesh</media:credit>
<creativeCommons:license>http://creativecommons.org/licenses/by-nd/2.0/deed.en_GB</creativeCommons:license>
</item>
</channel>
</rss>
I have a code something like below, but didn't work. Can someone help? Thanks very much. Newly changed code below.
<?xml version='1.0'?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:media="http://search.yahoo.com/mrss/">
<xsl:output method="html" />
<xsl:template match="/">
<xsl:for-each select="rss/channel/item">
<xsl:variable name="newurl" select="replace(media:thumbnail/#url,'_s.jpg','_m.jpg')"/>
<img src="{$newurl}" style="margin:5px 5px" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Your stylesheet sample showns the version as "2.0" which is good, because there is a replace function you can use in XSLT 2.0 that will probably do the trick here
<xsl:variable name="newurl" select="replace(media:thumbnail/#url,'_s.jpg','_m.jpg')"/>
<img src="{$newurl}" style="margin:5px 5px" />
Note that the second argument of the replace function can actually be a regular expression, if you wanted more control over what 's' needed to be replaced.
As an aside, do note the correct use of Attribute Value Templates here, signified by the curly braces { }. You use them in outputting the src attribute of the img element, but not within the translate/replace function.
Here is the full XSLT in this case:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:media="http://search.yahoo.com/mrss/">
<xsl:output method="html" />
<xsl:template match="/">
<xsl:for-each select="rss/channel/item">
<xsl:variable name="newurl" select="replace(media:thumbnail/#url,'_s.jpg','_m.jpg')"/>
<img src="{$newurl}" style="margin:5px 5px" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

XSLT - Grouping same items

I have the following XML:
<Info>
<Name>Dan</Name>
<Age>24</Age>
</Info>
<Info>
<Name>Tom</Name>
<Age>15</Age>
</Info>
<Info>
<Name>Dan</Name>
<Age>24</Age>
</Info>
<Info>
<Name>James</Name>
<Age>18</Age>
</Info>
And I need to produce the following HTML:
<ul class="data">
<li>Dan</li>
<li>Dan</li>
</ul>
<ul class="data">
<li>James</li>
</ul>
<ul class="data">
<li>Tom</li>
<li>Tom</li>
</ul>
As well as producing the output it needs to sort based on the Name also. Any help appreciated, started by looking at group-by but couldnt work out how to get it finished:
Pretty sure its wrong?
<xsl:for-each-group select="Info" group-by="#Name">??????
<xsl:for-each-group select="current-group()" sort-by="#Name">
I don't have an XSLT 2.0 parser, but to do it in XSLT 1.0 at least you could use Muenchian Grouping to do this...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" method="html" version="1.0"/>
<xsl:key name="Names" match="Name" use="."/>
<xsl:template match="/">
<xsl:for-each select="//Info[generate-id(Name) = generate-id(key('Names', Name)[1])]">
<xsl:sort select="Name" />
<ul class="data">
<xsl:for-each select="key('Names', Name)">
<li><xsl:value-of select="." /></li>
</xsl:for-each>
</ul>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I'm not sure why your result has two 'Tom' elements, I assume you have an extra node in the XML that you didn't provide in your sample.
Anyway, the XSLT would look something like this:
<xsl:for-each-group select="Info" group-by="Name">
<xsl:sort select="current-grouping-key()"/>
<ul class="data">
<xsl:for-each select="current-group()/Name">
<li><xsl:value-of select="." /></li>
</xsl:for-each>
</ul>
</xsl:for-each-group>
I don't have an XSLT 2.0 parser handy to test it, but I think that should work.
I don't have an xslt 2.0 parser to test this, but at the very least you will need to change "#Name" to just "Name", since it is a subelement not an attribute.