How to transform XML attributes to an HTML table - html

Here is an excerpt from an xsl document (inside a template technically):
<table>
<tr>
<th>INSTANCE</th>
<th>SWVER</th>
<th>SYSTEMID</th>
<th>SYSTIME</th>
<th>SYSMEM</th>
<th>CUTMEM</th>
<th>FILEMEM</th>
<th>CALCONFIG</th>
</tr>
<tr>
<td><xsl:value-of select='#INSTANCE'/></td>
<td><xsl:value-of select='#SWVER'/></td>
<td><xsl:value-of select='#SYSTEMID'/></td>
<td><xsl:value-of select='#SYSTIME'/></td>
<td><xsl:value-of select='#SYSMEM'/></td>
<td><xsl:value-of select='#CUTMEM'/></td>
<td><xsl:value-of select='#FILEMEM'/></td>
<td><xsl:value-of select="#CALCONFIG"/></td>
</tr>
</table>
Is there some way that I can avoid the redundancy of writing out the attributes both as table headers and as the attribute selection? I cannot use external sources.
I was thinking I could define some xsl variable that contains a basic structure, as follows and generate the table from there.
<list>
<item>INSTANCE</item>
...
<item>CALCONFIG</item>
</list>
The XML data is just a bunch of tags, of the same value that contain at least the above listed attributes. Each tag looks something like this:
<THING INSTANCE="boop" SWVER="foo" SYSTEMID="123"
...
CALCONFIG="cal.cfg" SOMETHINGELSE="bar"
/>

To illustrate the point I made in a comment to your question, consider the following example:
XML
<root>
<item color="red" name="alpha" size="small" garbage="123" id="1"/>
<item color="green" name="bravo" size="medium" garbage="456" id="2"/>
<item color="blue" name="charlie" size="large" garbage="789" id="3"/>
</root>
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://www.example.com/my"
exclude-result-prefixes="my">
<xsl:output method="html" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<my:columns>
<col>id</col>
<col>name</col>
<col>size</col>
<col>color</col>
</my:columns>
<xsl:variable name="columns" select="document('')/xsl:stylesheet/my:columns" />
<xsl:template match="/root">
<table border="1">
<tr>
<xsl:for-each select="$columns/col">
<th>
<xsl:value-of select="." />
</th>
</xsl:for-each>
</tr>
<xsl:for-each select="item">
<xsl:variable name="attributes" select="#*" />
<tr>
<xsl:for-each select="$columns/col">
<td>
<xsl:value-of select="$attributes[name()=current()]" />
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Result (rendered)

Bryant, you need to work with two files (XML and XSL):
First, you need to specify a template (example.xsl):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>My Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>instance</th>
<th>swver</th>
<th>systemid</th>
<th>systime</th>
<th>sysmem</th>
<th>cutmem</th>
<th>filemem</th>
<th>calconfig</th>
</tr>
<xsl:for-each select="catalog/cd">
<tr>
<td><xsl:value-of select="instance"/></td>
<td><xsl:value-of select="swver"/></td>
<td><xsl:value-of select="systemid"/></td>
<td><xsl:value-of select="systime"/></td>
<td><xsl:value-of select="sysmem"/></td>
<td><xsl:value-of select="cutmem"/></td>
<td><xsl:value-of select="filemem"/></td>
<td><xsl:value-of select="calconfig"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
And then you need to define your XML file (example.xml), based on the template (example.xsl):
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="example.xsl"?>
<catalog>
<cd>
<instance>instance1</instance>
<swver>swver1</swver>
<systemid>systemid1</systemid>
<sysmem>sysmem1</sysmem>
<systime>systime1</systime>
<cutmem>cutmem1</cutmem>
<filemem>filemem1</filemem>
<calconfig>calconfig1</calconfig>
</cd>
<cd>
<instance>instance2</instance>
<swver>swver2</swver>
<systemid>systemid2</systemid>
<sysmem>sysmem2</sysmem>
<systime>systime2</systime>
<cutmem>cutmem2</cutmem>
<filemem>filemem2</filemem>
<calconfig>calconfig2</calconfig>
</cd>
.
.
</catalog>
Result:
Reference:
http://www.w3schools.com/xsl/xsl_transformation.asp
Hope this helps!

Related

XSL: for each within another for each iterating data

