XSLT Row counter excluding Null and Zero - xslt

<Rows>
<Row RowNo="1" Amount="0,0"/>
<Row RowNo="2" Amount="12"/>
<Row RowNo="3" Amount=""/>
<Row RowNo="4" Amount="00"/>
<Row RowNo="5" Amount="33"/>
<Row RowNo="6" Amount="0,00"/>
<Row RowNo="7" Amount="0"/>
<Row RowNo="8" Amount="00,2"/>
</Rows>
These Row's are my input. I want the output to exclude rows where #Amount is '' or 0. And also not count the row for when #Amount = '' and = 0.
Expected result:
<Rows>
<Row RowNo="1" Amount="12"/>
<Row RowNo="2" Amount="33"/>
<Row RowNo="3" Amount="00,2"/>
</Rows>
How can I do this?

With your given input, a specific (based on input) solution in XSLT 1.0 can be as following:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Rows>
<xsl:for-each select="Rows/Row">
<xsl:choose>
<xsl:when test="#Amount != '' and (number(#Amount) * 1) != '0' and translate(#Amount, '0', '') != ','">
<xsl:copy>
<xsl:attribute name="RowNo">
<xsl:value-of select="count(preceding-sibling::*[#Amount != '' and (number(#Amount) * 1) != '0' and translate(#Amount, '0', '') != ',']) + 1" />
</xsl:attribute>
<xsl:for-each select="#*">
<xsl:if test="name() != 'RowNo'">
<xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</Rows>
</xsl:template>

How about something simple, for a change?
XSLT 1.0
<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="/Rows">
<xsl:copy>
<xsl:for-each select="Row[translate(#Amount, ',0', '')]">
<Row RowNo="{position()}" Amount="{#Amount}"/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

XSLT element selection based on Choose not working

