How to use summation in XSLT - xslt

I want to get the summation in XSLT
I used the following code to perform it.
<xsl:if test="table/tr/td[preceding::td[starts-with(text(),'MKTDATQ - NZX Equity Price Summary')]][following::td[starts-with(text(),'MKTDATQ - NZX Debt Price Summary')]]">
<xsl:variable name="summ2" select="sum(table/tr/td[preceding::td[starts-with(text(),'MKTDATQ - NZX Equity Price Summary')]][following::td[starts-with(text(),'MKTDATQ - NZX Debt Price Summary')]][12][text() != ' 0'][text() != ' '][text()[not(starts-with(.,'-'))]][text()[not(starts-with(.,'Value'))]])"/>
<fieldSet name="NZXVolumeTraded" value="{concat($summ2,'m')}"/>
</xsl:if>
This works fine. But the problem is I am getting 3.2496176E7m as the result of summation. The correct answer must be 32.496m.
What is the mistake that I have done in my code and how can I fix that? I am using XSLT 2.0

I think the issue is somewhere on your text() filters, lets try to sum() up numeric values only using translate()
<xsl:if test="table/tr/td[preceding::td[starts-with(text(),'MKTDATQ - NZX Equity Price Summary')]][following::td[starts-with(text(),'MKTDATQ - NZX Debt Price Summary')]]">
<xsl:variable name="summ2"
select="sum(table/tr/td[preceding::td[starts-with(text()
,'MKTDATQ - NZX Equity Price Summary')]]
[following::td[starts-with(text(),'MKTDATQ - NZX Debt Price Summary')]][12]/
translate(text(), translate(text(), '0123456789.', ''), ''))"/>
<fieldSet name="NZXVolumeTraded" value="{concat($summ2,'m')}"/>
</xsl:if>

Related

How to hide sensitive data using xslt

I have a requirement to modify sensitive data using xslt before entering data into database
For example -
If account number is 12345678 then it should go to database as ****5678. I have to make this changes in xslt.
As i am new to xslt i am not able to crack this.
Could you please help me out with this ??
Try something like:
<xsl:text>****</xsl:text>
<xsl:value-of select="substring(accountnumber, string-length(accountnumber) - 3)" />
No context was provided, so you will need to make the necessary adjustments yourself.
Added:
But what if my account number is not fixed value. For example - if account number is 123456 then it should go as **3456
I would be reluctant to provide a potential attacker information about the length of the masked portion. But if you are willing to take the risk, you can use:
<xsl:variable name="len" select="string-length(accountnumber)" />
<xsl:value-of select="translate(substring(accountnumber, 1, $len - 4), '0123456789', '**********')" />
<xsl:value-of select="substring(accountnumber, $len - 3)" />

XSLT: how to sum a nodeset in a selected node

