How to get attribute name and its value properly? - html

Input:
<?xml version="1.0" encoding="utf-8" ?>
<Software>
<MS version="5.2.3.1"/>
<Java version="5.1.0.29" />
<Oracle id="A" version="1.0.1.11" />
<SQL id="P" version="1.0.1.11" />
</Software>
XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<table>
<tr>
<xsl:for-each select="//*[1]">
<xsl:for-each select="#*">
<td>
<xsl:value-of select="name()"/>
</td>
</xsl:for-each>
</xsl:for-each>
</tr>
<xsl:for-each select="//*">
<tr>
<xsl:value-of select="local-name()"/> ::
<xsl:for-each select="#*">
<td>
<xsl:value-of select="."/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Output:
Current output [wrong]:
This script gives output without column name
"id". Some "version" column value added in "id(not visible)" column as
there is no attribute name id in node. eg. MS node has version
attribute only hence output result added version info in "id" column.
Please go through output once, possibly save and check in html for proper understanding.
<table>
<tr>
<td/>
<td>
<td>version</td>
</td>
</tr>
<tr>
<td>Software ::
</td>
</tr>
<tr>
<td>MS ::
<td>5.2.3.1</td></td>
</tr>
<tr>
<td>Java ::
<td>5.1.0.29</td></td>
</tr>
<tr>
<td>Oracle ::
<td>A</td><td>1.0.1.11</td></td>
</tr>
<tr>
<td>SQL ::
<td>P</td><td>1.0.1.11</td></td>
</tr>
</table>
Expected output:
Every attribute as columnname/header and all column
have their own value. NOTE: Attributes MS, JAVA, etc as column Name
should not be hard coded because the number of attributes may change on runtime.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title></title>
</head>
<body>
<table>
<tr>
<td/>
<td>
id<td>version</td>
</td>
</tr>
<tr>
<td>MS ::</td>
<td>Not exist</td><td>5.2.3.1</td>
</tr>
<tr>
<td>Java ::</td>
<td>Not exist</td><td>5.1.0.29</td>
</tr>
<tr>
<td>Oracle ::</td>
<td>A</td><td>1.0.1.11</td>
</tr>
<tr>
<td>SQL ::</td>
<td>P</td><td>1.0.1.11</td>
</tr>
</table>
</body>
</html>

If you are looking to output one column for each possible attribute name, and not restrict it to just id and version, then (in XSLT 1.0) you could make use of a technique called Muenchian Grouping to get the distinct attribute names.
First define a key to look up the attributes by their name
<xsl:key name="columns" match="#*" use="local-name()" />
Then, to get the distinct ones (or rather, get the first occurrence of each distinct name), you can define a variable like so
<xsl:variable name="columns" select="//#*[generate-id() = generate-id(key('columns', local-name())[1])]" />
Then, to output the column headers, you can just iterate over this variable
<xsl:for-each select="$columns">
<xsl:sort select="local-name()" />
<td><xsl:value-of select="local-name()" /></td>
</xsl:for-each>
For each row, you would take a similar approach. Assuming you were positioned on a child element, you could first define a variable that contains the attributes of the current element. Then you would iterate over the columns variable again, and output the value of the corresponding attribute for the current element
<xsl:variable name="attributes" select="#*" />
<xsl:for-each select="$columns">
<xsl:sort select="local-name()" />
<td><xsl:value-of select="$attributes[local-name() = local-name(current())]" /></td>
</xsl:for-each>
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:key name="columns" match="#*" use="local-name()" />
<xsl:variable name="columns" select="//#*[generate-id() = generate-id(key('columns', local-name())[1])]" />
<xsl:template match="Software">
<table>
<tr>
<td>Software</td>
<xsl:for-each select="$columns">
<xsl:sort select="local-name()" />
<td><xsl:value-of select="local-name()" /></td>
</xsl:for-each>
</tr>
<xsl:apply-templates select="*" />
</table>
</xsl:template>
<xsl:template match="Software/*">
<tr>
<td><xsl:value-of select="local-name()" /></td>
<xsl:variable name="attributes" select="#*" />
<xsl:for-each select="$columns">
<xsl:sort select="local-name()" />
<td><xsl:value-of select="$attributes[local-name() = local-name(current())]" /></td>
</xsl:for-each>
</tr>
</xsl:template>
</xsl:stylesheet>
This should output the following
<table>
<tr>
<td>Software</td>
<td>id</td>
<td>version</td>
</tr>
<tr>
<td>MS</td>
<td/>
<td>5.2.3.1</td>
</tr>
<tr>
<td>Java</td>
<td/>
<td>5.1.0.29</td>
</tr>
<tr>
<td>Oracle</td>
<td>A</td>
<td>1.0.1.11</td>
</tr>
<tr>
<td>SQL</td>
<td>P</td>
<td>1.0.1.11</td>
</tr>
</table>
Note, for brevity, I have not included code to output "Not exists" in the case where the attribute does not exist. But it should be simple enough to add a check for this. (Just store the value in a variable, and use an xsl:choose)
EDIT: If you want to restrict the attributes to specifically be for Software elements too, try changing the key to this:
<xsl:key name="columns" match="Software/*/#*" use="local-name()" />

