XSLT group by and then copy child node by "n" number of times - group-by

I need to group the company and the maximum number the node row can occur in the new node group is 3.
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:decimal-format name="coerce" NaN="0"/>
<xsl:template match="/root">
<root>
<!--Group by fields, can only group by 1 element each time-->
<xsl:for-each-group select="row" group-by="Company">
<xsl:for-each-group select="current-group()" group-by="Department">
<xsl:for-each select="current-group()">
<!--Create a variable for the row count-->
<xsl:variable name="Grpcounter" select="number(position())"/>
<xsl:if test="$Grpcounter < 3">
<xsl:call-template name="Group">
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</xsl:for-each-group>
</xsl:for-each-group>
</root>
</xsl:template>
<xsl:template name="Group">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>

could you pls update your xslt as follows:
replace: <xsl:template match="/root"> . . . </xsl:template> with
<xsl:template match="/root">
<xsl:variable name="grouped">
<!--Group by fields, can only group by 1 element each time-->
<xsl:for-each-group select="row" group-by="Company">
<!--<xsl:for-each-group select="current-group()" group-by="Department">-->
<xsl:for-each select="current-group()">
<xsl:choose>
<xsl:when test="position() mod 3 = 1">
<Group>
<xsl:call-template name="Group"/>
</Group>
</xsl:when>
<xsl:otherwise>
<Group1>
<xsl:call-template name="Group"/>
</Group1>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<!--</xsl:for-each-group>-->
</xsl:for-each-group>
</xsl:variable>
<root>
<xsl:for-each-group select="$grouped/*" group-starting-with="Group">
<Group>
<xsl:copy-of select="current-group()/row"/>
</Group>
</xsl:for-each-group>
</root>
</xsl:template>
to get your expected output. See result

Related

How to get the weekday from a date with XSLT? [duplicate]

Trying to calculate day of the week number from date. Found some examples in the internet, but instead of day of the week number i see this : NaN.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="date">
<xsl:copy>
<xsl:call-template name="date-format">
<xsl:with-param name="date-time" select="."/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template name="date-format">
<xsl:param name="date-time"/>
<xsl:param name="date" select="substring-before($date-time,'T')"/>
<xsl:param name="year" select="substring-before($date,'-')"/>
<xsl:param name="month"
select="substring-before(substring-after($date,'-'),'-')"/>
<xsl:param name="day" select="substring-after(substring-after($date,'-'),'-')"/>
<xsl:variable name="a" select="floor((14 - $month) div 12)"/>
<xsl:variable name="y" select="$year - $a"/>
<xsl:variable name="m" select="$month + 12 * $a - 2"/>
<xsl:value-of select="($day + $y + floor($y div 4) - floor($y div 100)
+ floor($y div 400) + floor((31 * $m) div 12)) mod 7"/>
</xsl:template>
</xsl:stylesheet>
.
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="test2.xsl" type="text/xsl" ?>
<document>
<date>2013-01-01</date>
<date>2013-05-24</date>
<date>2013-12-25</date>
<date>1957-07-13</date>
<date>1776-07-04</date>
</document>
The following stylesheet:
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="date">
<xsl:call-template name="day-of-week">
<xsl:with-param name="date" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="day-of-week">
<xsl:param name="date"/>
<xsl:variable name="year" select="substring($date,1, 4)"/>
<xsl:variable name="month" select="substring($date, 6, 2)"/>
<xsl:variable name="day" select="substring($date, 9, 2)"/>
<xsl:variable name="a" select="floor((14 - $month) div 12)"/>
<xsl:variable name="y" select="$year + 4800 - $a"/>
<xsl:variable name="m" select="$month + 12*$a - 3"/>
<xsl:variable name="JDN" select="$day + floor((153*$m + 2) div 5) + 365*$y + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045" />
<xsl:value-of select="($JDN + 1) mod 7" />
</xsl:template>
</xsl:stylesheet>
when applied to your input example:
<document>
<date>2013-01-01</date>
<date>2013-05-24</date>
<date>2013-12-25</date>
<date>1957-07-13</date>
<date>1776-07-04</date>
</document>
will return the following result:
<?xml version="1.0" encoding="UTF-8"?>
25364
Note:
The main problem with your attempt is this:
<xsl:param name="date" select="substring-before($date-time,'T')"/>
Your input has dates, not date-times, and they do not contain "T".

