How to add conditions to xsl for-each statements - html

I'm trying to filter out any amount below $1,000,000. Here is the code:
<xsl:for-each select="row">
<tr>
<xsl:for-each select="cell">
<xsl:if test="position()=5">
<td width="140" style="vertical-align: top; padding: 3px;" valign="top">
<xsl:choose>
<xsl:when test="#href">
<a href="">
<xsl:attribute name="href">
<xsl:value-of select="#href" />
</xsl:attribute>
<xsl:value-of select="." />
</a>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</td>
</xsl:if>
<xsl:if test="position()=8">
<td class="categorydetail-last-column" style="text-align: right; vertical-align: top; padding: 3px;" align="right" valign="top">
<xsl:value-of select="." /><!-- THIS OUTPUTS NUMBERS. WANT TO FILTER NUMBERS BELOW 1000000 -->
</td>
</xsl:if>
</xsl:for-each>
</tr>
</xsl:for-each>
It outputs dollar amount but it needs to output if it's bigger than one million. I tried creating variables and wrapping the whole code in IF statement but it didn't work. What am I doing wrong? Do I have to add condition to the ROW?

As I understood, you want to process only rows in which cell No 8
contains value less than a million.
So use a template with the match condition specifying just this
filtering criterion:
<xsl:template match="row[cell[8] < 1000000]">
...
</xsl:template>
If your XSLT script contains also an identity template, you must
somehow "block" other rows from proessing. You can do it with a
"more general", empty template, for all rows:
<xsl:template match="row"/>
Actually, it would be applied to all "other" rows (to which the former
template does not apply).
Another solution (more close to the script you posted):
Change the "external" loop to:
<xsl:for-each select="row[cell[8] < 1000000]">

Related

Using XSL, I need to check links' href value and change if necessary while leaving other attributes alone

The following code does the following:
Finds links
Checks to see if it's a fully pathed link or an email link
If yes, it does nothing.
If no, it checks for a # to see if it's an anchor
If yes, it does nothing.
If no, it concatenates the domain in front.
This works, but if I add another attribute to the link, it strips it out as well. For example: My link
How do I keep the behavior below but also keep the style attribute?
<xsl:template match="a[#href][not(contains(#href, 'http')) and not(contains(#href, 'mailto'))]">
<xsl:choose>
<xsl:when test="starts-with(#href, '#')">
<a href="{#href}" ><xsl:value-of select="." /></a>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I am guessing (!) you want to do something like:
<xsl:template match="a[#href][not(contains(#href, 'http')) and not(contains(#href, 'mailto'))]">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:attribute name="href">
<xsl:if test="not(starts-with(#href, '#'))">
<xsl:value-of select="$domain" />
</xsl:if>
<xsl:value-of select="#href"/>
</xsl:attribute>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
Or possibly just:
<xsl:template match="a/#href[not(contains(., 'http') or contains(., 'mailto') or starts-with(., '#'))]">
<xsl:attribute name="href">
<xsl:value-of select="$domain" />
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
Untested, because no example was provided.

Facing issues in parsing xml with xslt

