Confused in XSLT scripting - xslt

hi i want to create an HTML table that shows the numbers(Years) in one column and the data in the second column. below is my xslt. i'm really confused as they have the same tags.
<chapter>
<row>
<entry>
<para>1984</para>
</entry>
<entry>
<para>International Business Companies Act passed into
law.</para>
</entry>
</row>
<row>
<entry>
<para>2004</para>
</entry>
<entry>
<para>BVI Business Companies Act passed into law, coming into
force on 1 January 2005.</para>
</entry>
</row>
<row>
<entry>
<para>2005</para>
</entry>
<entry>
<para>All three corporate statutes exist in parallel and it is
possible to incorporate companies under any of them.</para>
</entry>
</row>
<row>
<entry>
<para>2006</para>
</entry>
<entry>
<para>Incorporation provisions in the International Business
Companies Act and the Companies Act are repealed on 31 December
2005; the Acts remain in force but new companies may only be
incorporated under the BVI Business Companies Act.</para>
</entry>
</row>
</chapter>
Thanks

I am assuming that the first two elements of the XML that you posted are wrapped by a <row> element and all the rows are grouped under a parent element called rows.
If some of those assumptions are wrong, tell me and I will correct the code.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output mode="html" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="text()" />
<!-- I am assuming that the parent element for the set of row elements is
named rows. You can change this to match your XML -->
<xsl:template match="chapter">
<table>
<tr>
<th>Year</th>
<th>Data</th>
</tr>
<xsl:apply-templates select="row" />
</table>
</xsl:template>
<xsl:template match="row">
<tr>
<xsl:apply-templates select="*" />
</tr>
</xsl:template>
<xsl:template match="para">
<td><xsl:value-of select="." /></td>
</xsl:template>
</xsl:stylesheet>
UPDATE : If you just want to match the first entry/para element for each row, then you should use a template like the following one:
<xsl:template match="entry[1]/para">
<!-- Put your code here -->
</xsl:template>

Here's an example of creating a HTML table using XSLT. The dataset used in the example is very similar to your XML, just substitute the examples <book> tags with your <row> tags and so on.

Related

How to perform pagination of table for following case using xslt?

enter image description here
I want to paginate this table using xslt such that the last two p elements, and first two p elements should be together.
My input xml is somewhat like this:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<table>
<tgroup>
<row>
<entry>Parameter</entry>
<entry>Description</entry>
</row>
<row>
<entry>A</entry>
<entry><p>A1</p>
<p>A2</p>
<p>A3</p>
<p>A4</p>
<p>A5</p>
</entry>
</row>
<row>
<entry>B</entry>
<entry><p>B1</p>
<p>B2</p>
<p>B3</p>
<p>B4</p>
<p>B5</p>
</entry>
</row>
</tgroup>
</table>
And I want to automate adding outputclass attributes to p elements for pagination as shown here:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<table>
<tgroup>
<row>
<entry>Parameter</entry>
<entry>Description</entry>
</row>
<row>
<entry>A</entry>
<entry><p>A1</p>
<p>A2 outputclass="keep-with-previous"</p>
<p>A3</p>
<p>A4 outputclass="keep-with-next"</p>
<p>A5</p>
</entry>
</row>
<row>
<entry>B</entry>
<entry><p>B1</p>
<p>B2 outputclass="keep-with-previous"</p>
<p>B3</p>
<p>B4 outputclass="keep-with-next"</p>
<p>B5</p>
</entry>
</row>
</tgroup>
</table>
My css is as follows:
#media print
{ *[outputclass~="keep-with-next"] { page-break-after: avoid; }
*[outputclass~="keep-with-previous"] { page-break-before: avoid; }
*[outputclass~="top-of-page"] { page-break-before: always; } }
You might need these two templates:
<xsl:template match="row/entry[2]/p[2]">
<p outputclass="keep-with-previous" >
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="row/entry[2]/p[4]">
<p outputclass="keep-with-next">
<xsl:apply-templates/>
</p>
</xsl:template>

Find the maximum entries using xslt