I was blocked and hope someone could guide me ~
My XML is:
<PaymentMethodData>
<PaymentMethodDetails PayMethodName="ZHRX_USP_SOE_OPM_NACHA" PayRelShipNo="955160008183756">
<PayMethodName>ZHRX_USP_SOE_OPM_NACHA</PayMethodName>
<PayRelShipNo>955160008183756</PayRelShipNo>
<PaymentPercentage>50</PaymentPercentage>
<RemainingAmountFlag>N</RemainingAmountFlag>
<BankAccountDetails AccountName="PERSON ZHRX_US_CO_01">
<AccountName>PERSON ZHRX_US_CO_01</AccountName>
<AccountNumber>11111111</AccountNumber>
</BankAccountDetails>
</PaymentMethodDetails>
<PaymentMethodDetails PayMethodName="ZHRX_USP_SOE_OPM_NACHA" PayRelShipNo="955160008183756">
<PayMethodName>ZHRX_USP_SOE_OPM_NACHA</PayMethodName>
<PayRelShipNo>955160008183756</PayRelShipNo>
<PaymentPercentage>40</PaymentPercentage>
<RemainingAmountFlag>N</RemainingAmountFlag>
<BankAccountDetails AccountName="PERSON ZHRX_US_CO_01">
<AccountName>PERSON ZHRX_US_CO_01</AccountName>
<AccountNumber>22222222</AccountNumber>
</BankAccountDetails>
</PaymentMethodDetails>
<PaymentMethodDetails PayMethodName="ZHRX_USP_SOE_OPM_NACHA" PayRelShipNo="955160008183756">
<PayMethodName>ZHRX_USP_SOE_OPM_NACHA</PayMethodName>
<PayRelShipNo>955160008183756</PayRelShipNo>
<BankAccountDetails AccountName="PERSON ZHRX_US_CO_01">
<AccountName>PERSON ZHRX_US_CO_01</AccountName>
<AccountNumber>33333333</AccountNumber>
</BankAccountDetails>
</PaymentMethodDetails>
</PaymentMethodData>
The meaning is, the person "955160008183756" has three bank accounts, 50% of his payment will be made into his 1st bank account 11111111; 40% of his payment will be made into his 2nd bank account 22222222; thus the remaining amount will be made into his 3rd back account. The number of banks is dynamic.
I need to write the XSL to calculate the "remaining percentage" for the 3rd bank. I tried below XSL but it returns 100 because "sum(PaymentPercentage)" returned "false".
Pls kindly help, thank you very much!
<Percentage_Bank>
<xsl:choose>
<xsl:when test="RemainingAmountFlag = 'N'" >
<xsl:value-of select="PaymentPercentage" >
</xsl:when>
<xsl:otherwise >
<xsl:value-of select="100-sum(PaymentPercentage)"/>
</xsl:otherwise>
</xsl:choose>
</Percentage_Bank>
Regards,
Paula
I am assuming your XSLT snippet is in a template matching PaymentMethodDetails. If so, doing <xsl:value-of select="100-sum(PaymentPercentage)"/> will be looking for a PaymentPercentage under this the current node, but such a node does not exist. You need to sum up the PaymentPercentage nodes for all the other PaymentMethodDetails records.
To do this, consider defining a key like so....
<xsl:key name="payments" match="PaymentMethodDetails" use="PayRelShipNo" />
Then, to get the remaining percentage you can do this...
<xsl:value-of select="100-sum(key('payments', PayRelShipNo)/PaymentPercentage)"/>
Note I assumed your XML might have more than one account number in.
See an example of it in action at http://xsltfiddle.liberty-development.net/6qM2e2f/1

Select substrings dependent on context

I have this piece of XML and want to get the number immediately after Chapter
<para>Insolvency Rules, r 12.12, which gives the court a broad discretion, unfettered by the English equivalent of the heads of Order 11, r 1(1) (which are now to be found, in England, in CPR, Chapter 6, disapplied in the insolvency context by Insolvency Rules, r 12.12(1)). </para>
When I used this XSLT transform
<xsl:value-of select="translate(substring-after(current(),'Chapter'), translate(substring-after(current(),'Chapter'),'0123456789',''), '')"/>
I get this output
612121
Butut I want just 6.
Please let me know how I should do it.
I don't want to use a statement like
<xsl:value-of select="substring-before(substring-after(current(),'Chapter'), ,',')"/>
as the chapter number will be different in each instance, between 1 and 15.
Try this:
<xsl:variable name="vS" select="concat(substring-after(current(),'Chapter '),'Z')"/>
<xsl:value-of select=
"substring-before(translate($vS,translate($vS,'0123456789',''),'Z'),'Z')"/>
This is based on: https://stackoverflow.com/a/4188249/2115381 Thanks to
#Dimitre Novatchev
Update: If the quantity of space after the "Chapter" is not known you can use something like this:
<xsl:variable name="vS" select="concat(substring-after(current(),'Chapter'),'Z')"/>
<xsl:value-of select=
" translate(
substring-before(translate($vS,translate($vS,' 0123456789',''),'Z'),'Z')
, ' ','')"/>

date minus another date in xslt