Counter generation following a range in xslt

I am trying to generate a counter and I do it successfully using the position() function in xslt.
How can I create a counter within a range -1001 - 9999 such that the first counter is 1001 in place of 1.
in put xml
<ROOT>
<Row>
<Var>11</Var>
</Row>
<Row>
<Var>11</Var>
</Row>
<Row>
<Var>3</Var>
</Row>
<Row>
<Var>43</Var>
</Row>
<Row>
<Var>51</Var>
</Row>
and the desired o/p is
<ROOT>
<Row>
<Var>11</Var>
<seq>1001</seq>
</Row>
<Row>
<Var>11</Var>
<seq>1002</seq>
</Row>
<Row>
<Var>3</Var>
<seq>1003</seq>
</Row>
<Row>
<Var>43</Var>
<seq>1004</seq>
</Row>
<Row>
<Var>51</Var>
<seq>1005</seq>
</Row>
..
Thanks,
Vicky
I achieved the req as below:
<xsl:template match="/">
<ROOT>
<xsl:apply-templates />
</ROOT>
</xsl:template>
<xsl:template match="ROOT/Row">
<Row>
<Var>
<xsl:value-of select="Var"/>
</Var>
<seq>
<xsl:value-of select="1000 + position()" />
</seq>
</Row>
</xsl:template>
Well, XPath 2 and later literally allows you to write 1000 to 9999 to create a sequence of those numbers. I am not sure how that fits into your problem as you haven't provided any context like input you have and output you want. In XSLT 3 (not 2) the xsl:number element often used to count or number things also has a start-at attribute, see https://www.w3.org/TR/xslt-30/#element-number.
As you seem to want to number elements from the input I think the right approach is to use xsl:number so with XSLT 3 you can simply do:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output indent="yes"/>
<xsl:template match="Row">
<xsl:copy>
<xsl:apply-templates/>
<seq>
<xsl:number start-at="1001"/>
</seq>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
http://xsltfiddle.liberty-development.net/jyyiVhq
with XSLT 2 you can of course use xsl:number in a variable and then at 1000 e.g.
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0"
exclude-result-prefixes="xs">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:output indent="yes"/>
<xsl:template match="Row">
<xsl:copy>
<xsl:apply-templates/>
<seq>
<xsl:variable name="count" as="xs:integer"><xsl:number/></xsl:variable>
<xsl:value-of select="$count + 1000"/>
</seq>
</xsl:copy>
</xsl:template>
</xsl:transform>
http://xsltransform.hikmatu.com/jyyiVhn
I don't think it makes sense to use the to expression for your use case, it would rather be useful if you wanted to create a couple of new elements e.g.
<xsl:template match="body">
<xsl:copy>
<xsl:for-each select="1001 to 1100">
<seq>
<xsl:value-of select="."/>
</seq>
</xsl:for-each>
</xsl:copy>
</xsl:template>
Note that with XSLT 3.0 you can do
<seq>
<xsl:number start-at="1001" />
</seq>

applying class to element found with test

I'm still new to working with XSLT
How do I apply a class to an element found with an xsl:if
XML:
<ColumnBody><h3>Some Text</h3></ColumnBody>
The output that I want:
<h3 class="box-list-link">Some Text </h3>
I have something that does this:
<xsl:for-each select ="./ColumnBody" >
<xsl:call-template name="RichText"></xsl:call-template>
</xsl:for-each>
<xsl:template name="RichText">
<p>
<xsl:copy-of select="./node()" />
</p>
</xsl:template>
Would something like this work for you?
<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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="h3">
<h3 class="box-list-link">
<xsl:apply-templates/>
</h3>
</xsl:template>
</xsl:stylesheet>

How do we test whether a date is within 180 days from today in xslt in datapower