I have a table and I want to find the maximum entries from tbody and thead. I have mentioned the table code below.
<table>
<thead>
<row>
<entry>
<p>aaa</p>
<p>aaa</p>
</entry>
</row>
<row>
<entry>
<p>bbb</p>
<p>bbb</p>
</entry>
<entry>
<p>ccc</p>
<p>ccc</p>
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<p>ddd</p>
<p>ddd</p>
</entry>
</row>
<row>
<entry>
<p>eee</p>
<p>eee</p>
</entry>
<entry>
<p>fff</p>
<p>fff</p>
</entry>
<entry>
<p>ggg</p>
<p>ggg</p>
</entry>
<entry>
<p>hhh</p>
<p>hhh</p>
</entry>
<entry>
<p>iii</p>
<p>iii</p>
</entry>
</row>
<row>
<entry>
<p>jjj</p>
<p>jjj</p>
</entry>
<entry>
<p>kkk</p>
<p>kkk</p>
</entry>
</row>
</tbody>
</table>
It means here maximum entry count is 5 and it is xpath is table/tbody/row[2]/entry. I want to find the maxmium entry value between thead and tbody.
I have used xslt code for this. But it is not work properly. It always returns me 0 as the answer.
<xsl:template match="table">
<xsl:variable name="is-notempty-table" select="max(count(thead/row/entry))" />
<xsl:value-of select="$is-notempty-table" />
</xsl:template>
Help me to solve this.
The problem with doing this...
<xsl:variable name="is-notempty-table" select="max(count(thead/row/entry))" />
Is that count(thead/row/entry) is only going to return a single value; namely the count of all entry nodes in the thead. (And it will return 3, not 0). If you want to find the maximum count of entry nodes for the rows within thead, the expression you want is this
<xsl:variable name="is-notempty-table" select="max(thead/row/count(entry))" />
Or, for tbody
<xsl:variable name="is-notempty-table" select="max(tbody/row/count(entry))" />
Or, to check the entire table...
<xsl:variable name="is-notempty-table" select="max(*/row/count(entry))" />
As an aside, the use of the variable name is-notempty-table suggests you are just wanting to check if the table has entry nodes in? If so, you can just do this...
<xsl:variable name="is-notempty-table" select="*/row/entry" />
<xsl:if test="$is-notempty-table">Table is not empty</xsl:if>
Inside <xsl:template match="table"> it seems you want to compute max(*/row/count(entry)) (if there are no other children for the table element) or perhaps max((thead|tbody)/row/count(entry)) if you expect other elements like a tfoot.
Both expressions assume XSLT/XPath 2 or later.

Change template so XSLT Outputs a sum instead of a list of values

I have an XSLT template that is working fine.
<xsl:template match="Row[contains(BenefitType, 'MyBenefit')]">
<value>
<xsl:value-of select="BenefitList/Row/Premium* 12" />
</value>
</xsl:template>
The output is
<value>100</value>
<value>110</value>
What I would prefer is if it would just output 220. So, basically in the template I would need to use some sort of variable or looping to do this and then output the final summed value?
XSLT 1 compliance is required.
The template is being used as follows:
<xsl:apply-templates select="Root/Row[contains(BenefitType, 'MyBenefit')]" />
For some reason, when I use the contains here it only sums the first structure that matches and not all of them. If The XML values parent wasn't dependent on having a sibling element that matched a specific value then a'sum' approach would work.
The direct solution to the problem was already mentioned in the comments, but assuming you really want to do the same with some variables, this might be interesting for you:
XML:
<Root>
<Row>
<BenefitType>MyBenefit</BenefitType>
<BenefitList>
<Premium>100</Premium>
</BenefitList>
</Row>
<Row>
<BenefitType>MyBenefit, OtherBenefit</BenefitType>
<BenefitList>
<Premium>100</Premium>
</BenefitList>
</Row>
<Row>
<BenefitType>OtherBenefit</BenefitType>
<BenefitList>
<Premium>1000</Premium>
</BenefitList>
</Row>
<Row>
<BenefitType>OtherBenefit</BenefitType>
<BenefitList>
<Premium>1000</Premium>
</BenefitList>
</Row>
</Root>
XSLT:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl">
<xsl:template match="/">
<total>
<xsl:variable name="valuesXml">
<values>
<xsl:apply-templates select="Root/Row[contains(BenefitType, 'MyBenefit')]" />
</values>
</xsl:variable>
<xsl:variable name="values" select="exsl:node-set($valuesXml)/values/value" />
<xsl:value-of select="sum($values)" />
</total>
</xsl:template>
<xsl:template match="Row[contains(BenefitType, 'MyBenefit')]">
<value>
<xsl:value-of select="BenefitList/Premium * 12" />
</value>
</xsl:template>
</xsl:stylesheet>
Here the same result set generated in your question is saved in another variable, which can then again be processed.

Compare 2 sets of child nodes to find a match where a least one of the child node values matches one of the other child node values