Hope someone can help. I am trying to compare 2 dates inside an XML file and using a XSLT to do some calculation:
For example I have 2 dates in XML: 2011-05-23 and 2011-04-29. I want to do calculation inside XSLT like below:
('2011-05-23'-'2011-04-29')*30 = 24*30= 720
Can anyone shed any light?
An XSLT 2.0 solution
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:template match="/">
<xsl:value-of select="days-from-duration(
xs:date('2011-05-23')
- xs:date(xs:date('2011-04-29'))
)*30"/>
</xsl:template>
</xsl:stylesheet>
Yields: 720
xs:date() function evaluates the dates, which can be used to perform date operations
subtracting the second date from the first yields the xdt:dayTimeDuration P24D (24 days)
days-from-duration() extracts the days component from the xdt:dayTimeDuration (24)
then you can use that number to perform normal arethmatic (e.g. 24*30=720)
It might be worth looking at the EXSLT - date:difference solution. I believe that should do what you want, and there's even a straight XSLT implementation available.
Be aware though that the returned value is in duration format as specified in the XML Schema Part 2: Datatypes Second Edition, so it's likely you will need to process the result to derive the unit you wish to use in calculation (for instance in your example above you're expecting a result detailing the number of days difference - so you would need to pull out the relevant unit you want to work with, the result from date:difference in that case would most likely be "P24D").
Here's two templates I sometimes use for date calculations:
<xsl:template name="calcseconds">
<xsl:param name="date" />
<xsl:value-of select="(((substring($date,1,4) - 1970) * 365)+floor((substring($date,1,4) - 1970) div 4)+substring('000,031,059,090,120,151,181,212,243,273,304,334,365',substring($date,6,2)*4-3,3)+(substring($date,9,2)-1)+(1-floor(((substring($date,1,4) mod 4) + 2) div 3))*floor((substring($date,6,2)+17) div 20))*86400+(substring($date,12,2)*3600)+(substring($date,15,2)*60)+substring($date,18,2)" />
</xsl:template>
<xsl:template name="calcdays">
<xsl:param name="date" />
<xsl:value-of select="(((substring($date,1,4) - 1970) * 365)+floor((substring($date,1,4) - 1970) div 4)+substring('000,031,059,090,120,151,181,212,243,273,304,334,365',substring($date,6,2)*4-3,3)+(substring($date,9,2)-1)+(1-floor(((substring($date,1,4) mod 4) + 2) div 3))*floor((substring($date,6,2)+17) div 20))" />
</xsl:template>
They're a bit of a mouthful, but they'll calculate the number of seconds/days since midnight 1st January 1970 as an integer, which you can then do straight arithmetic on. They rely on the date format being yyyy-mm-dd hh:mm:ss, but manipulation of the parameters of the substring calls should allow you to process dates in any format you need.

XSLT line counter - is it that hard?