I am trying to parse a xml to generate a html report.The problematic section of the xml is as given
<failure message="Management changes link count is not 3$HiHello" type="junit.framework.AssertionFailedError">junit.framework.AssertionFailedError: Management changes link count is not 3$HiHelloJI
at CustomProjects.CommonTemplates.verifyManagementChanges(Unknown Source)
at CustomProjects.EmersonTest.testEmerson_VerifyManagementChanges(Unknown Source)
</failure>
The xslt written for parsing this is :
<xsl:choose>
<xsl:when test="failure">
<td>Failure</td>
<td><xsl:apply-templates select="failure"/></td>
<td>screenshot</td>
<td><xsl:apply-templates select="failurelink"/></td>
</xsl:when>
</xsl:choose>
<xsl:template match="failure">
<xsl:call-template name="display-failures"/>
</xsl:template>
<xsl:template match="failurelink">
<xsl:call-template name="display-failures-link"/>
</xsl:template>
<xsl:template name="display-failures">
<xsl:param name="FailText" select="#message"/>
<xsl:choose>
<xsl:when test="not(#message)">N/A</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-before($FailText,'$')"/>
</xsl:otherwise>
</xsl:choose>
<!-- display the stacktrace -->
<code>
<br/><br/>
<xsl:call-template name="br-replace">
<xsl:with-param name="word" select="."/>
</xsl:call-template>
</code>
<!-- the later is better but might be problematic for non-21" monitors... -->
<!--pre><xsl:value-of select="."/></pre-->
</xsl:template>
<xsl:template name="display-failures-link">
<xsl:param name="linktext" select="#message"/>
<xsl:choose>
<xsl:when test="not(#message)">N/A</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-after($linktext,'$')"/>
</xsl:otherwise>
</xsl:choose>
<!-- display the stacktrace -->
<code>
<br/><br/>
<xsl:call-template name="br-replace">
<xsl:with-param name="word" select="."/>
</xsl:call-template>
</code>
<!-- the later is better but might be problematic for non-21" monitors... -->
<!--pre><xsl:value-of select="."/></pre-->
</xsl:template>
Here I am getting the desired result(The String before $ sign) from display-failures template but on calling display-failures-link I am getting nothing.(Should get the string after $ sign).I dont know whether the problem is with sunstring function or with something else.Kindly let me know what I am doing wrong here.
Any help is highly appreciated.
The problem here is that you are trying to apply-templates on the XPath failurelink, but you don't have an element called <failurelink>, so this apply-templates isn't finding anything.
<xsl:apply-templates select="failurelink"/>
One way to apply two different templates on the same kind of element is to use modes:
<xsl:template match="failure">
<xsl:call-template name="display-failures"/>
</xsl:template>
<xsl:template match="failure" mode="link">
<xsl:call-template name="display-failures-link"/>
</xsl:template>
Then the area where you apply the templates would change to this:
<td>Failure</td>
<td><xsl:apply-templates select="failure"/></td>
<td>screenshot</td>
<td><xsl:apply-templates select="failure" mode="link"/></td>
But in your case, there's an even better approach. Just eliminate the second template, and do this:
Replace the whole <xsl:choose> with:
<xsl:apply-templates select="failure" />
Replace the first template you listed with:
<xsl:template match="failure">
<td>Failure</td>
<td><xsl:call-template name="display-failures"/></td>
<td>screenshot</td>
<td><xsl:call-template name="display-failures-link"/></td>
</xsl:template>
And delete the second template you listed.

Convert output of xsl to a url upon conditions