I have this XML with two sets of table data (element names generalized for simplicity).
<root>
<table>
<Row type="1">
<Id>AAAA</Id>
<Properties>
<Property>A</Property>
<Property>D</Property>
</Properties>
</Row>
<Row type="1">
<Id>BBBB</Id>
<Properties>
<Property>B</Property>
</Properties>
</Row>
<Row type="1">
<Id>CCCC</Id>
<Properties>
<Property>G</Property>
<Property>H</Property>
</Properties>
</Row>
</table>
<table>
<Row type="2">
<Id>123abc</Id>
<Properties>
<Property>A</Property>
<Property>D</Property>
<Property>E</Property>
</Properties>
</Row>
<Row type="2">
<Id>456def</Id>
<Properties>
<Property>B</Property>
<Property>C</Property>
<Property>I</Property>
</Properties>
</Row>
<Row type="2">
<Id>798ghi</Id>
<Properties>
<Property>F</Property>
<Property>G</Property>
<Property>H</Property>
</Properties>
</Row>
</table>
</root>
I am trying to write a transform to output a new table that associates a row from table 1 to a row in table 2 based on their properties. Rows in table 1 are not required to have all of the properties present in a row in table 2; any one property is all that's required to be considered a match. There will always only be a one to one relationship between a row in table 1 and a row in table 2.
My desired output is:
<root>
<Row>
<Name>NewTable:AAAA</Name>
<Table2Id>123abc</Table2Id>
</Row>
<Row>
<Name>NewTable:BBBB</Name>
<Table2Id>456def</Table2Id>>
</Row>
<Row>
<Name>NewTable:CCCC</Name>
<Table2Id>789ghi</Table2Id>
</Row>
</root>
I've started with this and have been trying to follow this logic: Find me the Id tag of the row in table 2 who has at least one property that matches one of the properties of the current row being processed in table 1.
Here's what I have so far. It's not working, but I feel like I'm close.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="//Row[#type != '2']">
<xsl:variable name="Name" select="concat('NewTable:', ./Id)"/>
<Row>
<Name>
<xsl:value-of select="$Name"/>
</Name>
<Table2Id>
<xsl:value-of select="//Row[#type = '2'][Property = ./Property]/Id"/>
</Table2Id>
</Row>
</xsl:template>
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
There are two things that can be very useful here: one is the key feature of XSLT that allows you to create a relationship based on matching values. The other is set comparison, where if at least one member of a set matches at least one member of another set, the sets will be considered matching.
Try the following stylesheet that takes advantage of both:
<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:key name="table2" match="Row[#type=2]" use="Properties/Property" />
<xsl:template match="/">
<root>
<xsl:for-each select="root/table/Row[#type=1]">
<row>
<Name>
<xsl:value-of select="Id" />
</Name>
<Table2Id>
<xsl:value-of select="key('table2', Properties/Property)/Id" />
</Table2Id>
</row>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
BTW, your method would have worked too (albeit less efficiently) if only you had used:
[Properties/Property = ./Properties/Property]
instead of just:
[Property = ./Property]
since you are in the context of a <Row> (a grandparent of <Property>).

A tricky XSLT transformation

