Convert scientific notation into double with XSLT(FO) - xslt

the XML i would like to transform contains something like 7.3248378E7, which is not supported natively by the XSL-FO processor (ends up in NaN in the PDF file). Is there a nice way (which is the opposite of a dirty hack) to convert this number format?
Thanks a lot.

I found this post: XSL Transform for converting from scientific notation to decimal number that provides an XSLT 1.0 stylesheet.
After including/importing the stylesheet, call the convertSciToNumString template to convert:
<xsl:call-template name="convertSciToNumString">
<xsl:with-param name="myval" select="'7.3248378E7'"/>
</xsl:call-template>
Produces: 73248378
This can be evaluated as a number and should get around your NaN issue:
<xsl:variable name="num">
<xsl:call-template name="convertSciToNumString">
<xsl:with-param name="myval" select="'7.3248378E7'"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$num"/> + 1 = <xsl:value-of select="$num + 1" />
Produces: 73248378 + 1 = 73248379

Is the item designated xs:float or xs:double, as opposed to xs:decimal? If that doesn't work, then I think you will have to go with the "dirty hack" approach. See "Requirement 29" in the XSLT page.

Today I had to solve a similar problem with scientific conversion. Finally I had to write my own XSLT conversion template, because original "convertSciToNumString" from Michael Case mentioned here in some other answer have some bugs.
Here is an article about this implementation and conversion tests.
http://www.orm-designer.com/article/xslt-convert-scientific-notation-to-decimal-number

Related

Date compare in XSLT

I want to compare two dates in XSLT (1.0). Here I have mentioned hard coded dates
<xsl:variable name="DATE1" select="ms:format-date(16-FEB-19, 'dd-MMM-yy')" />
<xsl:variable name="DATE2" select="ms:format-date(01-MAY-19, 'dd-MMM-yy')" />
<xsl:if test="$DATE1 $lt; $DATE2">
</xsl:if>
I tried above but not getting proper result.
It looks like you're using some kind of extension function ms:format-date to format the dates. If you can format them as pure numeric YYYYMMDD then you can compare them as numbers. XSLT 1.0 does not offer a "<" operator for strings, let alone for dates.
Do think about moving forward to a later XSLT version (available from third parties) rather than asking the StackOverflow community to help you use a 20-year old version of the language.

XPATH: How to use a variable in call-template

I am writing an XSLT to transform an XML: I am not experienced in Xpath, and my XML is too big to post.
But here is my problem:
This code: works fine:
<cac:testmathieu>
<xsl:variable name="lineAmount2" select="body:LineAmount * -1" />
<xsl:value-of select="../body:TaxTrans[body:TaxBaseAmount=$lineAmount2]/body:TaxAmount"/>
</cac:testmathieu>
Then, why is the code below not working, when it's used right below the above code:
<xsl:call-template name ="TaxTotalLine">
<xsl:with-param name="TaxAmount" select="../body:TaxTrans[body:TaxBaseAmount=body:LineAmount*-1]/body:TaxAmount"></xsl:with-param>
</xsl:call-template>
The second piece of code is just a merge of of the code in the first example, or am I mistaken?
I think you want
<xsl:call-template name ="TaxTotalLine">
<xsl:with-param name="TaxAmount" select="../body:TaxTrans[body:TaxBaseAmount = current()/body:LineAmount*-1]/body:TaxAmount"></xsl:with-param>
</xsl:call-template>
to compare the LineAmount of the currently processed node (e.g. any outer template or for-each) to the TaxBaseAmout of the TaxTrans element you have applied the predicate in square brackets to.
thanks for the response.
It was acutally a combination of the "current()" and the brackets.
Much appreciated !
Kind regards
Mathieu

XSLT format-number with comma for indian price

