XML to Table <for-each> - html

I am trying to turn the following into a table, but I am having a hard time understanding how it parses through all of the child nodes. Will it go through each element until the last one or do I need to have a for-each for each parent node? Also, if there are multiple occurrences of "capris" allowed, do I need to have cells for each possible occurrence (say if there are 3 max but I only have 2, then I would still need 3 cells displayed).
XML:
<Persons xmlns = "">
<Person>
<Shirts>One</Shirts>
<Pants>
<Jeans>
<Shorts>One</Shorts>
<Capris>One</Capris>
<Capris>Two</Capris>
</Jeans>
<Dress>One</Dress>
</Pants>
</Person>
<Person>
<Shirts>One</Shirts>
<Pants>
<Jeans>
<Shorts>One</Shorts>
<Capris>One</Capris>
<Capris>Two</Capris>
<Capris>Three</Capris>
</Jeans>
<Dress>One</Dress>
</Pants>
</Person>
</Persons>
XSL:
<table border="1">
<tr bgcolor="yellow">
<td><b>Shirts</b></td>
<td><b>Shorts</b></td>
<td><b>Capris</b></td>
<td><b>Capris</b></td>
<td><b>Capris</b></td>
</tr>
<xsl:for-each select="Persons">
<xsl:sort select="Persons/Persib" />
<tr style="font-size: 10pt; font-family: verdana">
<td><xsl:value-of select="Shirts"/></td>
<td><xsl:value-of select="Shorts"/></td>
<td><xsl:value-of select="Capris"/></td>
</tr>
</xsl:for-each>
</table>

You had your xsl:for-each matching rule wrong.
Use the following template: it matches the root <Persons> element, creates the <html> and <body> elements and the <table>.
Finally, it iterates over the <Person> elements with an xsl:for-each. This works, because the context node is right (Person is a child of Persons).
The Capris situation is resolved by iterating over all Capris elements and creating a <td> for each present element. You can see this by looking at the borders of the elements.
One thing left to be corrected is the xsl:sort element. It tries to sort by the value of a <Persib> element which is not present in the example. I guess that you can fix this on your own.
<xsl:template match="/Persons">
<html>
<body>
<table border="1">
<tr bgcolor="yellow">
<td><b>Shirts</b></td>
<td><b>Shorts</b></td>
<td><b>Capris</b></td>
<td><b>Capris</b></td>
<td><b>Capris</b></td>
</tr>
<xsl:for-each select="Person">
<xsl:sort select="Persib" />
<tr style="font-size: 10pt; font-family: verdana">
<td>
<xsl:value-of select="Shirts"/>
</td>
<td>
<xsl:value-of select="Pants/Jeans/Shorts"/>
</td>
<xsl:for-each select="Pants/Jeans/Capris">
<td>
<xsl:value-of select="." />
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
The output should be as desired.

Related

XML , XSL parsing - couldnt display all rows in a table

Am trying to generating PDF out of XML document. Please find my below XML and XSL for the same.
Am expecting it should display all rows under tag but am getting only very first element (rows) in each tag.
Please find my below xml
<receipt>
<order>
<page></page>
<page>
<line_number>1</line_number>
<product_code>S10</product_code>
<line_number>2</line_number>
<product_code>S20</product_code>
<line_number>3</line_number>
<product_code>S92</product_code>
</page>
<page>
<line_number>6</line_number>
<product_code>S92</product_code>
<line_number>7</line_number>
<product_code>S31</product_code>
<line_number>8</line_number>
<product_code>S31</product_code>
</page>
</order>
</receipt>
Please find my xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml"
xmlns:date="http://exslt.org/dates-and-times" extension-element-prefixes="date">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/receipt">
<html>
<head>
<style>#page {size: a4 landscape;}</style>
</head>
<body>
<table >
<thead>
<tr >
<th >Line</th>
<th>Item Code</th>
</tr>
</thead>
<xsl:for-each select="order/page" >
<tbody>
<tr style="font-size: 9px; ">
<td ><xsl:value-of select="line_number" /></td>
<td ><xsl:value-of select="product_code" /></td>
</tr>
</tbody>
</xsl:for-each>
</table>
<br />
</body>
</html>
</xsl:template>
</xsl:stylesheet>
In the output only 1st element in tag is coming instead of all the elements (rows) under each tag.
for example :
output :
1 s10
6 s92
Expected Output
1 s10
2 s20
3 s92
6 s92
7 s31
8 s31
You want to output one row per line_number, rather that one row per page, so you xsl:for-each needs to select these line_number elements
<xsl:for-each select="order/page/line_number">
Then to get the value of the line_number and following product_code, do this...
<td><xsl:value-of select="." /></td>
<td><xsl:value-of select="following-sibling::product_code[1]" /></td>
Try this...
<xsl:template match="/receipt">
<html>
<head>
<style>#page {size: a4 landscape;}</style>
</head>
<body>
<table >
<thead>
<tr>
<th>Line</th>
<th>Item Code</th>
</tr>
</thead>
<tbody>
<xsl:for-each select="order/page/line_number">
<tr style="font-size: 9px; ">
<td><xsl:value-of select="." /></td>
<td><xsl:value-of select="following-sibling::*[1][self::product_code]" /></td>
</tr>
</xsl:for-each>
</tbody>
</table>
<br />
</body>
</html>
</xsl:template>
Note that this does make the assumption that each line_number will be followed by a product_code.
(I have also moved the creation of the tbody element outside the xsl:for-each as you should really only have one such element in your table, rather than one for each row)

