Dynamic HTML Table with XSLT - html

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.

Related

XSLT template to match multiple nodes is repeating the html

I am facing issues while displaying the xml in html table using xsl.
1. The html view is repeating with the below xsl and
2. unable to differentiate sub elements with the same name (Eg: name tag in the below xml).
The xml is having different kinds of information in different nodes as follows.
<employee>
<address>
<street>street1</street>
<city>city1</city>
<pincode>123456</pincode>
</address>
<personalinfo>
<name>testname1</name>
<phone>999999999</phone>
<dob>23-09-34</dob>
</personalinfo>
<remarks>
<education>
<name>testname2</name>
<college>college1</college>
<gpa>7.5</gpa>
</education>
</remarks>
</employee>
and Here is my xsl
<?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"/>
<xsl:template match="employee">
<html>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="address|personalinfo|remarks">
<table width="630">
<tr>
<td>Name</td>
<td>College</td>
<td>City</td>
</tr>
<tr>
<td><xsl:value-of select="//name"/></td>
<td><xsl:value-of select="//college"/></td>
<td><xsl:value-of select="//city"/></td>
</tr>
</table>
<span><br/>
</span>
</xsl:template>
</xsl:stylesheet>
Please help me in this regard. Thank you.
Judging from your table structure, you want to move the table header up to the template matching the root node, and then get the corresponding detail of each employee using a relative (and explicit) path:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:template match="/">
<html>
<body>
<table>
<tr>
<th>Name</th>
<th>College</th>
<th>City</th>
</tr>
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="employee">
<tr>
<td>
<xsl:value-of select="personalinfo/name"/>
</td>
<td>
<xsl:value-of select="remarks/education/college"/>
</td>
<td>
<xsl:value-of select="address/city"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
The expression //name selects all the name elements anywhere in the document. That's not you want: you want the name element within the personalInfo or remarks that you are currently processing: that's select="name" for an immediate child, or select=".//name" for a descendant at any depth.
It seems odd to use the same template rule to process three elements (address|personalinfo|remarks) that have very different structure.

Add attribute to XML root node using XSLT (and output existing XSLT with HTML?)

