Reputation: 425
I am building an expression calculator using XSD, XML and XSLT.
At the moment only Sum
operation is implemented.
I have already developed the xml-schema
and a valid xml
document.
SCHEMA (XSD)
<xs:complexType name="typeSum">
<xs:sequence>
<xs:element ref="exp:Expression"/>
<xs:element ref="exp:Expression"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="EE">
<xs:sequence>
<xs:element ref="exp:Expression" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:element name="Expression" abstract="true"/>
<xs:element name="EE" type="exp:EE"/>
<xs:element name="Const" type="xs:integer" substitutionGroup="exp:Expression"/>
<xs:element name="Sum" type="exp:typeSum" substitutionGroup="exp:Expression"/>
XML (In this XML I would like to do the following: Sum(Sum(8,4),Sum(1,2))
)
<?xml version="1.0" encoding="UTF-8"?>
<e:EE xmlns:e="expr"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="xxx/xxx/xxx...">
<e:Sum>
<e:Sum>
<e:Const>
8
</e:Const>
<e:Const>
4
</e:Const>
</e:Sum>
<e:Sum>
<e:Const>
1
</e:Const>
<e:Const>
2
</e:Const>
</e:Sum>
</e:Sum>
</e:EE>
The problem comes with the XSLT. This is what I have tried so far:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:e="expr"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output
method="xml"
indent="yes"
encoding="iso-8859-1"/>
<xsl:template match="e:EE">
<xsl:copy>
<xsl:apply-templates select="e:Sum" />
</xsl:copy>
</xsl:template>
<xsl:template match="e:Sum">
<xsl:variable name="v1">
<xsl:apply-templates select="e:Const[1]"></xsl:apply-templates>
</xsl:variable>
<xsl:variable name="v2">
<xsl:apply-templates select="e:Const[2]"></xsl:apply-templates>
</xsl:variable>
<Operation>
<xsl:value-of select="$v1" />
+
<xsl:value-of select="$v2" />
</Operation>
<result>
<xsl:value-of select="$v1 + $v2"/>
</result>
</xsl:template>
</xsl:stylesheet>
This works when I sum
only 2 Const
. i.e. Sum(8,4)
However, I want to recursively apply the Sum template
to every expression in the XML.
Upvotes: 1
Views: 2202
Reputation: 101652
You can do this by using templates effectively - define templates so that they know how to handle the operation at hand.
In the case of Sum, that would be:
Sum doesn't need to know what kind of operands it has. It can just apply templates to them and let the XSLT figure that out. This is what we get:
<xsl:template match="e:Sum">
<xsl:variable name="v1">
<xsl:apply-templates select="*[1]" />
</xsl:variable>
<xsl:variable name="v2">
<xsl:apply-templates select="*[2]" />
</xsl:variable>
<xsl:value-of select="$v1 + $v2"/>
</xsl:template>
It probably makes sense to also define a template for e:Const
, just so it's clearly defined:
<xsl:template match="e:Const">
<xsl:value-of select ="."/>
</xsl:template>
Here's a full XSLT, with the addition of e:Multiply
and a unary operation (e:Invert
):
<xsl:stylesheet version="1.0"
xmlns:e="expr"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output
method="xml"
indent="yes"
encoding="iso-8859-1"/>
<xsl:template match="e:EE">
<xsl:copy>
<xsl:apply-templates select="*[1]" />
</xsl:copy>
</xsl:template>
<xsl:template match="e:Sum">
<xsl:variable name="v1">
<xsl:apply-templates select="*[1]" />
</xsl:variable>
<xsl:variable name="v2">
<xsl:apply-templates select="*[2]" />
</xsl:variable>
<xsl:value-of select="$v1 + $v2"/>
</xsl:template>
<xsl:template match="e:Multiply">
<xsl:variable name="v1">
<xsl:apply-templates select="*[1]" />
</xsl:variable>
<xsl:variable name="v2">
<xsl:apply-templates select="*[2]" />
</xsl:variable>
<xsl:value-of select="$v1 * $v2"/>
</xsl:template>
<xsl:template match="e:Invert">
<xsl:variable name="v1">
<xsl:apply-templates select="*[1]" />
</xsl:variable>
<xsl:value-of select="1 div $v1"/>
</xsl:template>
<xsl:template match="e:Const">
<xsl:value-of select ="."/>
</xsl:template>
</xsl:stylesheet>
When run on this input XML:
<?xml version="1.0" encoding="UTF-8"?>
<e:EE xmlns:e="expr"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="xxx/xxx/xxx...">
<e:Sum>
<e:Sum>
<e:Const>8</e:Const>
<e:Const>4</e:Const>
</e:Sum>
<e:Sum>
<e:Multiply>
<e:Invert>
<e:Const>3</e:Const>
</e:Invert>
<e:Const>6</e:Const>
</e:Multiply>
<e:Const>2</e:Const>
</e:Sum>
</e:Sum>
</e:EE>
The result is:
<e:EE xmlns:e="expr" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">16</e:EE>
Upvotes: 2