Turn XML with XSLT to aHTML order Table

i need yout help. I'm struggle with XSLT: I have for example an XML file like this:
<Adresses>
<Person id="1">
<Name>Bott</Name>
<Vorname>Nils</Vorname>
</Person>
<Person id="2">
<Name>Hubert</Name>
<Vorname>Hubertus</Vorname>
</Person>
<Person id="3">
<Name>Large</Name>
<Vorname>Lars</Vorname>
</Person>
<Numbers>
<number>
<id>1</id>
<tel>12354</tel>
</number>
<number>
<id>3</id>
<tel>32354</tel>
</number>
<number>
<id>2</id>
<tel>22354</tel>
</number>
</Numbers>
</Adresses>
And i have to transform to a order HTML Table.
The HTML Table should look like folowwing (Ordering after ID):
<table>
<tr>
<th>Name</th>
<th>Vorname</th>
<th>Tel</th>
</tr>
<tr>
<th>Bott</th>
<th>Nils</th>
<th>12354</th>
</tr>
</table>
My Problem is i don't know how to ckeck: Choose the number where Peron.id = number.id .... It would be realy great if someone could give me a full example for this problem. At web search it is always not so complicated in the tutorials ;-( .... I'm learning XSLT and just know some rules like apply-templates match="Person" .... but how is it possible in such a statement to call a other template wich select the coorect number?
I try to start with that:
<xsl:template match="/">
<html>
<body>
<xsl:for-each select="Adresses/Person">
<xsl:call-template name="curPos">
</xsl:call-template>
<xsl:value-of select="Name"/>
<xsl:value-of select="Vorname"/>
</xsl:for-each>
</body>
</html>
</xsl:template>
<xsl:template name="curPos" match="Person">
<xsl:value-of select="position()"/><br> </br>
</xsl:template>
My Problem is i don't know how to ckeck: Choose the number where
Peron.id = number.id ....
That's exactly what keys are for:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="num" match="number" use="id" />
<xsl:template match="/Adresses">
<html>
<body>
<table>
<tr>
<th>Name</th>
<th>Vorname</th>
<th>Tel</th>
</tr>
<xsl:for-each select="Person">
<tr>
<td>
<xsl:value-of select="Name"/>
</td>
<td>
<xsl:value-of select="Vorname"/>
</td>
<td>
<xsl:value-of select="key('num', #id)/tel"/>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Note that your table structure assumes each person has only one phone number (as in your example). However, the XML structure suggests there is a one-to-many relationship between persons and phone numbers, so you might want to consider changing:
<td>
<xsl:value-of select="key('num', #id)/tel"/>
</td>
to:
<td>
<xsl:for-each select="key('num', #id)">
<xsl:value-of select="tel"/>
<br/>
</xsl:for-each>
</td>
I have got the following XSLT 2.0 solution for you:
<xsl:template name="makeTable">
<table>
<tr>
<th>Name</th>
<th>Vorname</th>
<th>Tel</th>
</tr>
<xsl:for-each select="/Adresses/Person">
<tr>
<td><xsl:value-of select="Name"/></td>
<td><xsl:value-of select="Vorname"/></td>
<td><xsl:value-of select="../Numbers/number[id = current()/#id]/tel"/></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
Now, for the explanation as far as I understand the problem you have is finding the correct XPath expression for selecting the correct telephone number. The most important code snipped in this case is this line:
<td>
<xsl:value-of select="../Numbers/number[id = current()/#id]/tel"/>
</td>
In XSLT 2.0 - I'm not sure about XSLT 1.0 - you can refer to the current loop element with the current() function in an XPath.
To select the telephone number I filter for the number on the condition of its id being equal to the current loop elements attribute id and then getting this node's child tel to get the actual number.

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>

