Vertical column data filling using XSLT - html

Im new to XSLT and My XML is below
<Aus>
<au>
<ele>
<auid>Au1</auid>
<cid>C1</cid>
<fn>F1</fn>
<sn>S1</sn>
<dept>D1</dept>
</ele>
<ele>
<auid>Au2</auid>
<cid>C2</cid>
<fn>F2</fn>
<sn>S2</sn>
<dept>D2</dept>
</ele>
<ele>
<auid>Au3</auid>
<cid>C3</cid>
<fn>F3</fn>
<sn>S3</sn>
<dept>D4</dept>
</ele>..............
</au>
</Aus>
I want the output like below in html view using XSLT conversion
but XSLT code should be simple to identify next columns by position increment. Please help me.
My current code is
<xsl:for-each select="//Aus/au">
<table>
<tr>
<td><xsl:value-of select="ele[1]/auid"/></td><td><xsl:value-of select="ele[2]/auid"/></td><td><xsl:value-of select="ele[3]/auid"/></td>
</tr>
<tr>
<td><xsl:value-of select="ele[1]/cid"/></td><td><xsl:value-of select="ele[2]/cid"/></td><td><xsl:value-of select="ele[3]/cid"/></td>
</tr>
..........
</table>
</xsl:for-each>

I would do it like this:
<xsl:template match="Aus/au">
<table>
<tbody>
<xsl:apply-templates select="ele[1]/*" mode="row"/>
</tbody>
</table>
</xsl:template>
<xsl:template match="ele/*" mode="row">
<tr>
<xsl:variable name="pos" select="position()"/>
<xsl:apply-templates select="../../ele/*[$pos]"/>
</tr>
</xsl:template>
<xsl:template match="ele/*">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
https://xsltfiddle.liberty-development.net/gVhEaiK
The sample you linked in your comment seems to have more complex input data as it seems there are nested elements, also there seem to be lots of elements without data; however, the templates could be adapted to e.g.
<xsl:template match="authorDetails/authors">
<table>
<tbody>
<xsl:apply-templates
select="element[1]/descendant::*[not(*)]" mode="row"/>
</tbody>
</table>
</xsl:template>
<xsl:template match="element//*" mode="row">
<tr>
<th>
<xsl:value-of select="local-name()"/>
</th>
<xsl:variable name="pos" select="position()"/>
<xsl:apply-templates select="ancestor::authors/element/descendant::*[not(*)][$pos]"/>
</tr>
</xsl:template>
<xsl:template match="element//*">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
Example: https://xsltfiddle.liberty-development.net/gVhEaiK/5

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>

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.

Converting XHTML table with rowspan and colspan to LaTeX