Hummm, I have tried to fix the HTML issue and the problem you are facing.
This is a bit mannual, but can achieve your requirement. Here is the XSL:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<table>
<tr>
<td></td>
<td>id</td>
<xsl:for-each select="//*[1]">
<xsl:for-each select="#*">
<td><xsl:value-of select="name()"/></td>
</xsl:for-each>
</xsl:for-each>
</tr>
<xsl:for-each select="//*">
<xsl:if test="position() > 1">
<tr>
<td><xsl:value-of select="local-name()"/> ::</td>
<td>
<xsl:choose>
<xsl:when test="#id"><xsl:value-of select="#id"/></xsl:when>
<xsl:otherwise>Not Exist</xsl:otherwise>
</xsl:choose>
</td>
<td>
<xsl:if test="#version"><xsl:value-of select="#version"/></xsl:if>
</td>
</tr>
</xsl:if>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Here is the output of it:
<table>
<tr>
<td/>
<td>id</td>
<td>version</td>
</tr>
<tr>
<td>MS ::</td>
<td>Not Exist</td>
<td>5.2.3.1</td>
</tr>
<tr>
<td>Java ::</td>
<td>Not Exist</td>
<td>5.1.0.29</td>
</tr>
<tr>
<td>Oracle ::</td>
<td>A</td>
<td>1.0.1.11</td>
</tr>
<tr>
<td>SQL ::</td>
<td>P</td>
<td>1.0.1.11</td>
</tr>
</table>
I have tested it on http://www.xslfiddle.net/

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>

Convert XML List to HTML Table Using XSL

Hello I was struggling with this for the past days and I could not find a good answer nor solution. I have an XML file with a list of objects like this:
<?xml version="1.0" encoding="UTF-8"?>
<LineItems>
<TableName>Lines</TableName>
<TableTerm>Lines</TableTerm>
<LineItems>
<Class>A Class</Class>
</LineItems>
<LineItems>
<Number>1234</Number>
</LineItems>
<LineItems>
<Description>G</Description>
</LineItems>
<LineItems>
<Class>B Class</Class>
</LineItems>
<LineItems>
<Number>5678</Number>
</LineItems>
<LineItems>
<Description>F</Description>
</LineItems>
<ColumnMetadata>
<Name>Class</Name>
<Term>Class</Term>
</ColumnMetadata>
<ColumnMetadata>
<Name>Number</Name>
<Term>No</Term>
</ColumnMetadata>
<ColumnMetadata>
<Name>Description</Name>
<Term>Description</Term>
</ColumnMetadata>
</LineItems>
I am applying the following transformaiton:
<?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>
<xsl:variable name="columns" select="count(LineItems/ColumnMetadata)" />
<xsl:variable name="items" select="count(LineItems/LineItems)" />
<xsl:variable name="rows" select="$items div $columns" />
<table border="1">
<thead >
<tr bgcolor="#9acd32">
<xsl:for-each select="LineItems/ColumnMetadata">
<th style="padding: .3em 0;">
<xsl:value-of select="Term" />
</th>
</xsl:for-each>
</tr>
</thead>
<tbody style="text-align: center;">
<xsl:for-each select="(//LineItems)[position()<=$rows]">
<xsl:variable name="i" select="position() - 1"/>
<tr>
<xsl:for-each select="(//*)[position()<=$columns]">
<xsl:variable name="j" select="position()+($columns*$i)"/>
<td style="padding: .3em 0;">
<xsl:value-of select="LineItems/LineItems[$j]" />
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</tbody>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Finally the desire output for this case would be:
<table>
<thead>
<tr>
<th>Class</th>
<th>No</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>A Class</td>
<td>1234</td>
<td>G</td>
</tr>
<tr>
<td>B Class</td>
<td>5678</td>
<td>F</td>
</tr>
</tbody>
</table>
This case is a case of MxN table, I cna know how many columns from nodes. So, to sumarise:
The actual list that have to be transformed as a table is all inside root <LineItems>.
I don't know how many items (rows) I am going to get, but I can calculate them dividing amount of <LineItems> nodes ($items) by amount of <ColumnMetadata> nodes ($columns)
Nodes like <Class>, <Number> and <Description>, are columns in table, but they can have other names, they are dynamic and can be 5, 6... Many columns.
If I transform above XML with XSL in online tools I only get the header row of the table (and inspecting HTML, I can see 2 rows for 2 items, but empty). If I use Visual Studio transformation tool, I not only get header row, I also get first 2 columns of table (in this example Class values) but not the values in the rest of them. I really don't understand what is going on and why do I get different results using different tools.
Thanks in advance
lineitmes folllow a regular pattern.
Then I believe it could be simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/LineItems">
<xsl:variable name="columns" select="count(ColumnMetadata)"/>
<table border="1">
<thead >
<tr>
<xsl:for-each select="ColumnMetadata">
<th>
<xsl:value-of select="Term"/>
</th>
</xsl:for-each>
</tr>
</thead>
<tbody>
<xsl:for-each select="LineItems[position() mod $columns = 1]">
<tr>
<xsl:for-each select=". | following-sibling::LineItems[position() < $columns]">
<td>
<xsl:value-of select="*"/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</tbody>
</table>
</xsl:template>
</xsl:stylesheet>