I need to perform a cyclical sequence within another public secuanci and get the data of each person and each status, the problem that I have with the code is that it does not iterate the data that I am delivering. I need help in the second for-each when I have empty fields and then I can work with Word content controls
XML file
<?xml version="1.0"?>
<emailList>
<person>
<name>name 1</name>
<email>g#gmail.com</email>
<status>
<active>1</active>
<active>2</active>
<active>3</active>
</status>
</person>
<person>
<name>name 2</name>
<email>n#hotmail.com</email>
<status>
<active>4</active>
<active>5</active>
</status>
</person>
</emailList>
XSL file
<xsl:stylesheet version="1.0">
<html xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xsl:version="1.0">
<head>
<title>Email Listing</title>
</head>
<body>
<table>
<tr>
<th>Name</th>
<th>E-mail Address</th>
<th>Status</th>
</tr>
<xsl:for-each select="emailList/person">
<tr>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of select="email"/></td>
<td>
<xsl:for-each select="emailList/person/status">
<xsl:value-of select="active"/>
</xsl:for-each>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
You inner xsl:for-each will be relative to the outer one, which is selecting person.
Try changing it to this...
<xsl:for-each select="status/active">
<xsl:value-of select="."/>
</xsl:for-each>
Where . selects the current node.
If you wanted to separate the values by commas, you could do this....
<xsl:for-each select="status/active">
<xsl:if test="position() > 1">,</xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
Better still, upgrade to XSLT 2.0 and do away with this xsl:for-each entirely..
<td>
<xsl:value-of select="status/active" separator="," />
</td>
<xsl:template match="/">
<html>
<head>
<title>Email Listing</title>
</head>
<body>
<table>
<tr>
<th>Name</th>
<th>E-mail Address</th>
<th>Status</th>
</tr>
<xsl:for-each select="emailList/person">
<tr>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of select="email"/></td>
<td>
<xsl:for-each select="status">
<xsl:value-of select="active"/>
</xsl:for-each>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>

XSLT 1.0 - Generate HTML table using elements having unequal attribute count but same attribute name