I have a loosely structured XHTML data and I need to convert it to better structured XML.
Here's the example:
<tbody>
<tr>
<td class="header"><img src="http://www.abc.com/images/icon_apples.gif"/><img src="http://www.abc.com/images/flag/portugal.gif" alt="Portugal"/> First Grade</td>
</tr>
<tr>
<td>Green</td>
<td>Round shaped</td>
<td>Tasty</td>
</tr>
<tr>
<td>Red</td>
<td>Round shaped</td>
<td>Bitter</td>
</tr>
<tr>
<td>Pink</td>
<td>Round shaped</td>
<td>Tasty</td>
</tr>
<tr>
<td class="header"><img src="http://www.abc.com/images/icon_strawberries.gif"/><img src="http://www.abc.com/images/flag/usa.gif" alt="USA"/> Fifth Grade</td>
</tr>
<tr>
<td>Red</td>
<td>Heart shaped</td>
<td>Super tasty</td>
</tr>
<tr>
<td class="header"><img src="http://www.abc.com/images/icon_bananas.gif"/><img src="http://www.abc.com/images/flag/congo.gif" alt="Congo"/> Third Grade</td>
</tr>
<tr>
<td>Yellow</td>
<td>Smile shaped</td>
<td>Fairly tasty</td>
</tr>
<tr>
<td>Brown</td>
<td>Smile shaped</td>
<td>Too sweet</td>
</tr>
I am trying to achieve following structure:
<data>
<entry>
<type>Apples</type>
<country>Portugal</country>
<rank>First Grade</rank>
<color>Green</color>
<shape>Round shaped</shape>
<taste>Tasty</taste>
</entry>
<entry>
<type>Apples</type>
<country>Portugal</country>
<rank>First Grade</rank>
<color>Red</color>
<shape>Round shaped</shape>
<taste>Bitter</taste>
</entry>
<entry>
<type>Apples</type>
<country>Portugal</country>
<rank>First Grade</rank>
<color>Pink</color>
<shape>Round shaped</shape>
<taste>Tasty</taste>
</entry>
<entry>
<type>Strawberries</type>
<country>USA</country>
<rank>Fifth Grade</rank>
<color>Red</color>
<shape>Heart shaped</shape>
<taste>Super</taste>
</entry>
<entry>
<type>Bananas</type>
<country>Congo</country>
<rank>Third Grade</rank>
<color>Yellow</color>
<shape>Smile shaped</shape>
<taste>Fairly tasty</taste>
</entry>
<entry>
<type>Bananas</type>
<country>Congo</country>
<rank>Third Grade</rank>
<color>Brown</color>
<shape>Smile shaped</shape>
<taste>Too sweet</taste>
</entry>
</data>
Firstly I need to extract the fruit type from the tbody/tr/td/img[1]/#src, secondly the country from tbody/tr/td/img[2]/#alt attribute and finally the grade from tbody/tr/td itself.
Next I need to populate all the entries under each category while including those values (like shown above).
But... As you can see, the the data I was given is very loosely structured. A category is simply a td and after that come all the items in that category. To make the things worse, in my datasets, the number of items under each category varies between 1 and 100...
I've tried a few approaches but just can't seem to get it. Any help is greatly appreciated. I know that XSLT 2.0 introduces xsl:for-each-group, but I am limited to XSLT 1.0.
In this case, you are not actually grouping elements. It is more like ungrouping them.
One way to do this is to use an xsl:key to look up the "header" row for each of detail rows.
<xsl:key name="fruity"
match="tr[not(td[#class='header'])]"
use="generate-id(preceding-sibling::tr[td[#class='header']][1])"/>
i.e For each detail row, get the most previous header row.
Next, you can then match all your header rows like so:
<xsl:apply-templates select="tr/td[#class='header']"/>
Within the matching template, you could then extract the type, country and rank. Then to get the associated detail rows, it is a simple case of looking at the key for the parent row:
<xsl:apply-templates select="key('fruity', generate-id(..))">
Here is the overall XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="fruity"
match="tr[not(td[#class='header'])]"
use="generate-id(preceding-sibling::tr[td[#class='header']][1])"/>
<xsl:template match="/tbody">
<data>
<!-- Match header rows -->
<xsl:apply-templates select="tr/td[#class='header']"/>
</data>
</xsl:template>
<xsl:template match="td">
<!-- Match associated detail rows -->
<xsl:apply-templates select="key('fruity', generate-id(..))">
<!-- Extract relevant parameters from the td cell -->
<xsl:with-param name="type" select="substring-before(substring-after(img[1]/#src, 'images/icon_'), '.gif')"/>
<xsl:with-param name="country" select="img[2]/#alt"/>
<xsl:with-param name="rank" select="normalize-space(text())"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="tr">
<xsl:param name="type"/>
<xsl:param name="country"/>
<xsl:param name="rank"/>
<entry>
<type>
<xsl:value-of select="$type"/>
</type>
<country>
<xsl:value-of select="$country"/>
</country>
<rank>
<xsl:value-of select="$rank"/>
</rank>
<color>
<xsl:value-of select="td[1]"/>
</color>
<shape>
<xsl:value-of select="td[2]"/>
</shape>
<taste>
<xsl:value-of select="td[3]"/>
</taste>
</entry>
</xsl:template>
</xsl:stylesheet>
When applied to your input document, the following output is generated:
<data>
<entry>
<type>apples</type>
<country>Portugal</country>
<rank>First Grade</rank>
<color>Green</color>
<shape>Round shaped</shape>
<taste>Tasty</taste>
</entry>
<entry>
<type>apples</type>
<country>Portugal</country>
<rank>First Grade</rank>
<color>Red</color>
<shape>Round shaped</shape>
<taste>Bitter</taste>
</entry>
<entry>
<type>apples</type>
<country>Portugal</country>
<rank>First Grade</rank>
<color>Pink</color>
<shape>Round shaped</shape>
<taste>Tasty</taste>
</entry>
<entry>
<type>strawberries</type>
<country>USA</country>
<rank>Fifth Grade</rank>
<color>Red</color>
<shape>Heart shaped</shape>
<taste>Super tasty</taste>
</entry>
<entry>
<type>bananas</type>
<country>Congo</country>
<rank>Third Grade</rank>
<color>Yellow</color>
<shape>Smile shaped</shape>
<taste>Fairly tasty</taste>
</entry>
<entry>
<type>bananas</type>
<country>Congo</country>
<rank>Third Grade</rank>
<color>Brown</color>
<shape>Smile shaped</shape>
<taste>Too sweet</taste>
</entry>
</data>