How to compare XML element with XSL variable - html

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.

Related

How to add conditions to xsl for-each statements

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]">

Dynamic HTML Table with XSLT

I need to loop over a group of values and print them into a two-column table.
I thought about following solution (must be xslt1)
<table class="main">
<xsl:for-each select="Attribute/Gruppe">
<xsl:if test="current()/#ID=20064490">
<xsl:variable name="open_row"><![CDATA[<tr><td style="width:50%;">
<xsl:value-of select="current()/#name" /></td>]]></xsl:variable>
<xsl:variable name="closing_row"><![CDATA[<td style="width:50%;">
<xsl:value-of select="current()/#name" /></td></tr>]]></xsl:variable>
<xsl:variable name="table">
<xsl:for-each select="*">
<xsl:choose>
<xsl:when test="(position() mod 2) = 1">
<xsl:value-of select="$open_row"
disable-output-escaping="yes" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$closing_row"
disable-output-escaping="yes" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="$table"
disable-output-escaping="yes" />
</xsl:if>
</xsl:for-each>
</table>
I modified the code now I am using position() to find out if an closing <tr> or an opening </tr> is required.
The whole problem might be summarized to that you can not write single tags in XSLT. And that disable-output-escaping is not working.
Resulting HTML should be https://jsfiddle.net/dwetctm6/
For all the Nodes in the group. The order of the nodes in the table does not matter.
Furthermore assume following XML:
<bgroup>
<NODE1>text</NODE1>
<NODE2>text</NODE2>
<NODE n-1>text</NODE n-1>
<NODE n>text</NODE n>
</bgroup>
Dividing nodes into a two-column table is pretty trivial - especially if the order (across-first or down-first) does not matter.
Consider the following stylesheet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/bgroup">
<table border="1">
<xsl:for-each select="*[position() mod 2 = 1]">
<tr>
<td><xsl:value-of select="."/></td>
<td><xsl:value-of select="following-sibling::*[1]"/></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Applied to the following well-formed inpout:
XML
<bgroup>
<NODE1>A</NODE1>
<NODE2>B</NODE2>
<NODE3>C</NODE3>
<NODE4>D</NODE4>
<NODE5>E</NODE5>
</bgroup>
the result will be:
<table border="1">
<tr>
<td>A</td>
<td>B</td>
</tr>
<tr>
<td>C</td>
<td>D</td>
</tr>
<tr>
<td>E</td>
<td/>
</tr>
</table>
rendered as:
Well, a few remarks must be made first of all:
You must learn how XSLT works: It is a language for templating that, being based on XML, must be composed in an ordered way: Every node open must be closed within the same scope. michael.hor257k is right: You shall not open a node within an scope and close it in another different one.
Also, there must be said that your input XML does not seem to be properly structured: It should be a repetition of nodes with same names and types. If all the NODE-n nodes are functionally or semantically equal, they should have the same name, no problem: You can have several nodes with the same name.
And now, the solution: Asuming your XML is as you have posted it, and since there can't be a templating definition based upon named nodes with different names, I have matched all the child nodes of the bgroup root node. So, the XSL can be as easy as this:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/bgroup">
<html>
<body>
<table>
<xsl:apply-templates select="*[position() mod 2 =1]"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="*">
<tr>
<td><xsl:value-of select="."/></td>
<td><xsl:value-of select="following-sibling::*[1]"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
It's important that you realise how, thanks to the ordered structure of the XSL language, you can see how the output will be with just a glimpse of the XSL template.

XSLT: Transforming HTML attributes into inline CSS

I have invalid HTML that I am trying to transform into valid HTML using an XSLT transformation. For example, I want to turn some attributes into inline CSS. Consider the following:
<table border="1" id="t01" width="100%">
.
.
.
</table>
The border and width attributes on the table element are obsolete. So I want to use inline CSS instead, like this:
<table style="border:1;width:100%;" id="t01">
.
.
.
</table>
How can I do this with XSLT?
You could certainly make this prettier, more generic, etc., but my first pass would be something like:
<xsl:template match="table">
<table>
<xsl:attribute name="style">
<xsl:if test="#border">
<xsl:value-of select="concat('border:', #border, '; ')"/>
</xsl:if>
<xsl:if test="#width">
<xsl:value-of select="concat('width:', #width, '; ')"/>
</xsl:if>
</xsl:attribute>
<xsl:copy-of select="#id"/>
<!-- either this or apply-templates -->
<xsl:copy-of select="*"/>
</table>
</xsl:template>

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>