I have the following XML:
<Document>
<Row>
<KEY>NIKE|JB|APPAREL|MENS</KEY>
<Period>Nov-21</Period>
</Row>
<Row>
<KEY>FASCINATE|JB|ACCESSORIES|LADIES</KEY>
<Matches>
<Row>
<KEY>FASCINATE|JB|ACCESSORIES|LADIES</KEY>
<Period>Nov-22</Period>
</Row>
</Matches>
</Row>
</Document>
I want to use XSLT to return the nested /Matches/Row/Period when the Document/Row/Period is undefined (as it is in the second Row of the XML)
So I have the following XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns="http://exampleincludednamespace.com/"
exclude-result-prefixes="ns">
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="/">
<Document>
<xsl:for-each select="/Document/Row">
<xsl:variable name="period" select="/Period" />
<xsl:choose>
<xsl:when test="$period = null">
<xsl:copy>
<xsl:copy-of select="KEY | /Matches/Row/Period" />
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:copy-of select="KEY | Period" />
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</Document>
</xsl:template>
</xsl:stylesheet>
But it returns the following output:
<Document>
<Row>
<KEY>NIKE|JB|APPAREL|MENS</KEY>
<Period>Nov-21</Period>
</Row>
<Row>
<KEY>FASCINATE|JB|ACCESSORIES|LADIES</KEY>
</Row>
</Document>
(Note how it is not returning the nested /Matches/Row/Period in the second /Row.
I expect to get the following output:
<Document>
<Row>
<KEY>NIKE|JB|APPAREL|MENS</KEY>
<Period>Nov-21</Period>
</Row>
<Row>
<KEY>FASCINATE|JB|ACCESSORIES|LADIES</KEY>
<Period>Nov-22</Period>
</Row>
</Document>
What am I doing wrong?
undefined or null are not checked in XSLT/XPath using expression = null, you would rather use (for node-sets) <xsl:when test="expression"> e.g. <xsl:when test="Period"> that there is at least one Period child element for the context node or test="not(Period)" to check there is no Period child.
In the end I would suggest to use template matching based on the identity transformation template and put any conditions into match pattern (predicates), but that is a different issue.
Got it working with this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns="http://exampleincludednamespace.com/"
exclude-result-prefixes="ns">
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="/">
<Document>
<xsl:for-each select="/Document/Row">
<xsl:choose>
<xsl:when test="not(Period)">
<xsl:copy>
<xsl:copy-of select="KEY | Matches/Row/Period" />
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:copy-of select="KEY | Period" />
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</Document>
</xsl:template>
</xsl:stylesheet>
Thanks to #MartinHonnen's guidance.
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="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/Document">
<xsl:copy>
<xsl:for-each select="Row">
<xsl:copy>
<xsl:copy-of select="KEY | descendant::Period[1]" />
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Generate new group based on node value in XSLT

I am new to XSLT, and I am trying to create new group based on node value eventType so if eventType is alert, create new group event.
I am checking for last sibling
Input XML
<?xml version="1.0" encoding="UTF-8"?><Rowsets >
<Row>
<eventId>2</eventId>
<plantId>1020</plantId>
<workCenter>WC1</workCenter>
<eventType>alert</eventType>
<eventText>Downtime</eventText>
<eventDesc>WorkcenterDown</eventDesc>
</Row>
<Row>
<eventId>3</eventId>
<plantId>1021</plantId>
<workCenter>WC1</workCenter>
<eventType>alert</eventType>
<eventText>Downtime</eventText>
<eventDesc>WorkcenterDown</eventDesc>
</Row>
<Row>
<eventId>4</eventId>
<plantId>1020</plantId>
<workCenter>WC2</workCenter>
<eventType>incident</eventType>
<eventText>eventtext</eventText>
<eventDesc>failed</eventDesc>
</Row>
<Row>
<plantId>1020</plantId>
<workCenter>WC2</workCenter>
<eventType>incident</eventType>
<eventText>Text</eventText>
<eventDesc>failed</eventDesc>
</Row>
</Rowsets>
Expected output:
<?xml version="1.0" encoding="UTF-8"?>
<Rowsets>
<Alert>
<element>
<Title>Downtime:DIA01</Title>
<eventDesc>WorkcenterDown</eventDesc>
</element>
<element>
<Title>Downtime:DIA01</Title>
<eventDesc>WorkcenterDown</eventDesc>
</element>
</Alert>
<Incident>
<element>
<Title>YAT < 60%:DIA01</Title>
<eventDesc>7 Parts in 60 minutes have failed</eventDesc>
</element>
<element>
<Title>YAT < 60%:DIA01</Title>
<eventDesc>7 Parts in 60 minutes have failed</eventDesc>
</element>
</Incident>
</Rowsets>
Based on eventType, I want to generate group.
I am using this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<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:template match="/">
<Rowsets>
<Rowset>
<xsl:variable name="Type" select="'alert'"/>
<xsl:for-each select="/Rowsets/Rowset/Row">
<xsl:choose>
<xsl:when test="$Type = eventType">
<element>
<xsl:variable name="text" select="eventText"/>
<xsl:variable name="WC" select="workCenter"/>
<Title><xsl:value-of select="concat($text,':',$WC)" /></Title>
<xsl:copy-of select="eventDesc"/>
</element>
</xsl:when>
<xsl:otherwise>
<element>
<xsl:variable name="text" select="eventText"/>
<xsl:variable name="WC" select="workCenter"/>
<Title><xsl:value-of select="concat($text,':',$WC)" /></Title>
<xsl:copy-of select="eventDesc"/>
</element>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</Rowset>
</Rowsets>
</xsl:template>
</xsl:stylesheet>
Need help in generating id and key based on eventType
This is a grouping problem - and a rather trivial one at that. In XSLT 2.0 you could do simply:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="Rowsets">
<Rowsets>
<xsl:for-each-group select="Row" group-by="eventType">
<xsl:element name="{current-grouping-key()}">
<xsl:for-each select="current-group()">
<element>
<Title>
<xsl:value-of select="eventText, workCenter" separator=":"/>
</Title>
<xsl:copy-of select="eventDesc"/>
</element>
</xsl:for-each>
</xsl:element>
</xsl:for-each-group>
</Rowsets>
</xsl:template>
</xsl:stylesheet>
Alternatively, with only two possible types, you could do:
XSLT 1.0
<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:template match="Rowsets">
<Rowsets>
<Alert>
<xsl:apply-templates select="Row[eventType='alert']"/>
</Alert>
<Incident>
<xsl:apply-templates select="Row[eventType='incident']"/>
</Incident>
</Rowsets>
</xsl:template>
<xsl:template match="Row">
<element>
<Title>
<xsl:value-of select="eventText"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="workCenter"/>
</Title>
<xsl:copy-of select="eventDesc"/>
</element>
</xsl:template>
</xsl:stylesheet>
This is assuming you don't mind creating a group even if it is empty. Otherwise you would do:
<xsl:template match="Rowsets">
<Rowsets>
<xsl:variable name="alerts" select="Row[eventType='alert']"/>
<xsl:variable name="incidents" select="Row[eventType='incident']"/>
<xsl:if test="$alerts">
<Alert>
<xsl:apply-templates select="Row[eventType='alert']"/>
</Alert>
</xsl:if>
<xsl:if test="$incidents">
<Incident>
<xsl:apply-templates select="Row[eventType='incident']"/>
</Incident>
</xsl:if>
</Rowsets>
</xsl:template>
P.S. Note that XML is case-sensitive: <Alert> is not the same as <alert>.

Grouping dynamically

I'm trying to group the data together by summing a node adjacently. Example
<root>
<row id="AAA" val="2"/>
<row id="BBB" val="3"/>
<row id="CCC" val="1"/>
<row id="DDD" val="4"/>
<row id="EEE" val="6"/>
<row id="FFF" val="3"/>
<row id="GGG" val="6"/>
<row id="HHH" val="8"/>
<row id="III" val="3"/>
<row id="JJJ" val="4"/>
<row id="KKK" val="2"/>
<row id="LLL" val="1"/>
</root>
Let's say I have a parameter of 10 then, then every time the values sum to 10 or less than 10, they should be grouped together.
And the result should be
<root>
<grouped>
<row id="AAA" val="2"/>
<row id="BBB" val="3"/>
<row id="CCC" val="1"/>
<row id="DDD" val="4"/>
</grouped>
<grouped>
<row id="EEE" val="6"/>
<row id="FFF" val="3"/>
</grouped>
<grouped>
<row id="GGG" val="6"/>
</grouped>
<grouped>
<row id="HHH" val="8"/>
</grouped>
<grouped>
<row id="III" val="3"/>
<row id="JJJ" val="4"/>
<row id="KKK" val="2"/>
<row id="LLL" val="1"/>
</grouped>
</root>
I tried with group-adjacent with sum(current/#val + following-sibling::row/#val le 10) then tried group-by(sum(#val)) but I can see my basic approach is incorrect. Now I'm wondering, is this even possible. So I thought I'd ask the experts!
Thanks!
In XSLT 1 you could use sibling recursion, in XSLT 3 it is easier but a bit verbose to use xsl:iterate:
<xsl:template match="root">
<xsl:copy>
<xsl:iterate select="row">
<xsl:param name="sum" as="xs:integer" select="0"/>
<xsl:param name="group" as="element(row)*" select="()"/>
<xsl:on-completion>
<xsl:if test="$group">
<group>
<xsl:copy-of select="$group"/>
</group>
</xsl:if>
</xsl:on-completion>
<xsl:variable name="current-sum" select="$sum + xs:integer(#val)"/>
<xsl:if test="$current-sum > 10">
<group>
<xsl:copy-of select="$group"/>
</group>
</xsl:if>
<xsl:next-iteration>
<xsl:with-param name="sum" select="if ($current-sum > 10) then xs:integer(#val) else $current-sum"/>
<xsl:with-param name="group" select="if ($current-sum > 10) then . else ($group, .)"/>
</xsl:next-iteration>
</xsl:iterate>
</xsl:copy>
</xsl:template>
https://xsltfiddle.liberty-development.net/6pS2B6o
As an alternative, you could use an accumulator that sums the #val values and "remembers" when a "group" has been established, then in the grouping you can use group-starting-with to check the accumulator:
<xsl:param name="max" as="xs:integer" select="10"/>
<xsl:mode on-no-match="shallow-copy" use-accumulators="#all"/>
<xsl:output method="xml" indent="yes"/>
<xsl:accumulator name="window" as="item()*" initial-value="()">
<xsl:accumulator-rule match="root" select="(0, true())"/>
<xsl:accumulator-rule match="root/row"
select="let $val := xs:integer(#val),
$sum := $value[1],
$window-start := $value[2],
$current-sum := $sum + $val
return
if ($current-sum gt $max)
then ($val, true())
else ($current-sum, false())"/>
</xsl:accumulator>
<xsl:template match="root">
<xsl:copy>
<xsl:for-each-group select="row" group-starting-with="*[accumulator-before('window')[2]]">
<grouped>
<xsl:apply-templates select="current-group()"/>
</grouped>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
https://xsltfiddle.liberty-development.net/6pS2B6o/1
You can even make that streamable (with the help of Michael Kay):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all" version="3.0">
<xsl:param name="max" as="xs:integer" select="10"/>
<xsl:mode on-no-match="shallow-copy" use-accumulators="#all" streamable="yes"/>
<xsl:output method="xml" indent="yes"/>
<xsl:accumulator name="window" as="item()*" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="root" select="(0, true())"/>
<xsl:accumulator-rule match="root/row"
select="
let $val := xs:integer(#val),
$sum := $value[1],
$window-start := $value[2],
$current-sum := $sum + $val
return
if ($current-sum gt $max)
then
($val, true())
else
($current-sum, false())"
/>
</xsl:accumulator>
<xsl:template match="root">
<xsl:copy>
<xsl:for-each-group select="row"
group-starting-with="*[boolean(accumulator-before('window')[2])]">
<grouped>
<xsl:apply-templates select="current-group()"/>
</grouped>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The xsl:for-each-group instruction can't handle this requirement.
As well as Martin's suggestions, another 3.0 approach is fold-left:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="max" as="xs:integer" select="10"/>
<xsl:template match="root">
<xsl:copy>
<xsl:variable name="groups" select="
fold-left(row, ([]), function($groups, $next) {
if (sum(head($groups)?*/#val) + $next/#val le $max)
then (array:append(head($groups), $next), tail($groups))
else ([$next], $groups)
}) => reverse()"/>
<xsl:for-each select="$groups">
<grouped>
<xsl:copy-of select="?*"/>
</grouped>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This builds the groups as a sequence of arrays, one array per group, initially in reverse order: the callback function is executed once for each row, and it adds the row to the first (i.e. last) group if the total is within your threshold, otherwise it starts a new group.
(Why in reverse order? Largely because head() and tail() are convenient, and there's no equivalent for getting the last item and "all except the last").

XSLT Sort by Date Not Working

I'm trying to get the most recent tax data for a locality. I'm using the following input and xslt transformation:
<?xml version='1.0' encoding='UTF-8'?>
<root>
<row>
<Applies_To>Abbott Twp (Pennsylvania:Potter)</Applies_To>
<Tax_Code>42530035</Tax_Code>
<Tax>Local City Withholding (Resident)</Tax>
<Start_Date>2011-07-01</Start_Date>
<Tax_Rate>0.005</Tax_Rate>
</row>
<row>
<Applies_To>Abbott Twp (Pennsylvania:Potter)</Applies_To>
<Tax_Code>42530035</Tax_Code>
<Tax>Local City Withholding (Resident)</Tax>
<Start_Date>2015-01-01</Start_Date>
<Tax_Rate>0.099</Tax_Rate>
</row>
<row>
<Applies_To>Abbott Twp (Pennsylvania:Potter)</Applies_To>
<Tax_Code>42530035</Tax_Code>
<Tax>Local City Withholding (Work)</Tax>
<Start_Date>2011-07-01</Start_Date>
<Tax_Rate>0</Tax_Rate>
</row>
<row>
<Applies_To>Abbottstown Boro (Pennsylvania:Adams)</Applies_To>
<Tax_Code>42010033</Tax_Code>
<Tax>Local City Withholding (Resident)</Tax>
<Start_Date>2011-07-01</Start_Date>
<Tax_Rate>0.005</Tax_Rate>
</row>
<row>
<Applies_To>Abbottstown Boro (Pennsylvania:Adams)</Applies_To>
<Tax_Code>42010033</Tax_Code>
<Tax>Local City Withholding (Work)</Tax>
<Start_Date>2012-07-01</Start_Date>
<Tax_Rate>0.01</Tax_Rate>
</row>
<row>
<Applies_To>Abbottstown Boro (Pennsylvania:Adams)</Applies_To>
<Tax_Code>42010033</Tax_Code>
<Tax>xxxx</Tax>
<Start_Date>2012-07-01</Start_Date>
<Tax_Rate>0.01</Tax_Rate>
</row>
</root>
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="/">
<root>
<xsl:for-each-group select="*/row"
group-by="Tax_Code">
<xsl:sort order="descending" select="Start_Date"/>
<row>
<Tax_Code>
<xsl:attribute name="Code" select="current-grouping-key()"/>
<xsl:for-each-group select="current-group()" group-by="Tax">
<xsl:call-template name="Tax_Code"/>
</xsl:for-each-group>
</Tax_Code>
</row>
</xsl:for-each-group>
</root>
</xsl:template>
<xsl:template name="Tax_Code">
<Tax_Rate>
<xsl:attribute name="taxtype">
<xsl:value-of select="Tax"/>
</xsl:attribute>
<xsl:value-of select="Tax_Rate"/>
</Tax_Rate>
<date>
<xsl:value-of select="Start_Date"></xsl:value-of>
</date>
</xsl:template>
</xsl:stylesheet>
I then get the following output:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xs="http://www.w3.org/2001/XMLSchema">
<row>
<Tax_Code Code="42530035">
<Tax_Rate taxtype="Local City Withholding (Resident)">0.005</Tax_Rate>
<date>2011-07-01</date>
<Tax_Rate taxtype="Local City Withholding (Work)">0</Tax_Rate>
<date>2011-07-01</date>
</Tax_Code>
</row>
<row>
<Tax_Code Code="42010033">
<Tax_Rate taxtype="Local City Withholding (Resident)">0.005</Tax_Rate>
<date>2011-07-01</date>
<Tax_Rate taxtype="Local City Withholding (Work)">0.01</Tax_Rate>
<date>2012-07-01</date>
<Tax_Rate taxtype="xxxx">0.01</Tax_Rate>
<date>2012-07-01</date>
</Tax_Code>
</row>
</root>
As you can see, the output doesn't have the most recent tax rate for tax code 42530035. Even if I try to add position() = 1, I still don't get the most entry. I can't find an explanation of why the sorting doesn't work.
The items in your inner-most xsl:for-each-group are returned in document order. If you want to process the first sorted item, you could use an xsl:for-each and xsl:sort the current-group() items, and then only process the first one:
<xsl:for-each-group select="current-group()" group-by="Tax">
<xsl:for-each select="current-group()">
<xsl:sort order="descending" select="Start_Date" />
<xsl:if test="position() = 1">
<xsl:apply-templates select="." mode="Tax_Code"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each-group>
Complete stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="/">
<root>
<xsl:for-each-group select="*/row"
group-by="Tax_Code">
<xsl:sort order="descending" select="Start_Date"/>
<row>
<Tax_Code>
<xsl:attribute name="Code" select="current-grouping-key()"/>
<xsl:for-each-group select="current-group()" group-by="Tax">
<xsl:for-each select="current-group()">
<xsl:sort order="descending" select="Start_Date" />
<xsl:if test="position() = 1">
<xsl:apply-templates select="." mode="Tax_Code"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each-group>
</Tax_Code>
</row>
</xsl:for-each-group>
</root>
</xsl:template>
<xsl:template name="Tax_Code" match="row" mode="Tax_Code">
<Tax_Rate>
<xsl:attribute name="taxtype">
<xsl:value-of select="Tax"/>
</xsl:attribute>
<xsl:value-of select="Tax_Rate"/>
</Tax_Rate>
<date>
<xsl:value-of select="Start_Date"/>
</date>
</xsl:template>
</xsl:stylesheet>
Use
<date>
<xsl:value-of select="max(current-group()/xs:date(Start_Date))"></xsl:value-of>
</date>
to output the highest date in the group you have. The ordering with the xsl:sort you have only applied in the outer grouping and only for the groups, in the inner group you currently output the date of the first item in the group (in the original document order).

limit records display using xslt

I have a problem. I get the data from xml then transform it with xslt.
Let us say I have a xml file:
<?xml version="1.0"?>
<root>
<row id="1" fname="Dan" lname="Wahlin">
<address type="home">
<street>1234 Anywhere St.</street>
<city>AnyTown</city>
<zip>85789</zip>
</address>
<address type="business">
<street>1234 LottaWork Ave.</street>
<city>AnyTown</city>
<zip>85786</zip>
</address>
</row>
<row id="2" fname="Elaine" lname="Wahlin">
<address type="home">
<street>1234 Anywhere St.</street>
<city>AnyTown</city>
<zip>85789</zip>
</address>
<address type="business">
<street>1233 Books Way</street>
<city>AnyTown</city>
<zip>85784</zip>
</address>
</row>
</root>
And this stylesheet:
<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes" encoding="utf-8" omit-xml-declaration="no"/>
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="row">
<row>
<xsl:attribute name="id">
<xsl:value-of select="id"/>
</xsl:attribute>
<xsl:attribute name="fname">
<xsl:value-of select="name/fname"/>
</xsl:attribute>
<xsl:attribute name="lname">
<xsl:value-of select="name/lname"/>
</xsl:attribute>
<xsl:for-each select="address">
<xsl:copy-of select="."/>
</xsl:for-each> </row>
</xsl:template>
</xsl:stylesheet
How can limit this to 3 records, then after 3 records it create a tr tag?
For example:
<table>
<tr>
<td>Address1</td>
<td>Address2</td>
<td>Address3</td>
</tr>
<tr>
<td>Address4</td>
<td>Address5</td>
<td>Address6</td>
</tr>
</table
try something like
<xsl:for-each select="PATH">
<xsl:variable name="pos" select="position() mod 3" />
</xsl:for-each>
then you can work with
<xsl:if test="$pos = 0">
</xsl:if>
and
<xsl:if test="$pos != 0">
</xsl:if>
if $pos = 0 means that you reached the 3rd row
Here are some good resources to learn more about XSLT and XPath
http://w3schools.com/xsl/default.asp
http://w3schools.com/xpath/default.asp