4

I would like to know if in XLST can we use the math:abs(...) ? I saw this somewhere but it does not work . I have something like:

<tag>
  <xsl:value-of select="./product/blablaPath"/>
</tag>

I tried to do something like:

<tag>
  <xsl:value-of select="math:abs(./product/blablaPath)"/>
</tag>

but does not work. I'm using java 1.6 language.

Taryn
  • 242,637
  • 56
  • 362
  • 405
CC.
  • 2,736
  • 11
  • 53
  • 70

5 Answers5

7

Here is a single XPath expression implementing the abs() function:

($x >= 0)*$x - not($x >= 0)*$x

This evaluates to abs($x).

Here is a brief demonstration of this in action:

<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="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="text()">
  <xsl:param name="x" select="."/>
  <xsl:value-of select=
  "($x >= 0)*$x - not($x >= 0)*$x"/>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied to the following XML document:

<t>
  <num>-3</num>
  <num>0</num>
  <num>5</num>
</t>

the wanted, correct result (abs() on every number) is produced:

<t>
  <num>3</num>
  <num>0</num>
  <num>5</num>
</t>
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • 1
    @tomalak Actually, this is just 1:1 the definition of abs(). If $x >= 0, then just $x (multiplied by 1), If $x < 0, then $x multiplied by -1 THe conditions above are mutually exclusive, so exactly one of them is true (1) and exactly one is false (0). We use this to sum the two and produce a single formula. – Dimitre Novatchev Feb 15 '10 at 18:08
  • @Dimitre: That's exactly what is called "Becker's Method" when done with strings. :) See Олэг's blog: http://www.tkachenko.com/blog/archives/000156.html. – Tomalak Feb 15 '10 at 19:44
  • @tomalak This is a property of XPath 1.0's coersion of boolean into number. It has been used extensively and not only by O.B. – Dimitre Novatchev Feb 15 '10 at 20:37
  • This didn't seem to work for me, perhaps you can show an example of it being used. – James May 05 '11 at 22:58
  • @James: I have added to my answer a complete XSLT transformation and an XML document that you can copy/paste into your favorite XSLT IDE and then run the transformation. What didn't "work" for you is something additional in your code. Please, let me know if you continue to have problems of any kind -- best ask a question in the xslt tag and notify me via a comment. – Dimitre Novatchev May 05 '11 at 23:14
  • Sorry to bump this question/answer, but I'm trying to implement this too, and it isn't working. I can't make variables from the nodes due to how I'm having to do this: `{((entry/transaction-total >= 0)*entry/transaction-total - not(entry/transaction-total >= 0)*entry/transaction-total) -sum(((entry/transaction-splits/item/amount >= 0)*entry/transaction-splits/item/amount - not(entry/transaction-splits/item/amount >= 0)*entry/transaction-splits/item/amount))}` – designermonkey Jul 10 '11 at 16:38
3

A very simple solution is to use the XSL 1.0 translate function. I.e.

<xsl:value-of select="translate($x, '-', '')/>
Justin Rowe
  • 2,356
  • 1
  • 20
  • 15
3

abs() is trivial enough. Implemented in pure XSLT it would look like this:

<xsl:template name="abs">
  <xsl:param name="number">

  <xsl:choose>
    <xsl:when test="$number &gt;= 0">
      <xsl:value-of select="$number" />
    <xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$number * -1" />
    </xsl:otherwise>
  </xsl:if>
</xsl:template>

in your context you would invoke it like this:

<tag>
  <xsl:call-template name="abs">
    <xsl:with-param name="number" select="number(product/blablaPath)" />
  </xsl:call-template>
</tag>
Tomalak
  • 332,285
  • 67
  • 532
  • 628
1

Anotherway:

(2*($x >= 0) - 1)*$x

When $x is positive, the test returns "true", so 2*true-1 returns 1, so final result is $x. When $x is negative, the test returns "false", so 2*false-1 returns -1, so final result is -$x.

Using

2*(any-test-here)-1
is a good way to have +1 when test is true, and -1 when false.
Xenos
  • 3,351
  • 2
  • 27
  • 50
0

math:abs is not built in to XSLT or XPATH. It is an XSLT extension, provided by the runtime you are transforming with.

Here is an article about .NET xslt extensions.

Here is one for Java (Xalan).

Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • I would prefer to modify only the xslt file not the java code, because there is alot of impact in java code. – CC. Feb 03 '10 at 09:35
  • Too bad. `math:abs` would be provided as an xslt extension by your java code. Read the linked articles to see how this is exposed to your xslt. – Oded Feb 03 '10 at 09:37