I have been looking for a suitable solution for conversion of an HTML table to LaTeX. I have found the following questions which is similar to my requirement:
XML table to LaTeX
Converting XHTML table to LaTeX using XSLT
But both of these were not 100% helpful with my current requirement, as it does not involve in conversion of content with both colspan and rowspan.
The input HTML would look like:
<html>
<head>
</head>
<body>
<table border="1">
<tr>
<td>This</td>
<td>is</td>
<td>a</td>
<td>test</td>
</tr>
<tr>
<td colspan="2">This is</td>
<td>a</td>
<td>test</td>
</tr>
<tr>
<td>This</td>
<td>is</td>
<td colspan="2">a test</td>
</tr>
<tr>
<td rowspan="2">This</td>
<td colspan="2">is a</td>
<td rowspan="2">test</td>
</tr>
<tr>
<td>is</td>
<td>a</td>
</tr>
</table>
</body>
</html>
The LaTeX code I expect as the output is:
\documentclass{standalone}
\usepackage{multirow}
\begin{document}
\begin{tabular}{*{4}{|l}|}
\hline
This & is & a & test\\
\hline
\multicolumn{2}{|l|}{This is} & a & test \\
\hline
This & is & \multicolumn{2}{|l|}{a test} \\
\hline
\multirow{2}*{This} & \multicolumn{2}{|l|}{is a} & \multirow{2}*{test} \\
\cline{2-3}
& is & a & \\
\hline
\end{tabular}
\end{document}
I took the solution given by #DavidCarlisle in Converting XHTML table to LaTeX using XSLT and modified as given below:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/">
\documentclass{standalone}
\usepackage{multirow}
\begin{document}
<xsl:apply-templates/>
\end{document}
</xsl:template>
<xsl:template match="table">
<xsl:variable name="noc" select="max(tr/sum(td/(#colspan/number(.),1)[1]))"/>
<xsl:text>\begin{tabular}{*{</xsl:text>
<xsl:value-of select="$noc"/>
<xsl:text>}{|l}|}
</xsl:text>
<xsl:apply-templates select="tr[1]">
<xsl:with-param name="rspans" select="for $i in 1 to xs:integer($noc) return 0"/>
</xsl:apply-templates>
<xsl:text>\end{tabular}</xsl:text>
</xsl:template>
<xsl:template match="tr">
<xsl:param name="rspans"/>
<xsl:text/>% [<xsl:value-of select="$rspans"/>]
<xsl:variable name="tr" select="."/>
<xsl:for-each select="$rspans">
<xsl:variable name="c" select="position()"/>
<xsl:variable name="td" select="$tr/td[count($rspans[position() <=$c][.=0])]"/>
<xsl:if test=".=0">
<xsl:if test="$td/#rowspan[. > 1]">
<xsl:text>\multirow{</xsl:text>
<xsl:value-of select="$td/#rowspan"/>
<xsl:text>}{*}{</xsl:text>
</xsl:if>
<xsl:if test="$td/#colspan[. > 1]">
<xsl:text>\multicolumn{</xsl:text>
<xsl:value-of select="$td/#colspan"/>
<xsl:text>}{|l|}{</xsl:text>
</xsl:if>
<xsl:apply-templates select="$td"/>
<xsl:if test="$td/#colspan[. > 1]">}</xsl:if>
<xsl:if test="$td/#rowspan[. > 1]">}</xsl:if>
</xsl:if>
<xsl:choose>
<xsl:when test=". >1 and position()=last()">&\\\hline
</xsl:when>
<xsl:when test="position()=last()">\\\hline
</xsl:when>
<xsl:otherwise>&</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:apply-templates select="following-sibling::tr[1]">
<xsl:with-param name="rspans" select="for $c in 1 to count($rspans)
return
($rspans[$c] +
td[count($rspans[position() <=$c][.=0])]/(#rowspan,1)[1]
-1)"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
The resulting LaTeX code contains following problems:
I am not able to put the \cline{...} command properly. \cline{..} is reqired if there is a row spanned in the current row. Otherwise \hline is required.
In the second line there is an extra & (ampersand) symbol appearing at the end just before \\\hline.
In the last row, third column (content "a") is missing.
Is there any solution for this problem.

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>

XSL Displaying a message to the user if an element's count is 0

I have an element called channels which has been given values in the XML files. what i want to do is that when the XML file doesn't have any channel , display a message to the user.
here is my code.
<h2>Channels Subscription:</h2>
<xsl:template>
<xsl:if test=" <xsl:value-of select="count(subscription/channels)"/>
==0)">
<p>You have no channels</p>
</xsl:if>
</xsl:template>
the code above doesn't work
after that comes the other part wich is table.. when a user has a channel or more it gets displayed
<xsl:for-each select="subscription/channels">
<xsl:choose>
<xsl:when test="#favourite='true'">
<h3 >Channel Name:</h3> <h3 style="color:green;"><xsl:value-of select="#name"/></h3>
</xsl:when>
<xsl:otherwise>
<h3 >Channel Name: </h3><h3 style="color:black; "><xsl:value-of select="#name"/></h3>
</xsl:otherwise>
</xsl:choose>
<p>ID: <xsl:value-of select="#id"/></p>
<h3>Programs:</h3>
<xsl:for-each select="programs">
<table ID="gradient-style">
<tbody>
<tr>
<th>Program Name:</th>
<td><xsl:value-of select="name"/></td>
</tr>
<tr>
<th>Broadcast Time:</th>
<td>
<xsl:value-of select="#broadcastFrequency"/> at <xsl:value-of select="#broadcastTime"/>
</td>
</tr>
<tr>
<th>Description:</th>
<td><xsl:value-of select="description"/></td>
</tr>
</tbody>
</table>
</xsl:for-each>
and it works
Your xsl:if is wrong, it should be like
<xsl:if test="count(subscription/channels) = 0">
<p>You have no channels</p>
</xsl:if>
For reference: http://www.w3.org/TR/xslt#section-Conditional-Processing-with-xsl%3aif