I'm very much a novice at the moment, however I have used xsl to format an xml feed to out put into html for my site. However, I would like to go a step further by also converting some of the output text to an html link.
Are there any tutorials available that could help?
To give a bit better context, the output is a football league table, that I would like to make the teams names automatically link to a url. So if name = 'Portsmouth' then I would want Portsmouth to become a link I would determine. How do I format the table below to do this for all the potentially different team names?
<xsl:for-each select="team">
<tr>
<td><xsl:value-of select="position"/></td>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of select="played"/></td>
<td><xsl:value-of select="won"/></td>
<td><xsl:value-of select="drawn"/></td>
<td><xsl:value-of select="lost"/></td>
<td><xsl:value-of select="for"/></td>
<td><xsl:value-of select="against"/></td>
<td><xsl:value-of select="goalDifference"/></td>
<td><xsl:value-of select="points"/></td>
</tr>
`
If you want to conditionally output a tags you can do the following.
<xsl:template match="/">
<xsl:apply-templates select="//team"/>
</xsl:template>
<xsl:template match="team">
<td>
<xsl:value-of select="position"/>
</td>
<td>
<xsl:choose>
<xsl:when test="name='Portsmouth'">
<a>
<xsl:attribute name="href">
<xsl:value-of select="concat('someurl.com?name=',name)"/>
</xsl:attribute>
</a>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="name"/>
</xsl:otherwise>
</xsl:choose>
</td>
<td>
<xsl:value-of select="played"/>
</td>
<td>
<xsl:value-of select="won"/>
</td>
<td>
<xsl:value-of select="drawn"/>
</td>
<td>
<xsl:value-of select="lost"/>
</td>
<td>
<xsl:value-of select="for"/>
</td>
<td>
<xsl:value-of select="against"/>
</td>
<td>
<xsl:value-of select="goalDifference"/>
</td>
<td>
<xsl:value-of select="points"/>
</td>
</xsl:template>
Use apply-templates instead of a foreach loop.
If one of the teams is Portsmouth, the output would be
<td><a href="someurl.com?name=Portsmouth"/></td>
If you want each team to have a url then simply remove the choose statement and leave
<td>
<a>
<xsl:attribute name="href">
<xsl:value-of select="concat('someurl.com?name=',name)"/>
</xsl:attribute>
</a>
</td>

How to compare XML element with XSL variable

I am using xslt to transform an xml document to html for use in an email. I need to compare xml elements with another xml element value so that I know what format to give the value. Basically I have an xml structure as such:
<main>
<comparer>1</comparer>
<items>
<item>
<name>blarg</name>
<values>
<value>1</value>
<value>2</value>
</values>
</items>
</main>
The item information is being used to build a table:
<table>
<tr>
<td>blarg</td>
<td>1</td>
<td>2</td>
</tr>
</table>
What I need to be able to do is use xsl to compare the item values with the 'comparer' node value and if they are equal then bold the cell in the table otherwise the cell value i snot bolded. I need to accomplish this without the use of javascript so it has to be done in xsl. Right now, I am looking at using a xsl:variable then attempting to use the xsl:when to do the compare. Unfortunately, I am having little luck. This is what I have just started playing with for each row in the table:
<xsl:variable name="compare" select="//main/comparer" />
...
<xsl:for-each select="value">
<td>
<xsl:choose>
<xsl:when test=". = $compare">
<b>
<xsl:value-of select="."/>
</b>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>.
</xsl:otherwise>
</xsl:choose>
</td>
</xsl:for-each>
*Note: I left out most of the xsl for brevity. I'm just trying to focus on my issue.
I figured it out after some trial and error. Alejandro's answer appears that it would work, but I do not have the luxury of restructuring the xsl to make use of templating. Here is what I used to solve my issue:
<xsl:variable name="compare" select="//main/comparer" />
...
<xsl:for-each select="value">
<td>
<xsl:choose>
<xsl:when test="contains(., $expireDate)">
<b>
<xsl:value-of select="."/>
</b>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>.
</xsl:otherwise>
</xsl:choose>
</td>
</xsl:for-each>
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="main">
<table>
<xsl:apply-templates select="items"/>
</table>
</xsl:template>
<xsl:template match="item">
<tr>
<xsl:apply-templates/>
</tr>
</xsl:template>
<xsl:template match="name|value">
<td>
<xsl:apply-templates/>
</td>
</xsl:template>
<xsl:template match="value/text()[.=/main/comparer]">
<b>
<xsl:value-of select="."/>
</b>
</xsl:template>
</xsl:stylesheet>
Output:
<table>
<tr>
<td>blarg</td>
<td>
<b>1</b>
</td>
<td>2</td>
</tr>
</table>
Note: Pattern matching and node set comparison.

XSL character escape problem

I am writing this because I have really hit the wall and cannot go ahead. In my database I have escaped HTML like this: "<p>My name is Freddy and I was".
I want to show it as HTML OR strip the HTML tags in my XSL template. Both solutions will work for me and I will choose the quicker solution.
I have read several posts online but cannot find a solution. I have also tried disable-output-escape with no success. Basically it seems the problem is that somewhere in the XSL execution the engine is changing this <p> into this: &lt;p&gt;.
It is converting the & into &. If it helps, here is my XSL code. I have tried several combinations with and without the output tag on the top.
Any help will be appreciated. Thanks in advance.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes"/>
<xsl:template match="DocumentElement">
<div>
<xsl:attribute name="id">mySlides</xsl:attribute>
<xsl:apply-templates>
<xsl:with-param name="templatenumber" select="0"/>
</xsl:apply-templates>
</div>
<div>
<xsl:attribute name="id">myController</xsl:attribute>
<xsl:apply-templates>
<xsl:with-param name="templatenumber" select="1"/>
</xsl:apply-templates>
</div>
</xsl:template>
<xsl:template match="DocumentElement/QueryResults">
<xsl:param name="templatenumber">tobereplace</xsl:param>
<xsl:if test="$templatenumber=0">
<div>
<xsl:attribute name="id">myController</xsl:attribute>
<div>
<xsl:attribute name="class">article</xsl:attribute>
<h2>
<a>
<xsl:attribute name="class">title</xsl:attribute>
<xsl:attribute name="title"><xsl:value-of select="Title"/></xsl:attribute>
<xsl:attribute name="href">/stories/stories-details/articletype/articleview/articleid/<xsl:value-of select="ArticleId"/>/<xsl:value-of select="SEOTitle"/>.aspx</xsl:attribute>
<xsl:value-of select="Title"/>
</a>
</h2>
<div>
<xsl:attribute name="style">text-indent: 25px;</xsl:attribute>
<xsl:attribute name="class">articlesummary</xsl:attribute>
<xsl:call-template name="removeHtmlTags">
<xsl:with-param name="html" select="Summary" />
</xsl:call-template>
</div>
</div>
</div>
</xsl:if>
<xsl:if test="$templatenumber=1">
<div>
<xsl:attribute name="id">myController</xsl:attribute>
<span>
<xsl:attribute name="class">jFlowControl</xsl:attribute>
aa
</span>
</div>
</xsl:if>
</xsl:template>
<xsl:template name="removeHtmlTags">
<xsl:param name="html"/>
<xsl:choose>
<xsl:when test="contains($html, '<')">
<xsl:value-of select="substring-before($html, '<')"/>
<!-- Recurse through HTML -->
<xsl:call-template name="removeHtmlTags">
<xsl:with-param name="html" select="substring-after($html, '>')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$html"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Based in the assumption that you have this HTML string,
<p>My name is Freddy & I was
then if you escape it and store it in a database it would become this:
<p>My name is Freddy &amp; I was
Consequently, if you retrieve it as XML (without unescaping it beforehand), the result would be this:
&lt;p&gt;My name is Freddy &amp;amp; I was
and <xsl:value-of select="." disable-output-escaping="yes" /> would produce:
<p>My name is Freddy &amp; I was
You are getting exactly the same thing you have in your database, but of course you see the HTML tags in the output. So what you need is a mechanism that does the following string replacements:
"&lt;" with "<" (effectively changing < to < in unescaped ouput)
"&gt;" with ">" (effectively changing > to > in unescaped ouput)
"&quot;" with """ (effectively changing " to " in unescaped ouput)
"&amp;" with "&" (effectively changing & to & in unescaped ouput)
From your XSL I have inferred the following test input XML:
<DocumentElement>
<QueryResults>
<Title>Article 1</Title>
<ArticleId>1</ArticleId>
<SEOTitle>Article_1</SEOTitle>
<Summary>&lt;p&gt;Article 1 summary &amp;amp; description.&lt;/p&gt;</Summary>
</QueryResults>
<QueryResults>
<Title>Article 2</Title>
<ArticleId>2</ArticleId>
<SEOTitle>Article_2</SEOTitle>
<Summary>&lt;p&gt;Article 2 summary &amp;amp; description.&lt;/p&gt;</Summary>
</QueryResults>
</DocumentElement>
I have changed the stylesheet you supplied and implemented such a replacement mechanism. If you apply the following XSLT 1.0 template to it:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:namespace"
exclude-result-prefixes="my"
>
<xsl:output method="html" omit-xml-declaration="yes"/>
<my:unescape>
<my:char literal="<" escaped="&lt;" />
<my:char literal=">" escaped="&gt;" />
<my:char literal=""" escaped="&quot;" />
<my:char literal="&" escaped="&amp;" />
</my:unescape>
<xsl:template match="DocumentElement">
<div id="mySlides">
<xsl:apply-templates mode="slides" />
</div>
<div id="myController">
<xsl:apply-templates mode="controller" />
</div>
</xsl:template>
<xsl:template match="DocumentElement/QueryResults" mode="slides">
<div class="article">
<h2>
<a class="title" title="{Title}" href="{concat('/stories/stories-details/articletype/articleview/articleid/', ArticleId, '/', SEOTitle, '.aspx')}">
<xsl:value-of select="Title"/>
</a>
</h2>
<div class="articlesummary" style="text-indent: 25px;">
<xsl:apply-templates select="document('')/*/my:unescape/my:char[1]">
<xsl:with-param name="html" select="Summary" />
</xsl:apply-templates>
</div>
</div>
</xsl:template>
<xsl:template match="DocumentElement/QueryResults" mode="controller">
<span class="jFlowControl">
<xsl:text>aa </xsl:text>
<xsl:value-of select="Title" />
</span>
</xsl:template>
<xsl:template match="my:char">
<xsl:param name="html" />
<xsl:variable name="intermediate">
<xsl:choose>
<xsl:when test="following-sibling::my:char">
<xsl:apply-templates select="following-sibling::my:char[1]">
<xsl:with-param name="html" select="$html" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$html" disable-output-escaping="yes" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="unescape">
<xsl:with-param name="html" select="$intermediate" />
</xsl:call-template>
</xsl:template>
<xsl:template name="unescape">
<xsl:param name="html" />
<xsl:choose>
<xsl:when test="contains($html, #escaped)">
<xsl:value-of select="substring-before($html, #escaped)" disable-output-escaping="yes"/>
<xsl:value-of select="#literal" disable-output-escaping="yes" />
<xsl:call-template name="unescape">
<xsl:with-param name="html" select="substring-after($html, #escaped)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$html" disable-output-escaping="yes"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Then this output HTML is produced:
<div id="mySlides">
<div class="article">
<h2>
<a class="title" title="Article 1" href="/stories/stories-details/articletype/articleview/articleid/1/Article_1.aspx">Article 1</a>
</h2>
<div class="articlesummary" style="text-indent: 25px;">
<p>Article 1 summary & description.</p>
</div>
</div>
<div class="article">
<h2>
<a class="title" title="Article 2" href="/stories/stories-details/articletype/articleview/articleid/2/Article_2.aspx">Article 2</a>
</h2>
<div class="articlesummary" style="text-indent: 25px;">
<p>Article 2 summary & description.</p>
</div>
</div>
</div>
<div id="myController">
<span class="jFlowControl">aa Article 1</span>
<span class="jFlowControl">aa Article 2</span>
</div>
Note
the use of a temporary namespace and embedded elements (<my:unescape>) to create a list of characters to replace
the use of recursion to emulate an iterative replacement of all affected characters in the input
the use of the implicit context within the unescape template to transport the information which character is to be replaced at the moment
Furthermore note:
the use of template modes to get different output for the same input (this replaces your templatenumber parameter)
most of the time there is no need for <xsl:attribute> elements. They can safely be replaced by inline notation (attributename="{attributevalue}")
the use of the concat() function to create the URL
Generally speaking, it is a bad idea to store escaped HTML in a database (more generally speaking: It is a bad idea to store HTML in a database.). You set yourself up to get all kinds of problems, this being one of them. If you can't change this setup, I hope that the solution helps you.
I cannot guarantee that it does the right thing in all situations, and it may open up security holes (think XSS), but dealing with this was not part of the question. In any case, consider yourself warned.
I need a break now. ;-)
You shouldn't store escaped HTML in your database. If your database contained the actual "<" character, then the "disable-output-escaping" command would do what you wanted.
If you can't change the data then you'll have to unescape the data before your perform the transform.
Add this line to your stylesheet
<xsl:output method="html" indent="yes" version="4.0"/>
It is a bad idea to store HTML in a database
What? How are you supposed to store it then? In an XML doc so you have to use XSLT anyway? As a web developer, we've always used SQL databases to store user-defined HTML data. There's nothing wrong with that method as long as it is sanitized properly for your purposes.