Hi SO,
New to XSLT and have been debugging this code for a while
The current variable is always returning 0
I need to find the sum of all (X) with the same value of (D) through each (Row)
V and W are related, not sure how to "connect" them
Example:
Row (AAA123)[SomeDesc1] = 1.00 +
Row (BBB456)[SomeDesc1] = 3.00
SumOfSomeDesc1 = 4.00
XSLT 1.0 only
XML:
<Root>
<Row>
<ID>AAA123</ID>
<V>
<X>1.00</X>
</V>
<V>
<X>2.00</X>
</V>
<MultipleFieldsInBetween />
<W>
<D>SomeDesc1</D>
</W>
<W>
<D>SomeDesc2</D>
</W>
</Row>
<Row>
<ID>BBB456</ID>
<V>
<X>3.00</X>
</V>
<V>
<X>4.00</X>
</V>
<MultipleFieldsInBetween />
<W>
<D>SomeDesc1</D>
</W>
<W>
<D>SomeDesc2</D>
</W>
</Row>
</Root>
XSLT Sum (Current):
<xsl:variable name="SumOfX" select="sum(//Row[ID/text()=$ID]/V[D/text()
=$Description])" />
I would tackle it as a grouping problem, first identifying unique descriptions, then finding Rows by the description and finally summing up the elements in the same position:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:key name="desc-group" match="Row/W/D" use="."/>
<xsl:variable name="desc-groups" select="//Row/W/D[generate-id() = generate-id(key('desc-group', .)[1])]"/>
<xsl:key name="row-group" match="Row" use="W/D"/>
<xsl:template match="/Root">
<html>
<body>
<table>
<thead>
<tr>
<th>Description</th>
<th>Sum</th>
</tr>
</thead>
<tbody>
<xsl:apply-templates select="$desc-groups"/>
</tbody>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="Row/W/D">
<tr>
<td>
<xsl:value-of select="."/>
</td>
<td>
<xsl:variable name="pos" select="count(.. | ../preceding-sibling::W)"/>
<xsl:value-of select="sum(key('row-group', .)/V[position() = $pos]/X)"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
Result is
<html>
<body>
<table>
<thead>
<tr>
<th>Description</th>
<th>Sum</th>
</tr>
</thead>
<tbody>
<tr>
<td>SomeDesc1</td>
<td>4</td>
</tr>
<tr>
<td>SomeDesc2</td>
<td>6</td>
</tr>
</tbody>
</table>
</body>
</html>
Related
I'm using XSL1.0. My editor/debugger is OxygenXML with Saxon (OxygenXML can't debug with MSXML) and it will deployed to work with a 3rd party app that only uses MSXML. This means I can't use a variable containing a nodeset if I want to be able to debug.
The problem could probably be expressed as how to sequentially number output of the following -
<xsl:for-each select="node1">
<xsl:variable name="current_ID" select="ID">
<xsl:for-each select="sub_node1">
<xsl:value-of select="../ID"/>-<xsl:value-of select="Sub_ID"/>
</xsl:for-each>
</xsl:for-each>
understanding that I cannot simply use this in my scenario:
<xsl:for-each select="node1/sub_node1">
<xsl:value-of select="position()"/>
</xsl:for-each>
Below is a manufactured example that shows the problem I'm trying to solve as part of a much larger XSL/XML combo. I basically need to create manufacturing instructions. All nodes in the XML with the exception of products/versions (by qty) are in the correct order and I cannot change it. I need to generate the same set of sequential numbers from 3 different XSL's. My current context will always be shipments/deliveries/delivery_products (i.e. my XSL has to process the nodes in the seq shown). I need to produce a list of products sorted by version qty and their deliveries. Each row should have a sequential no (1-4) in example below
<shipments>
<product>
<name>Product 1</name>
<prod_id>P1</prod_id>
<version>
<version_id>P1_V1</version_id>
<qty>8800</qty>
</version>
<version>
<version_id>P1_V2</version_id>
<qty>1100</qty>
</version>
<version>
<version_id>P1_V3</version_id>
<qty>100</qty>
</version>
</product>
<product>
<name>Product 2</name>
<prod_id>P2</prod_id>
<version>
<version_id>P2_V1</version_id>
<qty>5000</qty>
</version>
<version>
<version_id>P2_V2</version_id>
<qty>5000</qty>
</version>
<version>
<version_id>P2_V3</version_id>
<qty>2000</qty>
</version>
</product>
<deliveries>
<del_id>1</del_id>
<destination>Miami</destination>
<delivery_products>
<version_id>P1_V1</version_id>
<qty>8000</qty>
</delivery_products>
<delivery_products>
<version_id>P2_V1</version_id>
<qty>5000</qty>
</delivery_products>
</deliveries>
<deliveries>
<del_id>2</del_id>
<destination>New York</destination>
<delivery_products>
<version_id>P1_V1</version_id>
<qty>800</qty>
</delivery_products>
<delivery_products>
<version_id>P2_V2</version_id>
<qty>1000</qty>
</delivery_products>
</deliveries>
Expected output is below. Note seq # starts from 1 and counts up to 4
<table>
<thead>
<tr>
<td class="col_head">
Seq
</td>
<td class="col_head">
Version
</td>
<td class="col_head">
Destination
</td>
<td class="col_head">
Qty
</td>
</tr>
</thead>
<tr>
<td colspan="4" class="rev_heading">Product 1</td>
</tr>
<tr>
<td>1</td>
<td>P1_V1</td>
<td>Miami</td>
<td>8000</td>
</tr>
<tr>
<td>2</td>
<td>P1_V1</td>
<td>New York</td>
<td>800</td>
</tr>
<tr>
<td colspan="4" class="rev_heading">Product 2</td>
</tr>
<tr>
<td>3</td>
<td>P2_V1</td>
<td>Miami</td>
<td>5000</td>
</tr>
<tr>
<td>4</td>
<td>P2_V2</td>
<td>New York</td>
<td>5000</td>
</tr>
</table>
Here's my XSL so far (just stuck a position() in for a place holder for the seq #)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
<xsl:template match="shipments">
<html>
<head>
<title>Seq Test</title>
<style type="text/css">
table {border: 1px solid black; border-collapse: collapse;}
td {border: 1px solid black; padding: 1px 5px 1px 5px;}
.col_head {font-weight: 600;}
.rev_heading {color: red; text-align: center; padding-top: 15px;}
</style>
</head>
<body>
<table>
<thead>
<tr>
<!-- SEQ# -->
<td class="col_head">
Seq
</td>
<!-- Imprint/Version -->
<td class="col_head">
Version
</td>
<!-- Ship to -->
<td class="col_head">
Destination
</td>
<!-- Qty -->
<td class="col_head">
Qty
</td>
</tr>
</thead>
<xsl:for-each select="product">
<xsl:sort data-type="number" select="qty"/>
<xsl:for-each select="version">
<xsl:variable name="curr_version" select="version_id"/>
<xsl:if test="position() = 1">
<tr>
<td colspan="4" class="rev_heading">
<xsl:value-of select="../name"/>
</td>
</tr>
</xsl:if>
<xsl:for-each select="../../deliveries/delivery_products[version_id = $curr_version]">
<tr >
<!-- SEQ# -->
<td>
<xsl:value-of select="position()"/>
</td>
<!-- Version -->
<td>
<xsl:value-of select="version_id"/>
</td>
<!-- Ship to -->
<td>
<xsl:value-of select="../destination"/>
</td>
<!-- QTY -->
<td>
<xsl:value-of select="qty"/>
</td>
</tr>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
I'm using XSL1.0. My editor/debugger is OxygenXML with Saxon
(OxygenXML can't debug with MSXML) and it will deployed to work with a
3rd party app that only uses MSXML. This means I can't use a variable
containing a nodeset if I want to be able to debug.
You can still use oXygen and the EXSLT node-set() function.
When you are finished, simply change the namespace-uri from "http://exslt.org/common" to "urn:schemas-microsoft-com:xslt"
Here is a short example of this technique. Suppose you are finished debugging the below transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates select="num[. mod 3 = 0]"/>
</xsl:variable>
<xsl:copy-of select="sum(ext:node-set($vrtfPass1)/*)"/>
</xsl:template>
<xsl:template match="num">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
Then you make the change from the EXSLT namespace-uri to the MSXSL namespace uri:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="urn:schemas-microsoft-com:xslt">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates select="num[. mod 3 = 0]"/>
</xsl:variable>
<xsl:copy-of select="sum(ext:node-set($vrtfPass1)/*)"/>
</xsl:template>
<xsl:template match="num">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
Finally, you run this last transformation with MSXML and it produces exactly the same result as the initial transformation that uses EXSLT:
18
Here is a fraction of the XML data I am processing
<?xml version="1.0" encoding="utf-16"?>
<ScorecardSummary>
<DivisionSummary>
<DivisionName>
<string> SYSTEM</string>
</DivisionName>
<ScorecardSummaryByDivision>
<ScorecardSummaryByKPI>
<Header>
<string>Committed Time of Arrival</string>
<string>Goal</string>
<string>1D</string>
<string>7D</string>
<string>QTD</string>
<string>YTD</string>
<string>YTD Event Cars</string>
</Header>
<Data>
<ScorecardContract>
<TypeName>System</TypeName>
<Goal>68</Goal>
<GoalWarning>64.6</GoalWarning>
<TotalCountYear>1234</TotalCountYear>
<Value1D>79</Value1D>
<Value7D>79.2</Value7D>
<ValueQTD>79.1</ValueQTD>
<ValueYTD>73.3</ValueYTD>
</ScorecardContract>
<ScorecardContract>
<TypeName>AG</TypeName>
<Goal>68</Goal>
<GoalWarning>64.6</GoalWarning>
<TotalCountYear>1111</TotalCountYear>
<Value1D>80.9</Value1D>
<Value7D>78.7</Value7D>
<ValueQTD>78.4</ValueQTD>
<ValueYTD>69.7</ValueYTD>
</ScorecardContract>
This is a small part of the XSL that produces the tables:
<xsl:template match="ScorecardSummary/DivisionSummary/DivisionName">
<h1>
<xsl:value-of select="current()/string"/>
</h1>
</xsl:template>
<xsl:template match="ScorecardSummaryByDivision">
<xsl:apply-templates select="current()/ScorecardSummaryByKPI"/>
</xsl:template>
<xsl:template match="ScorecardSummaryByKPI">
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<xsl:choose>
<xsl:when test="count(preceding-sibling::ScorecardSummaryByKPI) mod 6 < 4">
<td>
<table border="1" cellspacing="0" cellpadding="5">
<xsl:apply-templates select="Header"/>
<xsl:apply-templates select="Data"/>
</table>
</td>
</xsl:when>
<xsl:otherwise>
<td>
<table border="1" cellspacing="0" cellpadding="5">
<xsl:apply-templates select="Header"/>
<xsl:apply-templates select="Data"/>
</table>
</td>
</xsl:otherwise>
</xsl:choose>
</tr>
</table>
</xsl:template>
The XSL produces 6 tables repeatedly like this:
1
2
3
4
5
6
1
2
3
4
5
6
But I want to order them like this:
1 4
2 5
3 6
1 4
2 5
3 6
and so on. I tried using this check, but it doesn't work.
count(preceding-sibling::ScorecardSummaryByKPI) mod 6 < 4
Can anyone help?
Explanation
Your table must have two <td> per row (if you want two columns). Your XSLT does generate only one.
Solution is to interate over one half of the list and generate two <td> per iteration.
So first I would define a size of the table. Example:
<xsl:param name="size" select="count(catalog/cd)"/>
Then iterate over only a half of it ($size div 2). The number must be rounded if the input list can contain a non-even number of elements: ceiling($size div 2) (Rounding up to catch last element)
<xsl:for-each select="catalog/cd[ceiling($size div 2) >= position()]">
In each iteration, first render an element itself:
<td><xsl:value-of select="title"/></td>
Then render an appropriate element from the second half of the table (offset is the number defined before: ceiling($size div 2) Half size of the table)
<td><xsl:value-of select="following::cd[ceiling($size div 2)]/title"/></td>
You can wrap element rendering in a separate template to avoid code repeating.
Working example
Check this transformation example with W3C XSL TryIt (http://www.w3schools.com/xsl/tryxslt.asp?xmlfile=cdcatalog&xsltfile=cdcatalog):
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:param name="size" select="count(catalog/cd)"/>
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Title</th>
</tr>
<xsl:for-each select="catalog/cd[ceiling($size div 2) >= position()]">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="following::cd[ceiling($size div 2)]/title"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
It splits CD-catalog (given in example link above) in two columns.
Perhaps something like this is what you are looking for:
(This only shows the idea, you have to adapt it to your input.)
<?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">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/" >
<xsl:apply-templates select="//t[count(preceding-sibling::t) < 3]" mode="tables" />
</xsl:template>
<xsl:template match="t" >
{<xsl:value-of select="text()"/>}
</xsl:template>
<xsl:template match="t" mode="tables">
<table border="1">
<tr>
<td>
<table border="1" >
<xsl:apply-templates select="." />
</table>
</td>
<td>
<table border="1">
<xsl:apply-templates select="following-sibling::t[count(preceding-sibling::t) = count(current()/preceding-sibling::t) +3]" />
</table>
</td>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>
With this short test input xml:
<?xml version="1.0" encoding="utf-8"?>
<xml>
<tables>
<t>1</t>
<t>2</t>
<t>3</t>
<t>4</t>
<t>5</t>
<t>6</t>
</tables>
</xml>
It will generate this output:
<?xml version="1.0"?>
<table xmlns="http://www.w3.org/1999/xhtml" border="1">
<tr>
<td>
<table border="1">
{1}
</table>
</td>
<td>
<table border="1">
{4}
</table>
</td>
</tr>
</table><table xmlns="http://www.w3.org/1999/xhtml" border="1">
<tr>
<td>
<table border="1">
{2}
</table>
</td>
<td>
<table border="1">
{5}
</table>
</td>
</tr>
</table><table xmlns="http://www.w3.org/1999/xhtml" border="1">
<tr>
<td>
<table border="1">
{3}
</table>
</td>
<td>
<table border="1">
{6}
</table>
</td>
</tr>
</table>
I have the following xml:
input
<page>
<group category="cat1">
<item fileunder="#">.45 colt</item>
<item fileunder="#">8 queens</item>
<item fileunder="#">9 lives</item>
<item fileunder="#">99 bottles of beer</item>
<item fileunder="A">An innocent man</item>
<item fileunder="A">Academy awards</item>
<item fileunder="B">Before the dawn</item>
</group>
<group category="cat2">
<item fileunder="R">Rows of houses</item>
</group>
</page>
The input items are already sorted.
desired output
I want to produce a 3-column HTML table for every group, with a subheading (a 3-column spanning cell) for each distinct fileunder, optimally presented in a top-down, then-next-column (the items are already sorted):
<table>
<tr><td colspan="3">#</td></tr>
<tr><td>.45 colt</td><td>9 lives</td><td>99 bottles of beer</td></tr>
<tr><td>8 queens</td></tr>
<tr><td colspan="3">A</td></tr>
<tr><td>An innocent man</td><td>Academy awards</td></tr>
<tr><td colspan="3">B</td></tr>
<tr><td>Before the dawn</td></tr>
</table>
<table>
<tr><td colspan="3">R</td></tr>
<tr><td>Rows of houses</td></tr>
</table>
I can live if the items are presented as left-to-right, then-next-row.
What I have so far is:
current xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="itm_grp" match="/page/group/item" use="concat(../#category,':',#fileunder)"/>
<xsl:template match="page/group">
<table>
<xsl:for-each select="item[.=key('itm_grp',concat(../#category,':',#fileunder))[1]]">
<tr><td colspan="3"><xsl:value-of select="#fileunder"/></td></tr>
<xsl:variable name="nodeset" select="key('itm_grp',concat(../#category,':',#fileunder))"/>
<xsl:for-each select="$nodeset[position() mod 3=1]">
<tr>
<td><xsl:value-of select="."/></td>
<td><xsl:value-of select="following-sibling::item[1]"/></td>
<td><xsl:value-of select="following-sibling::item[2]"/></td>
</tr>
</xsl:for-each>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
which produces a left-to-right, then-next-row output (non-optimal); however, the following-sibling selects produce a “bleed-through” effect:
#
.45 colt 8 queens 9 lives
99 bottles of beer An innocent man Academy awards
A
An innocent man Academy awards Before the dawn
B
Before the dawn
R
Rows of houses
As you can see, fileunder # has two A items, and fileunder A has one B item.
So, my question is:
How can I produce the desired output (column-wise)?
If I can't do that, how can I have the row-wise output avoiding the “bleeding”?
Please note that I have very little experience with XSLT, so if my code is blatantly inefficient/idiotic/whatever, please feel free to educate me by replacing all of it!
NB: XSLT version 1, so apparently no index-of function is available.
There is a slight contradiction between your narrative and your listed expected output. You have asked for top-down, then left-right column fill order, which you have so in the listing for the non-empty values, but not for the empties. This spatial order implies that a whole column must be filled out before the next column can begin. I have assumed that your listing was a mistake and what your really want in output is ...
<table>
<tr>
<td colspan="3">#</td>
</tr>
<tr>
<td>.45 colt</td>
<td>9 lives</td>
<td>&npsp;</td>
</tr>
<tr>
<td>8 queens</td>
<td>99 bottles of beer</td>
<td>&npsp;</td>
</tr>
<tr>
<td colspan="3">A</td>
</tr>
<tr>
<td>An innocent man</td>
<td>Academy awards</td>
<td>&npsp;</td>
</tr>
<tr>
<td colspan="3">B</td>
</tr>
<tr>
<td>Before the dawn</td>
<td>&npsp;</td>
<td>&npsp;</td>
</tr>
</table>
<table>
<tr>
<td colspan="3">R</td>
</tr>
<tr>
<td>Rows of houses</td>
<td>&npsp;</td>
<td>&npsp;</td>
</tr>
</table>
... which is consistent top-down, then left-right column fill order.
This XSLT 1.0 style-sheet...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:key name="kItemByFile" match="item" use="concat(../#category,':',#fileunder)"/>
<xsl:template match="/">
<html lang="en">
<head><title>Songs</title></head>
<body>
<xsl:apply-templates select="*/group" />
</body>
</html>
</xsl:template>
<xsl:template match="group">
<xsl:variable name="cat" select="concat(#category,':')" />
<table>
<xsl:apply-templates select="item[
generate-id() = generate-id(key('kItemByFile',concat($cat,#fileunder))[1])]"
mode="group-head" />
</table>
</xsl:template>
<xsl:template match="item" mode="group-head">
<xsl:variable name="items"
select="key('kItemByFile',concat(../#category,':',#fileunder))" />
<xsl:variable name="row-count" select="ceiling( count($items) div 3)" />
<tr><td colspan="3"><xsl:value-of select="#fileunder" /></td></tr>
<xsl:for-each select="$items[position() <= $row-count]">
<xsl:variable name="pos" select="position()" />
<xsl:apply-templates select="." mode="row">
<xsl:with-param name="items" select="$items" />
<xsl:with-param name="row" select="$pos" />
<xsl:with-param name="row-count" select="$row-count" />
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="item" mode="row">
<xsl:param name="items" select="/.." />
<xsl:param name="row" select="1" />
<xsl:param name="row-count" select="1" />
<tr>
<xsl:apply-templates select="
$items[(position() mod $row-count) = ($row mod $row-count)]" mode="td" />
<xsl:variable name="full-cols" select="floor((count($items) div $row-count))" />
<xsl:variable name="part-col" select="number($row <
((count($items) mod $row-count) + 1))" />
<xsl:variable name="empties" select="3 - ($full-cols + $part-col)" />
<xsl:for-each select="(document('')/*/*)[position() <= $empties]">
<xsl:call-template name="empty-cell" />
</xsl:for-each>
</tr>
</xsl:template>
<xsl:template match="item" mode="td">
<td><xsl:value-of select="." /></td>
</xsl:template>
<xsl:template name="empty-cell">
<td> </td>
</xsl:template>
</xsl:stylesheet>
...when applied to this input...
<page>
<group category="cat1">
<item fileunder="#">.45 colt</item>
<item fileunder="#">8 queens</item>
<item fileunder="#">9 lives</item>
<item fileunder="#">99 bottles of beer</item>
<item fileunder="A">An innocent man</item>
<item fileunder="A">Academy awards</item>
<item fileunder="B">Before the dawn</item>
</group>
<group category="cat2">
<item fileunder="R">Rows of houses</item>
</group>
</page>
...yields...
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en">
<head>
<META http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Songs</title>
</head>
<body>
<table>
<tr>
<td colspan="3">#</td>
</tr>
<tr>
<td>.45 colt</td>
<td>9 lives</td>
<td> </td>
</tr>
<tr>
<td>8 queens</td>
<td>99 bottles of beer</td>
<td> </td>
</tr>
<tr>
<td colspan="3">A</td>
</tr>
<tr>
<td>An innocent man</td>
<td>Academy awards</td>
<td> </td>
</tr>
<tr>
<td colspan="3">B</td>
</tr>
<tr>
<td>Before the dawn</td>
<td> </td>
<td> </td>
</tr>
</table>
<table>
<tr>
<td colspan="3">R</td>
</tr>
<tr>
<td>Rows of houses</td>
<td> </td>
<td> </td>
</tr>
</table>
</body>
</html>
Note
For the empty cells in the output, when viewing the lexical HTML, you will get either or the literal white space equivalent. It is XSLT processor implementation dependant, but should not cause you any concern because it is model-equivalent.
Easiest way to fix that:
<xsl:variable name="header" select="#fileunder"/>
...
<xsl:value-of select="following-sibling::item[#fileunder=$header][1]"/>
<xsl:value-of select="following-sibling::item[#fileunder=$header][2]"/>
I want to perform division of tables into three in my xslt code dynamically.
Give me structure to append my text at specific node like if i am having 100 nodes i need to append my text at 33rd 66th nodes .
My XSLT code :
<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:key name="routes-by-origin" match="/schedules/Routes/org" use="./text()"/>
<xsl:template match="schedules" name="Main">
<html>
<head></head>
<body>
<table>
<tr> <td> Heading </td> </tr>
<table>
<xsl:for-each select="Routes/org[generate-id(.) = generate-id(key('routes-by-origin',.)[1])]">
<xsl:value-of select="./text()"/>
<xsl:apply-templates select="//Routes[org/text() = current()/text()]"/>
</xsl:for-each>
</table>
</xsl:template>
Template design called from above template
<xsl:template match="Routes">
<xsl:value-of select="des/text()"/>
<xsl:variable name="via" select="Via/text()"/>
<xsl:value-of select="Flgno/text()"/>
</xsl:template>
</xsl:stylesheet>
Input Document
<schedules>
<Routes>
<org>Agartala</org>
<des>Bangalore</des>
<Flgno>SG 872</Flgno>
<Via>CCU, HYD</Via>
</Routes>
<Routes>
<org>Agartala</org>
<des>Guwahati</des>
<Flgno>SG 873</Flgno>
<Via> BOM </Via>
</Routes>
<Routes>
<org>Agartala</org>
<des>Hyderabad</des>
<Flgno>SG 872</Flgno>
<Via>CCU</Via>
</Routes>
<Routes>
<org>Agartala</org>
<des>Kolkata</des>
<Flgno>SG 872</Flgno>
<Via> - </Via>
</Routes>
<Routes>
<org>Agartala</org>
<des>Kolkata</des>
<Flgno>SG 874</Flgno>
<Via> - </Via>
</Routes>
<Routes>
<org>Agartala</org>
<des>Mumbai</des>
<Flgno>SG 874</Flgno>
<Via>CCU</Via>
</Routes>
<Routes>
<org>Ahmedabad</org>
<des>Bangalore</des>
<Flgno>SG 528</Flgno>
<Symbols> - </Symbols>
<Via>BOM</Via>
</Routes>
</schedules>
Expected Result
My output should be in three tables in one page:
in first table
first 2 records
second table - next 2 records
third table last 3 records
2 records
2 records
3 records
<table>
<table>
<tr>
<td>Agartala</td>
<td>Bangalore</td>
<td>SG 872</td>
<td>CCU</td>
</tr>
<tr>
<td> Agartala</td>
<td>Guwahati</td>
<td>SG 87</td>
<td>BOM</td>
</tr>
</table>
<table>
<tr>
<td>Agartala</td>
<td>Hyderabad</td>
<td>SG 872 </td>
<td>CCU</td>
</tr>
<tr>
<td>Agartala</td>
<td>Kolkatta</td>
<td>SG 872</td>
<td> - </td>
</tr>
</table>
<table>
<tr>
<td> Agartala</td>
<td>Kolkatta</td>
<td>SG 874</td>
<td> - </td>
</tr>
<tr>
<td>Agartala</td>
<td>Mumbai</td>
<td>SG 874 </td>
<td>CCU</td>
</tr>
<tr>
<td>Agartala</td>
<td>Bangalore</td>
<td>SG 528</td>
<td>BOM</td>
</tr>
</table>
</table>
Note : My input data is dynamic
This XSLT 1.0 style-sheet is a copy of Dimitre's solution here, slightly tweaked for the OP's particular data and having the 3rd table have ceil( row-count / 3) rows.
XSLT 1.0 Solution
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="prowLimit" select="floor(count(/*/Routes) div 3)" />
<xsl:template match="/">
<table>
<xsl:apply-templates select="*/Routes" />
</table>
</xsl:template>
<xsl:template match="Routes">
<xsl:if test="(position() mod $prowLimit = 1) and
(position() < (3 * $prowLimit + 1))">
<xsl:variable name="is-last-table" select="position() - (2*$prowLimit)" />
<table>
<xsl:for-each select=".|following-sibling::Routes[
not(position() > $prowLimit - 1) or ($is-last-table > 0)]" >
<tr>
<td><xsl:value-of select="org" /></td>
<td><xsl:value-of select="des" /></td>
<td><xsl:value-of select="Flgno" /></td>
<td><xsl:value-of select="Via" /></td>
</tr>
</xsl:for-each>
</table>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
XSLT 2.0 Solution
It gets easier in XSLT 2.0 ...
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="xsl fn">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="prowLimit" select="floor(count(/*/Routes) div 3)" />
<table>
<xsl:for-each-group select="*/Routes"
group-adjacent="fn:min((floor((position() - 1) div $prowLimit),2))" >
<table>
<xsl:apply-templates select="current-group()" />
</table>
</xsl:for-each-group>
</table>
</xsl:template>
<xsl:template match="Routes">
<tr>
<td><xsl:value-of select="org" /></td>
<td><xsl:value-of select="des" /></td>
<td><xsl:value-of select="Flgno" /></td>
<td><xsl:value-of select="Via" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>
I'm calling a template:
<table>
<xsl:apply-templates select="data/pics/row"/>
</table>
The template is
<xsl:template match="row">
<tr>
<xsl:for-each select="td">
<td border="0">
<a href="{#referencddePage}">
<img src="{pic/#src}" width="{pic/#width}" height="{pic/#height}"/>
</a>
</td>
</xsl:for-each>
</tr>
</xsl:template>
My XML is:
<?xml version="1.0" encoding="iso-8859-8"?>
<?xml-stylesheet type="text/xsl" href="xslFiles\smallPageBuilder.xsl"?>
<data pageNo="3" referencePage="xxxxxxxxxxxxxxx.xml">
<pics>
<row no="0">
<td col="0">
<pic src="A.jpg" width="150" height="120"></pic>
</td>
</row>
</pics>
</data>
I want the line :a h r e f="{#referencddePage}" to get the input from
the root, :a h r e f= "{#referencddePage}"..., but I'm already in the <td level>.
I want the line :a h r e
f="{#referencddePage}" to get the
input from the root :a h r e f=
"{#referencddePage}"... but I'm
already in the <td level>
In case it is a rule that the #referencePage attribute is always an attribute of the top element, then it can always be accessed as:
/*/#referencePage
Therefore, in your code you'll have:
<a href="{/*/#referencePage}">
I would recommend not to use <xsl:for-each> and to use only and`. In this way the resulting XSLT code is more understandable and can be more easily modified in the future:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="row">
<tr>
<xsl:apply-templates/>
</tr>
</xsl:template>
<xsl:template match="td">
<td border="0">
<a href="{/*/#referencePage}">
<xsl:apply-templates/>
</a>
</td>
</xsl:template>
<xsl:template match="pic">
<img src="{#src}" width="{#width}" height="{#height}"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document,
<data pageNo="3" referencePage="xxxxxxxxxxxxxxx.xml">
<pics>
<row no="0">
<td col="0">
<pic src="A.jpg" width="150" height="120"></pic>
</td>
</row>
</pics>
</data>
the wanted output is produced:
<tr>
<td border="0">
<a href="xxxxxxxxxxxxxxx.xml">
<img src="A.jpg" width="150" height="120"/>
</a>
</td>
</tr>
See how each template is so very simple. Also, the code is further simplified.
Now, instead of:
<img src="{pic/#src}" width="{pic/#width}" height="{pic/#height}"/>
we have only:
<img src="{#src}" width="{#width}" height="{#height}"/>
Use an XPATH that "jumps" to the top of the document with a leading slash, then walk down the tree:
/data/#referencePage
Applying it to your stylesheet:
<xsl:template match="row">
<tr>
<xsl:for-each select="td">
<td border="0">
<a href="{/data/#referencePage}">
<img src="{pic/#src}" width="{pic/#width}" height="{pic/#height}"/>
</a>
</td>
</xsl:for-each>
</tr>
</xsl:template>