I apologize for this somewhat as there are similar questions around, however I've attempted to apply all of those and cannot get mine to work with existing stylesheet html output.
I have an existing XML and an existing XSLT which has HTML markup.
I Simply want use the XSLT to also add an attribute to my XML root node but still also output the XSLT HTML.
How would I do this?
My XML: (I want to add an xmlns attribute to the node: "StockList")
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="do_it.xsl"?>
<StockList>
<StockItem>
<Item>AX123</Item>
<Description>Firetruck</Description>
<Count>500</Count>
<Order>No</Order>
</StockItem>
</StockList>
My XSLT: (do_it.xsl)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:aa="http://www.w3schools.com/MMMXXX">
<xsl:template match="/">
<html>
<body>
<h2>Things On The Shelf:</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th style="text-align:left">Item Code</th>
<th style="text-align:left">Item Description</th>
<th style="text-align:left">Current Count</th>
<th style="text-align:left">On Order?</th>
</tr>
<xsl:for-each select="aa:StockList/aa:StockItem">
<tr>
<td><xsl:value-of select="aa:Item"/></td>
<td><xsl:value-of select="aa:Description"/></td>
<td><xsl:value-of select="aa:Count"/></td>
<td><xsl:value-of select="aa:Order"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
So what do I need to do to my XSLT and WHERE do I need to do it? I've attempted about 15 different 'apply templates' but while I manage to add the attribute I can't also add my html table? :(
Please help.
This has the ring of an XY problem. Just be assured that you cannot change your source document through XSLT, i.e. adding a new attribute to it so your existing stylesheet code could work. You also cannot produce an intermediate document and then transform that through XSLT 1.0 (at least as far as I know).
You have to do one of two things:
Modify whatever is generating the source XML so it correctly declares the namespace of <StockList> for you. You could do this like so:
using (MemoryStream ms = new MemoryStream())
{
dataSet.WriteXml(ms);
ms.Position = 0; // reset!
XDocument xd = XDocument.Load(ms);
XNamespace aa = "http://www.w3schools.com/MMMXXX";
foreach (var el in xd.Root.DescendantsAndSelf())
{
el.Name = aa.GetName(el.Name.LocalName);
}
xd.Root.Add(new XAttribute(XNamespace.Xmlns + "aa", aa);
xd.Save(ms); // or just return the XDocument.ToString(), or return XDocument.CreateReader() as desired
}
Modify your XSLT so that it doesn't care about namespaces. For example, change <xsl:for-each select="aa:StockList/aa:StockItem"> to <xsl:for-each select="*[local-name()='StockList']/*[local-name()='StockItem']"> and so on everywhere you have a select using that namespace prefix. This is probably the easier fix - I'd go with this solution myself.
If you want to apply two transformations in one style sheet with XSLT 1.0 where the first adds the namespace (you would need to add it to all elements) and the second transforms the result of the first to HTML then you need to perform one step with a variable that is a result tree fragment and then convert that result tree fragment to a node set to apply your for-each or other templates. Converting a result tree fragment to a node set can be done with most XSLT processors using the EXSLT extension function exsl:node-set (http://exslt.org/exsl/functions/node-set/index.html), however MSXML used by IE does not support that EXSLT function, only a similar function in a different namespace. Using the ms:script extension element it is however possible to have MSXML support exsl:node-set, so a complete style sheet doing two transformations looks like
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:aa="http://example.com/ns1"
xmlns:exsl="http://exslt.org/common"
xmlns:ms="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="aa exsl ms"
version="1.0">
<ms:script language="JScript" implements-prefix="exsl">
this['node-set'] = function(nodeList) {
return nodeList;
}
</ms:script>
<xsl:variable name="rtf1">
<xsl:apply-templates mode="add-namespace"/>
</xsl:variable>
<xsl:template match="*" mode="add-namespace">
<xsl:element name="aa:{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="add-namespace"/>
</xsl:element>
</xsl:template>
<xsl:template match="/">
<html>
<body>
<h2>Things On The Shelf:</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th style="text-align:left">Item Code</th>
<th style="text-align:left">Item Description</th>
<th style="text-align:left">Current Count</th>
<th style="text-align:left">On Order?</th>
</tr>
<xsl:for-each select="exsl:node-set($rtf1)/aa:StockList/aa:StockItem">
<tr>
<td><xsl:value-of select="aa:Item"/></td>
<td><xsl:value-of select="aa:Description"/></td>
<td><xsl:value-of select="aa:Count"/></td>
<td><xsl:value-of select="aa:Order"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
The example is online at http://home.arcor.de/martin.honnen/xslt/test2015100701.xml respectively http://home.arcor.de/martin.honnen/xslt/test2015100701.xsl.
Works as intended with current versions of Firefox, Chrome and IE on Windows 10. Unfortunately Microsoft Edge complains about an error in the XSLT, I am not sure what causes this as Edge like IE 11 uses MSXML 6 to do XSLT. To make it work with Edge I rewrote the style sheet not to make use of ms:script and instead use function-available and xsl:when:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:aa="http://example.com/ns1"
xmlns:exsl="http://exslt.org/common"
xmlns:ms="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="aa exsl ms"
version="1.0">
<xsl:variable name="rtf1">
<xsl:apply-templates mode="add-namespace"/>
</xsl:variable>
<xsl:template match="*" mode="add-namespace">
<xsl:element name="aa:{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="add-namespace"/>
</xsl:element>
</xsl:template>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="function-available('exsl:node-set')">
<xsl:apply-templates select="exsl:node-set($rtf1)/aa:StockList"/>
</xsl:when>
<xsl:when test="function-available('ms:node-set')">
<xsl:apply-templates select="ms:node-set($rtf1)/aa:StockList"/>
</xsl:when>
<xsl:otherwise>
<html>
<body>
<p>Your XSLT processor does not support exsl:node-set or ms:node-set.</p>
</body>
</html>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/aa:StockList">
<html>
<body>
<h2>Things On The Shelf:</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th style="text-align:left">Item Code</th>
<th style="text-align:left">Item Description</th>
<th style="text-align:left">Current Count</th>
<th style="text-align:left">On Order?</th>
</tr>
<xsl:for-each select="aa:StockItem">
<tr>
<td><xsl:value-of select="aa:Item"/></td>
<td><xsl:value-of select="aa:Description"/></td>
<td><xsl:value-of select="aa:Count"/></td>
<td><xsl:value-of select="aa:Order"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
That way of course it works with Firefox, Chrome and IE as well.

XML + XSL to XSLT for an HTML

Sorry if my Post won´t be so good i am not that good in expressing myself.
What i want:
I´ve got an XML and a XSL Data i want to convert to an XSLT for an HTML.
As transformating i use Altova XMLSpy and this webpage: http://www.freeformatter.com/xsl-transformer.html
What i´ve got:
My XML:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="Style.xsl"?>
<Configuration>
<Environment>
...
</Environment>
<Configurations>
...
<Config>
...
</Config>
</Configurations>
</Configuration>
My XSL:
<?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="/">
<html>
<body>
<table border="4">
<xsl:apply-templates select="Configuration"/>
</table>
</body>
</html>>
</xsl:template>
<xsl:template match="Environment">
<tr>Environment</tr>
<br/>
<td> <th>Text</th>
<xsl:value-of select="Environment"/>
</td>
<td> <th>Text2</th>
<xsl:value-of select="config"/>
</td>
...
...
</xsl:template>
</xsl:stylesheet>
This is just a example how i build it.
so far everything was fine, but then:
Problem:
If i start my XML in Firefox now everything works. If i start it in IE or Chrome i just get the XML Data but not the Table.
Trying to transform it in XSLT gives me this Error:
Unable to perform XSL transformation on your XML file. null
I hope you can help me. I am a bloody beginner in XML/XSl/XSLT but i hope my post will give you the Information you need.
Greetings
Max
You have made a very common beginner's mistake:
<xsl:template match="Environment">
<tr>Environment</tr>...
<xsl:value-of select="Environment"/>
In this template, the context node is an Environment element, and your xsl:value-of instruction is looking for a child of that element with the name Environment. But your Environment element (I assume) does not have any Environment children. What you should write (I assume) is
<xsl:value-of select="."/>
However, I don't think this is the whole problem. It doesn't account for all the symptoms described. You've actually made another beginner's mistake, which is trying to run your XSLT code in the browser before testing it elsewhere. The browser-based XSLT engines give lousy diagnostics (even when you use something like Firebug). Get your code working first in a desktop environment - preferably an XML IDE like oXygen. You'll then get a decent explanation of what is wrong.
Hi Here Is The Modified Version of Your Code.
XSL Template Match Must match the XML node you wish to apply templates.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:stylesheet version="2.0">
<xsl:template match="Configuration">
<html>
<body>
<table border="4">
<xsl:apply-templates select="Environment"/>
</table>
<table border="4">
<xsl:apply-templates select="Configurations"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="Environment">
<tr>
<th>
<xsl:value-of select="name()"/>
</th>
</tr>
<tr>
<td>
<xsl:value-of select="."/>
</td>
</tr>
</xsl:template>
<xsl:template match="Configurations">
<tr>
<th>
<xsl:value-of select="name()"/>
</th>
</tr>
<tr>
<td>
<xsl:apply-templates select="Config"/>
</td>
</tr>
</xsl:template>
<xsl:template match="Config">
<tr>
<th>
<xsl:value-of select="name()"/>
</th>
</tr>
<tr>
<td>
<xsl:value-of select="."/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>

To transform XML to HTML in table format

I want to change this xml content to HTML table
<SSI>
<data>
<expanded>Chemical Research</expanded><abbre>Chem. Res.</abbre>
<expanded>Materials Journal</expanded><abbre>Mater. J.</abbre>
<expanded>Chemical Biology</expanded><abbre>Chem. Biol.</abbre>
<expanded>Symposium Series</expanded><abbre>Symp. Ser.</abbre>
<expanded>Biochimica Polonica</expanded><abbre>Biochim. Pol.</abbre>
<expanded>Chemica Scandinavica</expanded><abbre>Chem. Scand.</abbre>
<\data>
<data>
<expanded>Botany</expanded><abbre>Bot.</abbre>
<expanded>Chemical Engineering</expanded><abbre>Chem. Eng.</abbre>
<expanded>Chemistry</expanded><abbre>Chem.</abbre>
<expanded>Earth Sciences</expanded><abbre>Earth Sci.</abbre>
<expanded>Microbiology</expanded><abbre>Microbiol.</abbre>
<\data>
<\SSI>
Tried with following XSL
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html>
<head><title>Abbreviate</title></head>
<body>
<table border="1">
<tr>
<th>Expanded</th>
<th>Abbre</th>
</tr>
<xsl:for-each select="SSI/data">
<tr>
<td><xsl:value-of select="expanded"/></td>
<td><xsl:value-of select="abbre"/></td>
</tr>
</xsl:for-each>
</table>
</body></html>
</xsl:template>
</xsl:stylesheet>
I got only the first entry of data tag in HTML Table format
Expanded Abbre
----------- --------------------
Chemical Research Chem. Res
Botany Bot.
how can get all the values in HTMl???
If you clean up your XSLT and use xsl:apply-templates rather than xsl:for-each, life will become simpler. There is almost never a reason to use xsl:for-each. Try this:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html>
<head><title>Abbreviate</title></head>
<body>
<table border="1">
<tr>
<th>Expanded</th>
<th>Abbre</th>
</tr>
<xsl:apply-templates select='SSI/data/expanded'/>
</table>
</body></html>
</xsl:template>
<xsl:template match="expanded">
<tr>
<td><xsl:apply-templates/></td>
<xsl:apply-templates select='following-sibling::abbre[1]'/>
</tr>
</xsl:template>
<xsl:template match="abbre">
<td><xsl:apply-templates/></td>
</xsl:template>
</xsl:stylesheet>
By using small templates that are applied, you simplify your stylesheet. Additionally, there is no real reason to use xsl:value-of here - the built-in templates will do the right thing. You will end up with simpler templates that are easier to understand.

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.