Modifying a XSLT XPATH code for a different output

I need to modify my XSLT code for a different output result. I need to output a table with the sigle, number of students and average.
Here's my XML code :
<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet href="class.xsl" type="text/xsl" ?>
<university>
<student><sname>Charlie Parker</name>
<course sigle="INF8430" note="69" />
<course sigle="INF1030" note="65" />
<course sigle="INF1230" note="73" /></student>
<student><name>Miles Davis</name>
<course sigle="INF8430" note="65" />
<course sigle="INF1030" note="77" />
<course sigle="INF1230" note="83" /></student>
<student><name>John Coltrane</name>
<course sigle="INF9430" note="24" />
<course sigle="INF1030" note="64" />
<course sigle="INF1230" note="56" /></student>
<student><name>Charles Mingus</name>
<course sigle="INF8430" note="34" />
<course sigle="INF1230" note="89" /></student>
</university>
Here's my XSLT Code so far :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/university">
<html>
<body>
<table border="1">
<tr>
<th>Name</th>
<th>Average</th>
</tr>
<xsl:for-each select="student">
<xsl:sort select="substring-after(name, ' ')"/>
<tr>
<td><xsl:value-of select="name" /></td>
<td><xsl:value-of select="sum(course/#note) div count(course)"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Here's what the output should look like :
Thanks a lot for your help!
Your input document is not well-formed, please be careful when posting questions on Stackoverflow.
The standard approach to identify elements that are unique with respect to their content or one of their attributes is to use a key. The best explanation of this is still on Jeni Tennison's web page.
You could also consider using the round() function if there's too much precision in the average column.
XSLT Stylesheet
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="course-sigle" match="course" use="#sigle"/>
<xsl:template match="/university">
<html>
<body>
<table border="1">
<tr>
<th>Sigle</th>
<th>Number of Students</th>
<th>Average</th>
</tr>
<xsl:for-each select="student/course[count(. | key('course-sigle', #sigle)[1]) = 1]">
<xsl:variable name="count" select="count(key('course-sigle', #sigle))"/>
<tr>
<td>
<xsl:value-of select="#sigle"/>
</td>
<td>
<xsl:value-of select="$count"/>
</td>
<td>
<xsl:value-of select="sum(key('course-sigle', #sigle)/#note) div $count"/>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
HTML Output
<html>
<body>
<table border="1">
<tr>
<th>Sigle</th>
<th>Number of Students</th>
<th>Average</th>
</tr>
<tr>
<td>INF8430</td>
<td>3</td>
<td>56</td>
</tr>
<tr>
<td>INF1030</td>
<td>3</td>
<td>68.66666666666667</td>
</tr>
<tr>
<td>INF1230</td>
<td>4</td>
<td>75.25</td>
</tr>
<tr>
<td>INF9430</td>
<td>1</td>
<td>24</td>
</tr>
</table>
</body>
</html>
Rendered HTML Output
Including the round function, the output looks like
Besides, if you are in charge of designing this XML document, note that some of the names in it are either incomprehensible ("sigle") or inappropriate given the context ("note"). Proper terms would be "signature" and "grade", or "score".

Creating semantic tables with rowspan via xslt