I need to generate an HTML table using the attribute names as header and the attribute values as the data. However the elements that need to be looped do not have all the attributes present all the time. In such a scenario a blank <td> needs to be added to the table for all such non-available attribute values.
Below is a sample XML (a scaled down version with dummy values).
<root>
<oelement attr="abc">
<ielement attr="101">
<child attr1="a" attr2="b" attr3="c" attr4="d" attr5="e" />
<child attr1="e" attr2="f" attr3="g" attr4="h" attr5="i" />
</ielement>
<ielement attr="102">
<child attr1="x" attr3="y" attr5="w" />
<child attr1="j" attr3="k" attr5="l" />
</ielement>
</oelement>
<oelement attr="pqr">
<ielement attr="101">
<child attr1="g" attr2="j" attr3="t" attr4="y" attr5="r" />
<child attr1="d" attr2="q" attr3="a" attr4="c" attr5="b" />
</ielement>
<ielement attr="102">
<child attr1="t" attr3="y" attr5="u" />
<child attr1="i" attr3="o" attr5="p" />
</ielement>
</oelement>
<oelement attr="xyz">
<ielement attr="101">
<child attr1="h" attr2="o" attr3="u" attr4="z" attr5="x" />
</ielement>
<ielement attr="103">
<child attr1="q" attr3="w" attr5="e" />
</ielement>
</oelement>
</root>
Output HTML
I have tried to put together the following XSLT, however it does not match the attribute names in column header when loading the data in the appropriate column of the table.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:strip-space elements="*" />
<xsl:template match="root">
<html>
<body>
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<th>oelement</th>
<th>ielement</th>
<xsl:for-each select="//oelement[1]/ielement[1]/child[1]/#*">
<th><xsl:value-of select="local-name()" /></th>
</xsl:for-each>
</tr>
<xsl:apply-templates />
</table>
</body>
</html>
</xsl:template>
<xsl:template match="child">
<tr>
<td><xsl:value-of select="ancestor::oelement/#attr" /></td>
<td><xsl:value-of select="ancestor::ielement/#attr" /></td>
<xsl:for-each select="#*">
<td><xsl:value-of select="." /></td>
</xsl:for-each>
</tr>
</xsl:template>
</xsl:stylesheet>
Need help in doing the column matching when adding value and inserting blank value for non-matching column. I am stuck with XSLT 1.0 and cannot upgrade to XSLT 2.0 for finding the distinct-values() of the attribute names.
There is no nice solution to do this in XLST 1.0.
You can use a key to achieve the desired effect, though:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:strip-space elements="*" />
<xsl:key name="child-by-attr-name" match="child/#*" use="name(.)"/>
<xsl:template match="root">
<html>
<body>
<table border="1" cellspacing="0" cellpadding="5">
<!-- skipped for simplicity -->
<xsl:apply-templates />
</table>
</body>
</html>
</xsl:template>
<xsl:template match="child">
<tr>
<td><xsl:value-of select="ancestor::oelement/#attr" /></td>
<td><xsl:value-of select="ancestor::ielement/#attr" /></td>
<xsl:variable name="current-child" select="."/>
<xsl:for-each select="(//child/#*)[generate-id(.) = generate-id(key('child-by-attr-name',name(.))[1])]">
<td>
<xsl:value-of select="$current-child/#*[name(.) = name(current())]" />
</td>
</xsl:for-each>
</tr>
</xsl:template>
The "trick" here is that the key will index all attributes (in every child element) by it's name. When you query the key by an attribute name, you get all occurrences of attributes with that name in document order. You can then use generate-id() to make sure you only get the first one of each name.
The key to success is a proper way to loop through attribure names.
To do this, I used:
A key (named attrib), matching child attributes and saving their names (name()).
Two loops, one creating the header, and another printing attribute values.
To cope with changes in the context object in the template matching child,
and for readability, I used 2 variables:
curr - the current child element.
nn - the name of the current attribute.
So the whole script can look like below:
<?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:strip-space elements="*" />
<xsl:key name="attrib" match="child/#*" use="name()"/>
<xsl:template match="root">
<html>
<body>
<table>
<tr>
<th>oelement</th>
<th>ielement</th>
<xsl:for-each select="//child/#*[generate-id()=generate-id(key('attrib', name())[1])]">
<th><xsl:value-of select="name()"/></th>
</xsl:for-each>
</tr>
<xsl:apply-templates select="//child"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="child">
<tr>
<td><xsl:value-of select="ancestor::oelement/#attr" /></td>
<td><xsl:value-of select="ancestor::ielement/#attr" /></td>
<xsl:variable name="curr" select="."/>
<xsl:for-each select="//child/#*[generate-id()=generate-id(key('attrib', name())[1])]">
<td>
<xsl:variable name="nn" select="name()"/>
<xsl:value-of select="$curr/#*[name()=$nn]"/>
</td>
</xsl:for-each>
</tr>
</xsl:template>
</xsl:stylesheet>

Browsers don't parse stylesheet properly

I have an XML File:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet hred="remakes.xsl" type="text/xsl" ?>
<remakes>
<remake>
<rtitle>Pygmalion</rtitle>
<ryear>1938</ryear>
<fraction>0.5</fraction>
<stitle>Pygmalion</stitle>
<syear>1937</syear>
</remake>...
and I have created a stylesheet:
?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/remakes">
<html>
<body>
<table border="1">
<tr>
<th>rtitle<</th>
<th>fraction<</th>
<th>stitle<</th>
<th>syear<</th>
</tr>
<xsl:for-each select="remake">
<xsl:value-of select="rtitle"/></td>
<xsl:value-of select="fraction"/></td>
<xsl:value-of select="stitle"/></td>
<xsl:value-of select="syear"/></td>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
But browsers don't parse it so instead of a table there is chaos. Is there a mistake in the code?
There are a number of problems with your stylesheet, although they could always be typos with your question, but as there are too many to mention in comments, they are as follows:
The namespace prefix xsl has not been bound. You should type xmlns:xsl="..."
There is a < symbol in each table header <th>rtitle<</th>. If you really wanted this, you should write it as <th>rtitle<</th>, but more likely it should just be <th>rtitle</th>
You have a closing <\td> for each row, but no opening <td> tag.
You also really need to wrap those <td> in a <tr> tag for it to be rendered properly.
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/remakes">
<html>
<body>
<table border="1">
<tr>
<th>rtitle</th>
<th>fraction</th>
<th>stitle</th>
<th>syear</th>
</tr>
<xsl:for-each select="remake">
<tr>
<td><xsl:value-of select="rtitle"/></td>
<td><xsl:value-of select="fraction"/></td>
<td><xsl:value-of select="stitle"/></td>
<td><xsl:value-of select="syear"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