I have cheated every time I've needed to do a line count in XSLT by using JScript, but in this case I can't do that. I simply want to write out a line counter throughout an output file. This basic example has a simple solution:
<xsl:for-each select="Records/Record">
<xsl:value-of select="position()"/>
</xsl:for-each>
Output would be:
1
2
3
4
etc...
But what if the structure is more complex with nested foreach's :
<xsl:for-each select="Records/Record">
<xsl:value-of select="position()"/>
<xsl:for-each select="Records/Record">
<xsl:value-of select="position()"/>
</xsl:for-each>
</xsl:for-each>
Here, the inner foreach would just reset the counter (so you get 1, 1, 2, 3, 2, 1, 2, 3, 1, 2 etc). Does anyone know how I can output the position in the file (ie. a line count)?
While it is quite impossible to mark the line numbers for the serialization of an XML document (because this serialization per se is ambiguous), it is perfectly possible, and easy, to number the lines of regular text.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:call-template name="numberLines"/>
</xsl:template>
<xsl:template name="numberLines">
<xsl:param name="pLastLineNum" select="0"/>
<xsl:param name="pText" select="."/>
<xsl:if test="string-length($pText)">
<xsl:value-of select="concat($pLastLineNum+1, ' ')"/>
<xsl:value-of select="substring-before($pText, '
')"/>
<xsl:text>
</xsl:text>
<xsl:call-template name="numberLines">
<xsl:with-param name="pLastLineNum"
select="$pLastLineNum+1"/>
<xsl:with-param name="pText"
select="substring-after($pText, '
')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>The biggest airlines are imposing "peak travel surcharges"
this summer. In other words, they're going to raise fees
without admitting they're raising fees: Hey, it's not a $30
price hike. It's a surcharge! This comes on the heels of
checked-baggage fees, blanket fees, extra fees for window
and aisle seats, and "snack packs" priced at exorbitant
markups. Hotels in Las Vegas and elsewhere, meanwhile, are
imposing "resort fees" for the use of facilities (in other
words, raising room rates without admitting they're
raising room rates). The chiseling dishonesty of these
tactics rankles, and every one feels like another nail in
the coffin of travel as something liberating and
pleasurable.
</t>
produces the desired line-numbering:
1 The biggest airlines are imposing "peak travel surcharges"
2 this summer. In other words, they're going to raise fees
3 without admitting they're raising fees: Hey, it's not a $30
4 price hike. It's a surcharge! This comes on the heels of
5 checked-baggage fees, blanket fees, extra fees for window
6 and aisle seats, and "snack packs" priced at exorbitant
7 markups. Hotels in Las Vegas and elsewhere, meanwhile, are
8 imposing "resort fees" for the use of facilities (in other
9 words, raising room rates without admitting they're
10 raising room rates). The chiseling dishonesty of these
11 tactics rankles, and every one feels like another nail in
12 the coffin of travel as something liberating and
13 pleasurable.
A line in an XML file is not really the same as an element. In your first example you don't really count the lines - but the number of elements.
An XML file could look like this:
<cheeseCollection>
<cheese country="Cyprus">Gbejna</cheese><cheese>Liptauer</cheese><cheese>Anari</cheese>
</cheeseCollection>
Or the exact same XML file can look like this:
<cheeseCollection>
<cheese
country="Cyprus">Gbejna</cheese>
<cheese>Liptauer</cheese>
<cheese>Anari</cheese>
</cheeseCollection>
which the XSLT will interpet exactly the same - it will not really bother with the line breaks.
Therefore it's hard to show line numbers in the way you want using XSLT - it's not really meant for for that kind of parsing.
Someone correct me if I'm wrong, but I'd say you would need Javascript or some other scripting language to do what you want.
Thanks for the responses guys - yup you're totally correct, some external function is the only way to get this behaviour in XSLT. For those searching, this is how I did this when using a compiled transform in .Net 3.5:
Create a helper class for your function(s)
/// <summary>
/// Provides functional support to XSLT
/// </summary>
public class XslHelper
{
/// <summary>
/// Initialise the line counter value to 1
/// </summary>
Int32 counter = 1;
/// <summary>
/// Increment and return the line count
/// </summary>
/// <returns></returns>
public Int32 IncrementCount()
{
return counter++;
}
}
Add an instance to an args list for XSLT
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(XmlReader.Create(s));
XsltArgumentList xslArg = new XsltArgumentList();
XslHelper helper = new XslHelper();
xslArg.AddExtensionObject("urn:helper", helper);
xslt.Transform(xd.CreateReader(), xslArg, writer);
Use it in you XSLT
Put this in the stylesheet declaration element:
xmlns:helper="urn:helper"
Then use like so:
<xsl:value-of select="helper:IncrementCount()" />
Generally, position() is referring to the number of the current node relative to the entire batch of nodes that is being processed currently.
With your "nested for-each" example, consecutive numbering can easily be achieved when you stop nesting for-each constructs and just select all desired elements at once.
With this XML:
<a><b><c/><c/></b><b><c/></b></a>
a loop construct like this
<xsl:for-each "a/b">
<xsl:value-of select="position()" />
<xsl:for-each "c">
<xsl:value-of select="position()" />
</xsl:for-each>
</xsl:for-each>
will result in
11221
bccbc // referred-to nodes
but you could simply do this instead:
<xsl:for-each "a/b/c">
<xsl:value-of select="position()" />
</xsl:for-each>
and you would get
123
ccc // referred-to nodes