I have following xml document:
...
<x>
<symptom><descr></descr></symptom>
<cause></cause>
<solution></solution>
<cause></cause>
<solution></solution>
</x>
...
In my document I have several <x>
In each <x> I have only one <symptom> and n <cause> and <solution> whereby the amount of <cause> and <solution> is always the same.
I want to get following autmatically generated structure:
<table>
<tr>
<td rowspan=count(cause)><xsl:value-of select="symptom/descr"></td>
<td><xsl:value-of select="cause"></td>
<td><xsl:value-of select="symptom"></td>
<tr>
<tr>
<td><xsl:value-of select="cause"></td>
<td><xsl:value-of select="symptom"></td>
<tr>
...
</table>
I tried following code, which I know is totally wrong. But I'm stuck since several hours and couldn't find any good solution on the internet.
<xsl:for-each select="cause">
<tr>
<td rowspan="count(.)">
<xsl:value-of select="../descr[1]"/>
</td>
<td>
<xsl:value-of select="."/>
</td>
<xsl:for-each select="../solution">
<td>
<xsl:value-of select="."/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
You're on the right lines with one tr per cause, how about this:
<xsl:template match="x">
<table>
<xsl:for-each select="cause">
<!-- the index of this cause within the list of causes in the current x -->
<xsl:variable name="pos" select="position()" />
<tr>
<!-- first cause - create the spanning symptom cell -->
<xsl:if test="$pos = 1">
<td rowspan="{last()}"><xsl:value-of select="../symptom/descr"/></td>
</xsl:if>
<!-- this cause -->
<td><xsl:value-of select="." /></td>
<!-- the matching solution -->
<td><xsl:value-of select="../solution[$pos]" /></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
A trick here is the last() function, which returns the total number of nodes that the current for-each (or apply-templates) is processing, which in this case is precisely the number of rows you want to span.
This is based on the assumption that you want as result a table with following structure: Given an example input XML
<x>
<symptom>
<descr>
Description
</descr>
</symptom>
<cause>
Cause 1
</cause>
<solution>
Solution 1
</solution>
<cause>
Cause 2
</cause>
<solution>
Solution 2
</solution>
</x>
I assume you want following table:
<table>
<tr>
<td rowspan="2">Description</td>
<td>Cause 1</td>
<td>Solution 1</td>
</tr>
<tr>
<td>Cause 2</td>
<td>Solution 2</td>
</tr>
</table>
This could be done with following XSLT:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="x">
<table>
<xsl:for-each select="cause">
<xsl:apply-templates select="." mode="row">
<xsl:with-param name="amount" select="count(../cause)"/>
<xsl:with-param name="position" select="position()"/>
</xsl:apply-templates>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template match="cause" mode="row">
<xsl:param name="amount"/>
<xsl:param name="position"/>
<tr>
<xsl:if test="$position = 1">
<td rowspan="{$amount}">
<xsl:value-of select="//symptom/descr"/>
</td>
</xsl:if>
<td>
<xsl:value-of select="."/>
</td>
<td>
<xsl:value-of select="following-sibling::solution"/>
</td>
</tr>
</xsl:template>
</xsl:transform>
For each cause a row is created by applying the template
<xsl:template match="cause" mode="row">
with the amount of rows and the the position of the current cause as parameters. If the position is 1, the description is written as value in a td with the amount of cause as value of rowspan.
Each row contains the value of the current cause:
<td>
<xsl:value-of select="."/>
</td>
and the value of the solution at the same position (the solution which is the following-sibling of the current cause):
<td>
<xsl:value-of select="following-sibling::solution"/>
</td>

XML Transformation to HTML using XSLT

I have simple xml as below
<Scores>
<Score1>
<Name>A</Name>
<Address>Address1</Address>
</Score1>
<Score2>
<Name>B</Name>
<Address>Address2</Address>
</Score2>
</Scores>
I want it's output in HTML table as below.
(HTML table will have 2 columns headers "Name" and "Address", and i need it's values in rows) I do not want to hardcode "Name" and "Address" headers. They may change in future.
Name Address
A Address1
B Address2
Can you please let me know what will be the XSLT for this?
Use:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/Scores">
<table>
<tr>
<xsl:for-each select="*[1]/*">
<th>
<xsl:value-of select="local-name()"/>
</th>
</xsl:for-each>
</tr>
<xsl:apply-templates select="*"/>
</table>
</xsl:template>
<xsl:template match="*">
<tr>
<xsl:for-each select="*">
<td>
<xsl:value-of select="."/>
</td>
</xsl:for-each>
</tr>
</xsl:template>
</xsl:stylesheet>
Output:
<table>
<tr>
<th>Name</th>
<th>Address</th>
</tr>
<tr>
<td>A</td>
<td>Address1</td>
</tr>
<tr>
<td>B</td>
<td>Address2</td>
</tr>
</table>