XSLT XML to HTML Crosstab table

I am trying to make a crosstab tables from the XML file from ToDoList (http://www.abstractspoon.com/tdl_resources.html) Here is an example XML file:
<?xml version="1.0" encoding="utf-16" ?>
<TODOLIST PROJECTNAME="Projects">
<TASK TITLE="proj1" ID="1" NUMPERSON="1" PERSON="Chris" NUMTAGS="1" TAG="Caro" CALCTIMEESTIMATE="36"/>
<TASK TITLE="proj2" ID="2" NUMPERSON="1" PERSON="Chris" NUMTAGS="1" TAG="Nat" CALCTIMEESTIMATE="8" />
<TASK TITLE="proj4" ID="4" NUMPERSON="1" PERSON="Chris" NUMTAGS="1" TAG="Caro" CALCTIMEESTIMATE="36" />
<TASK TITLE="proj5" ID="5" NUMPERSON="1" PERSON="Sahb" NUMTAGS="1" TAG="Nat" CALCTIMEESTIMATE="128" />
<TASK TITLE="proj32" ID="32" NUMPERSON="2" PERSON="Seb" PERSON1="Chris" NUMTAGS="1" TAG="Nat" CALCTIMEESTIMATE="0.90" />
</TODOLIST>
Each task is an element and all the info are attributes. I want to make a table like this with the tags as the top row and the persons as the first column.
<table>
<tr>
<td></td>
<td>Caro</td>
<td>Nat</td>
</tr>
<tr>
<td>Chris</td>
<td>72</td>
<td>8</td>
</tr>
<tr>
<td>Sahb</td>
<td>128</td>
<td>0</td>
</tr>
<tr>
<td>Seb, Chris</td>
<td>0</td>
<td>9</td>
</tr>
</table>
As you can see the same person can have many projects and I want to add up the CALCTIMEESTIMATE for each person based on the tag(s). I can get the first row with
<xsl:key name="dtag" match="/TODOLIST/TASK/#TAG" use="." />
<xsl:for-each select="TASK/#TAG[generate-id() = generate-id(key('dtag', .)[1])]">
<td>
<xsl:value-of select="."/>
</td>
</xsl:for-each>
But the sum function does not give the desired results. What I'm I missing? Is there an easy way to do the sum based on two attributes? ToDoList uses XSLT 1.0, that's why I couldn't use distinct-values.
Here is the full XSLT, I've tried alot of different thing, Here I was trying to get it working just for Chris but it would not add up his 2 projects.
<xsl:template match="TODOLIST">
<xsl:text disable-output-escaping="yes"><!DOCTYPE html></xsl:text>
<html>
<head>
<title>Projects..</title>
</head>
<body>
<table>
<caption>Projects..</caption>
<thead>
<tr>
<td></td>
<xsl:for-each select="TASK/#TAG[generate-id() = generate-id(key('dtag', .)[1])]">
<th scope="col">
<xsl:value-of select="."/>
</th>
</xsl:for-each>
</tr>
</thead>
<tbody>
<xsl:apply-templates select="TASK/#PERSON[generate-id() = generate-id(key('dperson', .)[1])]" />
</tbody>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="TASK/#PERSON">
<tr>
<th scope="row">
<xsl:value-of select="."/>
</th>
<xsl:apply-templates select="/TODOLIST/TASK/#TAG[generate-id() = generate-id(key('dtag', .)[1])]" />
</tr>
</xsl:template>
<xsl:template match="/TODOLIST/TASK/#TAG">
<td>
<xsl:value-of select="sum(../#CALCTIMEESTIMATE[../#PERSON = 'Chris'])" />
</td>
</xsl:template>
To get your sum to work, you need to know both the current person and the current tag.
In your current template that matches TASK/#PERSON, you should pass the current value of the attribute to the next template
<xsl:apply-templates
select="/TODOLIST/TASK/#TAG[generate-id() = generate-id(key('dtag', .)[1])]">
<xsl:with-param name="PERSON" select="." />
</xsl:apply-templates>
Then, in your template that matches #TAG, you could access the 'dperson' key in the SUM, so you sum all tasks for that person, and for the current tag, like so:
<xsl:template match="/TODOLIST/TASK/#TAG">
<xsl:param name="PERSON" />
<td>
<xsl:value-of
select="sum(key('dperson', $PERSON)[../#TAG = current()]/../#CALCTIMEESTIMATE)"/>
</td>
</xsl:template>

XML parsing: How to separate same type siblings into different cells?

XML file:
<books>
<scifi key=...>
<author>Don Larson</author>
<title>The Edge</title>
<year>...</year>
</scifi>
<scifi key=...>
<author>Don Larson</author>
<author>James Kiddleton</author>
<author>Danny Wobers</author>
<title>Incognitum</title>
<year>1987</year>
</scifi>
<scifi key=...>
<author>....</author>
<author>....</author>
<title>...</title>
<year>...</year>
</scifi>
etc......................
</books>
XSL file:
<xsl:template match="/">
<html>
<body>
<center><h1>SciFi</h1>
<table border="1">
<tr>
<th>Title</th>
<th>Authors</th>
<th>Year</th>
</tr>
<xsl:for-each select="books/scifi">
<xsl:sort select="year"/>
<tr>
<td><center><xsl:value-of select="title"/></center></td>
<td>
<xsl:for-each select="author">
<p><xsl:value-of select="."/></p>
</xsl:for-each>
</td>
<td><xsl:value-of select="year"/></td>
</tr>
</xsl:for-each>
</table>
</center>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
When the above table is displayed, for each cell containing a title, the cell next to it contains all the authors related to that title
eg:
Title Authors Year
(1cell) (1cell) (1cell)
----- ------- ----
Don Larson
Incognitum James Kiddleton 1987
Danny Wobers
Now what I want to do is for each title display the authors (if there is more than one that is..) related to it in separate cells and get something like this instead:
Title Authors Year
(3cells) (3cells) (3cells)
----- ------- ----
Incognitum Don Larson 1987
Incognitum James Kiddleton 1987
Incognitum Danny Wobers 1987
Instead of having one cell for title and one cell containing all three authors related to that title, I want to have three cells with the same title and each of those cells can have a cell with the related author next to it.
I still want to keep sorting everything by yeat and I still want the order of my cells (left->right) to be Title->Author->Year
What do I have to change in my XSL file?
you want one row per author, so change
<tr>
<td><center><xsl:value-of select="title"/></center></td>
<td>
<xsl:for-each select="author">
<p><xsl:value-of select="."/></p>
</xsl:for-each>
</td>
<td><xsl:value-of select="year"/></td>
</tr>
to
<xsl:for-each select="author">
<tr>
<td><center><xsl:value-of select="../title"/></center></td>
<td>
<xsl:value-of select="."/>
</td>
<td><xsl:value-of select="../year"/></td>
</tr>
</xsl:for-each>
David
Quick fix is
<xsl:template match="/">
<html>
<body>
<center><h1>SciFi</h1>
<table border="1">
<tr>
<th>Title</th>
<th>Authors</th>
<th>Year</th>
</tr>
<xsl:for-each select="books/scifi">
<xsl:sort select="year"/>
<xsl:for-each select="author">
<tr>
<td><center><xsl:value-of select="../title"/></center></td>
<td>
<xsl:for-each select=".">
<p><xsl:value-of select="."/></p>
</xsl:for-each>
</td>
<td><xsl:value-of select="../year"/></td>
</tr>
</xsl:for-each>
</xsl:for-each>
</table>
</center>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
but in the long run I would suggest to move from one template doing it all with nested for-eachs to structured stylesheet with templates mapping each element type to a result and then doing apply-templates to keep going.