Calculate attributes with XSLT

  • Thread starter Carles Company Soler
  • Start date
C

Carles Company Soler

Hello,
I want to calculate the value of an attribute. For example <rect x="2+3"
y="12"> and be <rect x="5" y="12">. Is it possible using XSLT?

Thanks!
 
M

Martin Honnen

Carles said:
I want to calculate the value of an attribute. For example <rect x="2+3"
y="12"> and be <rect x="5" y="12">. Is it possible using XSLT?

<rect x="{2 + 3}" y="12">...</rect>
The attribute value is interpreted as an attribute value template where
{} contains an XPath expression, in that example simply one adding two
numbers given as literals but of course you can have more complex XPath
expressions as needed to compute the attribute value.
 
J

Joe Kesselman

Carles said:
I want to calculate the value of an attribute. For example <rect x="2+3"
y="12"> and be <rect x="5" y="12">. Is it possible using XSLT?

Yes, but.

XSLT can certainly perform math, since the XPath language can do so. So
if the expression appears in the stylesheet, this is trivial.

But XSLT doesn't normally have the ability to interpret expressions
found in the input document rather than the stylesheet. Since that seems
to be what you want, you'll have to do some more work -- essentially,
you'll have to use XSLT as a programming language to implement an
expression interpreter, doing the string operations to break up the
expression into values and operators and executing the appropriate
operations.

This has certainly been done, and I suspect a websearch will dig up some
examples. It isn't difficult if you have any experience with
recursive-descent parsing, it's just a nuisance.

Alternatively, some XSLT processors support the "dynamic" section of the
EXSLT extension function library. This permits executing XPath
expressions found in arbitrary string data. It's overkill for your
stated needs, and of course relying on an extension function makes your
stylesheet less portable, but it is another alternative. (Or you could
plug in your own custom extension function, but obviously that's another
step away from portability.)
 
P

p.lepin

Carles said:
I want to calculate the value of an attribute. For
example <rect x="2+3" y="12"> and be <rect x="5" y="12">.
Is it possible using XSLT?

It sure is, BUT. It's really not what XSLT is for: you'd
have to write your own expression evaluator, and XSLT is
ill-suited for that. If you need that type of processing,
you'd better use something else, or wait for XSLT 2.0 to
become Recommendation -- I believe it's much easier to do
something like that with XSLT2. EXSLT is probably an
option, too, but that's something I'd try to avoid if at
all possible.

Just for the heck of it, simple arithmetics-only:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:eek:ps="http://www.example.org/Operators">
<ops:eek:ps>
<ops:eek:p name="+"/><ops:eek:p name="-"/>
<ops:eek:p name="*"/><ops:eek:p name="/"/>
</ops:eek:ps>
<xsl:eek:utput method="xml"/>
<xsl:template match="ops:eek:p[@name='+']" mode="eval">
<xsl:param name="l"/><xsl:param name="r"/>
<xsl:value-of select="$l+$r"/>
</xsl:template>
<xsl:template match="ops:eek:p[@name='-']" mode="eval">
<xsl:param name="l"/><xsl:param name="r"/>
<xsl:value-of select="$l - $r"/>
</xsl:template>
<xsl:template match="ops:eek:p[@name='*']" mode="eval">
<xsl:param name="l"/><xsl:param name="r"/>
<xsl:value-of select="$l*$r"/>
</xsl:template>
<xsl:template match="ops:eek:p[@name='/']" mode="eval">
<xsl:param name="l"/><xsl:param name="r"/>
<xsl:value-of select="$l div $r"/>
</xsl:template>
<xsl:template name="eval">
<xsl:param name="l"/>
<xsl:param name="x"/>
<xsl:variable name="car" select="substring($x,1,1)"/>
<xsl:variable name="cdr" select="substring($x,2)"/>
<xsl:choose>
<xsl:when test="document('')//ops:eek:p[@name=$car]">
<xsl:apply-templates
select="document('')//ops:eek:p[@name=$car]"
mode="eval">
<xsl:with-param name="l" select="$l"/>
<xsl:with-param name="r">
<xsl:call-template name="eval">
<xsl:with-param name="l" select="''"/>
<xsl:with-param name="x" select="$cdr"/>
</xsl:call-template>
</xsl:with-param>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="$car">
<xsl:call-template name="eval">
<xsl:with-param name="l"
select="concat($l,$car)"/>
<xsl:with-param name="x"
select="$cdr"/>
</xsl:call-template>
</xsl:when>
<xsl:eek:therwise>
<xsl:value-of select="$l"/>
</xsl:eek:therwise>
</xsl:choose>
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{name()}">
<xsl:call-template name="eval">
<xsl:with-param name="l" select="''"/>
<xsl:with-param name="x" select="."/>
</xsl:call-template>
</xsl:attribute>
</xsl:template>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Note that it's totally unaware of precedence. (It actually
calculates the right-most expression first. D'oh.) It can
recurse itself unto death really fast, too. And I don't
even mention precision problems it seems to be suffering
from (well, I suppose that's processor-dependent).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,006
Messages
2,570,265
Members
46,861
Latest member
SanoraS48

Latest Threads

Top