xslt with html table sort specific td

I'm new at XSLT language so i don't now how to do that..i want to sort all rows by category from table except the count products..i don't want to affect the order off count_products..please help..the code i have made so far
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<head>
<title></title>
<link rel="stylesheet" href="css/xsl_style.css" type="text/css"/>
</head>
<body>
<div class="tableStyle" >
<table>
<tr bgcolor="#B5E4EA">
<td>A/A</td>
<td>Product Name</td>
<td>Price</td>
<td>Corpration</td>
<td>Category</td>
</tr>
<xsl:for-each select="auction_products/product">
<xsl:sort select="category"/>
<tr>
<td><xsl:value-of select="count_products"/></td>
<td><xsl:value-of select="product_name"/></td>
<td><xsl:value-of select="price"/></td>
<td><xsl:value-of select="corporation"/></td>
<td><xsl:value-of select="category"/></td>
</tr>
</xsl:for-each>
</table>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
If you want the rows to be sorted but the count_products column to come out in document order then you'd need a trick like this:
<xsl:variable name="allProducts" select="auction_products/product" />
<xsl:for-each select="$allProducts">
<xsl:sort select="category"/>
<xsl:variable name="pos" select="position()" />
<tr>
<td><xsl:value-of select="$allProducts[$pos]/count_products"/></td>
<td><xsl:value-of select="product_name"/></td>
<td><xsl:value-of select="price"/></td>
<td><xsl:value-of select="corporation"/></td>
<td><xsl:value-of select="category"/></td>
</tr>
</xsl:for-each>
to take the count_products child from the nth product regardless of the sorting applied to the for-each.
Of course, if count_products is really just a counter (1, 2, 3, ...) then you could just use <xsl:value-of select="$pos" /> directly.

Use XSL to parse XML "color" attribute to fill a table cell

I'm learning XML and I'm running into the following problem: I have an attribute in my XML file which can have a color as content (e.g. <color>red</color>), but I don't know how to use it.
My XML:
<?xml version="1.0" encoding="UTF-8"?>
<cars>
<car year="2002" manufacturer="Jet" model="Sardine Can 1.6L">
<meter>95664</meter>
<color>silver</color>
<price>099900</price>
<dealersecurity buyback="no"/>
</car>
<car year="2004" manufacturer="Jet" model="Sardine Can 2.0">
<meter>81283</meter>
<color>red</color>
<price>129900</price>
<dealersecurity buyback="no"/>
</car>
<car year="2007" manufacturer="Jet" model="Sardine Can 2.0">
<meter>49741</meter>
<color>black</color>
<price>169999</price>
<dealersecurity buyback="yes"/>
</car>
</cars>
My XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml"
version="1.0">
<xsl:output method="xml"/>
<xsl:template match="/cars">
<html>
<head><title>Second Hand Sardine Cans</title>
<link rel="stylesheet" type="text/css" href="style.css"/></head>
<body>
<h1>Second Hand Sardine Cans</h1>
<table border="1"><th>Make</th><th>Model</th><th>Year</th><th>KMs</th><th>Color</th><th>Price</th><th>Warranty</th><xsl:apply-templates/></table>
</body>
</html>
</xsl:template>
<xsl:template match="cars/car">
<tr><xsl:for-each select="cars/car"/>
<td><xsl:value-of select="#manufacturer" /></td>
<td><xsl:value-of select="#model" /></td>
<td><xsl:value-of select="#year" /></td>
<td><xsl:value-of select="meter" /></td>
<xsl:apply-templates/></tr>
</xsl:template>
<xsl:template match="meter"/>
<xsl:template match="color">
<td><xsl:apply-templates /></td>
</xsl:template>
<xsl:template match="price">
<td><xsl:apply-templates /></td>
</xsl:template>
</xsl:stylesheet>
What I have until now:
But instead of it saying "Red" or "Black", I want that cell to fill with that exact color.
Thanks!
Edit: I fixed it with the help of Dimitre's answer. I added <td bgcolor="{color}"><xsl:value-of select="color" /></td> below <td><xsl:value-of select="meter" /></td>
Just change in your transformation:
<tr>
to:
<tr bgcolor="{color}">
Or, if you really want to have just one cell with that color, use in the template that matches color:
<td bgcolor="{.}">