I want to display a price for India, like this:
5,55,555
And not
555,555
There should be no decimal. There should be a comma, like this:
1,000 one thousand
10,000 ten thousand
1,00,000 one lakh
My code:
<Price>555555</Price>
<xsl:decimal-format name="Format_INR" grouping-separator="," />
<xsl:value-of select="format-number(Price, '#,##,###', 'Format_INR')" />
But it displays
555,555
What did I do wrong?
Thank you for your help.
The XSLT 2.0 specification of format-number() allows irregular grouping separators as in your example.
The XSLT 1.0 specification is based on the Java specification of DecimalFormat, which requires regular intervals between grouping separators.
(To be more precise: the JDK 7 spec requires regular intervals, or at any rate, it treats the last interval as the one to be used: (the interval between the last one and the end of the integer is the one that is used. So "#,##,###,####" == "######,####" == "##,####,####". But the XSLT 1.0 spec refers specifically to JDK 1.1.8, which is pretty-well unobtainable nowadays; my recollection is that it was very vague on such questions, and later versions of the JDK specification essentially documented the bugs in the initial implementation. To the extent that JDK 1.1.8 was vague, XSLT 1.0 implementations are free to do their own thing.)
As already mentioned, in XSLT 2.0 you can use:
<xsl:value-of select="format-number(Price, '#,##,###')" />
This will accommodate numbers up to 9,999,999. Above that, you need to add more separators, e.g.:
<xsl:value-of select="format-number(Price, '##,##,##,###')" />
will work for numbers up to 999,999,999 and so on.
In XSLT 1.0 you can do:
<xsl:choose>
<xsl:when test="Price >= 1000">
<xsl:value-of select="format-number(floor(Price div 1000), '#,##')" />
<xsl:text>,</xsl:text>
<xsl:value-of select="format-number(Price mod 1000, '000')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="format-number(Price, '#,###')" />
</xsl:otherwise>
</xsl:choose>
This will work for any magnitude of Price. If you need to reuse this, consider making it a named template.
Note that neither method requires you to define a xsl:decimal-format.

How can I shorten this XSLT snippet?

I have to repeat the following XSLT snippet like 100 times and I would like it to be as small as possible. Is there a way to make an equivalent XSLT snippet that is shorter?
<xslo:variable name="myVariable" select="//This/that/anotherthing" />
<xslo:choose>
<xslo:when test="string($myVariable) != 'NaN'">
<xslo:text>1</xslo:text>
</xslo:when>
<xslo:otherwise>
<xslo:text>0</xslo:text>
</xslo:otherwise>
</xslo:choose>
I'm basically setting the state of a checkbox based on whether or not a value exists in //This/that/anotherthing in the source xml.
Can be XSLT 1.0 or XSLT 2.0, doesn't matter.
You can use an if instead of xsl:choose (XSLT 2.0 only)...
<xsl:value-of select="if (string(number(//This/that/anotherthing)) = 'NaN') then 0 else 1"/>
I also dropped the xsl:variable, but if you need it for some other reason, you can put it back.
You could also create a function...
<xsl:function name="local:isNumber">
<xsl:param name="context"/>
<xsl:value-of select="if (string(number($context)) = 'NaN') then 0 else 1"/>
</xsl:function>
usage...
<xsl:value-of select="local:isNumber(//This/that/anotherthing)"/>
<xslo:variable name="myVariable" select="//This/that/anotherthing" />
<xslo:value-of select="number(boolean($myVariable))"/>
If I understand the purpose correctly - that is return 1 if the value in question can be successfully expressed as a number, 0 otherwise - then I believe:
<xsl:value-of select="number(//This/that/anotherthing castable as xs:double)"/>
would be the most straightforward way (in XSLT 2.0) to achieve it.
Edit
In view of your change of purpose:
I'm basically setting the state of a checkbox based on whether or not
a value exists in //This/that/anotherthing
That's even simpler:
<xsl:value-of select="number(boolean(string(//This/that/anotherthing)))"/>

XSLT Date Comparisons

<xsl:variable name="date1" select="2011-10-05"/>
<xsl:variable name="date2" select="2011-10-05"/>
<xsl:variable name="date3" select="2011-10-06"/>
<xsl:if test="$date2 = $date1 or $date2 < $date1">
..do something
</xsl:if>
<xsl:if test="$date3 = $date1 or $date3 > $date1">
.. do something
</xsl:if>
Both should evaluate true, but the second if doesn't. For the life of me I can't comprehended why!
In the actual transform the dates themselves are being drawn from an XML document but debugging through VS2010 i can see the values are as above.
Must be something fairly fundamental i'm doing wrong - any help would be brilliant!
I tried this in Oxygen/XML... select="2011-10-05 is being interpreted as an arithmetic expression, giving the value 1996 (2011 minus 10 minus 5) and "2011-10-06" is intrepreted as 1995.
What you want is
<xsl:variable name="date1" select="'2011-10-05'"/>
<xsl:variable name="date2" select="'2011-10-05'"/>
<xsl:variable name="date3" select="'2011-10-06'"/>
Note the extra single quotes.
From the XSLT 1.0 Specification:
If the variable-binding element has a select attribute, then the value
of the attribute must be an expression and the value of the variable
is the object that results from evaluating the expression.