I am writing an xslt for datapower and in that I am getting a date(Payment Date).I have to check whether that date(Paymentt Date) is within 180 days of current date
I am getting present date by the following
<xsl:variable name="timestamp" select="date:date-time()"/>
Now how to check the condition for 180 days
Following is my xslt
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dp="http://www.datapower.com/extensions"
xmlns:date="http://exslt.org/dates-and-times"
xmlns:dpconfig="http://www.datapower.com/param/config"
extension-element-prefixes="dp"
exclude-result-prefixes="dp dpconfig"
>
<xsl:output method="xml"/>
<xsl:template match="/">
<PaymentDate><xsl:value-of select="dp:http-request-header('X-payment-date')"/></PaymentDate> (From request I am getting payment date)
<xsl:variable name="timestamp" select="date:date-time()"/> (From here I am getting present date)
<xsl:if (this is what I am confused)
Thanks
Calculating date difference in XSLT 1.0:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:date="http://exslt.org/dates-and-times"
extension-element-prefixes="date">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<output>
<difference>
<xsl:call-template name="date-difference">
<xsl:with-param name="date1" select="input/originalDate" />
<xsl:with-param name="date2" select="date:date-time()" />
</xsl:call-template>
</difference>
</output>
</xsl:template>
<xsl:template name="date-difference">
<xsl:param name="date1"/>
<xsl:param name="date2"/>
<xsl:param name="JDN1">
<xsl:call-template name="JDN">
<xsl:with-param name="date" select="$date1" />
</xsl:call-template>
</xsl:param>
<xsl:param name="JDN2">
<xsl:call-template name="JDN">
<xsl:with-param name="date" select="$date2" />
</xsl:call-template>
</xsl:param>
<xsl:value-of select="$JDN2 - $JDN1"/>
</xsl:template>
<xsl:template name="JDN">
<xsl:param name="date"/>
<xsl:param name="year" select="substring($date, 1, 4)"/>
<xsl:param name="month" select="substring($date, 6, 2)"/>
<xsl:param name="day" select="substring($date, 9, 2)"/>
<xsl:param name="a" select="floor((14 - $month) div 12)"/>
<xsl:param name="y" select="$year + 4800 - $a"/>
<xsl:param name="m" select="$month + 12*$a - 3"/>
<xsl:value-of select="$day + floor((153*$m + 2) div 5) + 365*$y + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045" />
</xsl:template>
</xsl:stylesheet>
When applied to the following input XML:
<input>
<originalDate>2013-09-15</originalDate>
</input>
The result will be:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<difference>179</difference>
</output>
if the transformation is performed today, 2014-03-13.
IBM Datapower provides extension to existing EXSLT function. There is a function called 'difference' with following signature.
difference()
Returns the duration between the first date and the second date.
Syntax
date:difference(start-dateTime, end-dateTime)
You can utilize this function to find out the date-difference. For more information about extension function available in datapower see below link. Go to Chapter 5 and look for functions for manipulating 'date time'.
http://pic.dhe.ibm.com/infocenter/wsdatap/v3r8m1/topic/xm70/ExtensionFunctions.pdf
-Ajitabh
DataPower Supports Date:Difference
difference()
Returns the duration between the first date and the second date.
Syntax
date:difference(start-dateTime, end-dateTime)
You can pass current date as end-DateTime , date which wants to check as start-DateTime
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs"
xmlns:date="http://exslt.org/dates-and-times" version="1.0">
<!--<xsl:key name="lang" match="element" use="#language"></xsl:key>-->
<xsl:template match="/">
<xsl:variable name="date1" select="date:date-time()"/>
<xsl:variable name="date2" select="'2013-09-15'"/>
<output>
<difference>
<xsl:value-of select="translate(date:difference($date2, $date1),'PD','')"/>
</difference>
</output>
</xsl:template>
</xsl:stylesheet>

xslt 2.0 compare current date to start and end date

I have the following output:
<forms>
<form id="15">
<start>2013-12-09</start>
<end>2014-01-05</end>
</form>
</forms>
I would like to test to see if current date is equal or greater to "start" and lesser or equal to "end"
How would I go about testing for this?
What I had in mind is:
<xsl:variable name="fstart">
<xsl:value-of select="start" />
</xsl:variable>
<xsl:variable name="fend">
<xsl:value-of select="end" />
</xsl:variable>
<xsl:choose>
<xsl:when test="$fstart >= currentdate xor currentdate <= $fend ">
<!-- do stuff -->
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
I believe it goes like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
...
<xsl:for-each select="forms/form">
<xsl:choose>
<xsl:when test="xs:date(start) <= current-date()
and
current-date() <= xs:date(end)">
<!-- in progress -->
</xsl:when>
<xsl:otherwise>
<!-- not started yet or already ended-->
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>