Variables and Paths
Q: Can I change the value of an XSLT variable?
Is there any way to change the value of an
xsl:variable element inside an
A: Of all the hurdles facing people who're learning XSLT, in my opinion the biggest one is the word "variable" when used in this context. That's because XSLT variables do not vary. Rather, the word signifies that a value is not necessarily known until runtime: it may vary every time a stylesheet is consumed by an XSLT processor in order to perform a transformation. But, once set in a given transformation, the variable's value remains unchanged.
First, some background for those of you who haven't used XSLT variables previously.
A variable is declared in a stylesheet using an
xsl:variable element. Here's an example:
<xsl:variable name="empname" select="/employee/name">
In this case, a variable with the given
assigned a value equal to the string-value of the XPath expression
given by the
select attribute. The variable's value can
then be retrieved by referencing the variable name with a dollar sign
prepended. For instance:
empname variable has a value of "Munchausen,"
xsl:value-of element causes the string "Munchausen"
to be instantiated in the result tree at that point.
The chief gotcha when dealing with XSLT variables is that
a particular variable only has meaning within the context of the
xsl:variable element's parent element. This is referred
to as the variable's scope. You can declare a global variable -- one
whose value is accessible ("in scope") anywhere in the stylesheet --
by making the
xsl:variable a child of the stylesheet's
xsl:stylesheet element. For instance,
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:variable name="booktitle" select="/book/@title"/> ... </xsl:stylesheet>
(This is a convenient shorthand which avoids repeating complex XPath expressions throughout the stylesheet. It can also result in more maintainable and easily understood stylesheets.)
xsl:variable can also be a child of nearly any
other element in the XSLT namespace. For example,
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="book"> <xsl:variable name="booktitle" select="@title"/> <xsl:if> <xsl:when test="substring($booktitle,1,1) <= 'M'"> [template to apply for book titles from first half of alphabet] </xsl:when> <xsl:otherwise> [template to apply for book titles from second half of alphabet] </xsl:otherwise> </xsl:if> </xsl:template> ... </xsl:stylesheet>
xsl:template element above, the value of
booktitle variable will vary depending on the
string-value of a given
attribute. So, yes, it varies, but it remains constant for any given
It seems what the questioner wants to do, in the absence of a particular code example, would look something like
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="book"> <xsl:if> <xsl:when test="substring(@title,1,1) <='M'"> <xsl:variable name="somevar" select="[XPath expression]"/> [template to apply for book titles from first half of alphabet] </xsl:when> <xsl:otherwise> <xsl:variable name="somevar" select="[XPath expression]"/> [template to apply for book titles from second half of alphabet] </xsl:otherwise> </xsl:if> </xsl:template> ... </xsl:stylesheet>
There are a couple of potential problems here. The first
xsl:variable element's parent is now the
xsl:when element, and, therefore, the variable is not
available within, for example, the
element. Furthermore, no matter where you place your
xsl:variable, its value is never really
changeable. By declaring a variable called
twice, above, all you've done is create two variables which just
happen to have the same name but entirely different scopes: one within
xsl:when element and one within the
I think the best way to understand XSLT variable behavior -- and maybe to solve your problem (although the solution may not be what you expect) -- is by consulting Dave Pawson's indispensable XSLT FAQ, in particular the section specifically about variables.
Q: How do I extract all possible document paths using just a DTD?
I'm trying to extract all the paths from a given DTD: for every DTD there exists a node tree; for each node there exists a path from the root of the tree to that node. Is there some simple way of doing this? I need to extract all these paths and store them in a relational database management system.
A: My first guess is no, there's no simple way to accomplish this task. A DTD provides a general road map to a valid document's structure for a given XML application; it does not define the specific structures of all possible valid documents for a given XML application.
Assume you've got a simple DTD which looks like the following:
<!ELEMENT root_elem (child_elem+)>
<!ELEMENT child_elem (grandchild_elem*)>
<!ELEMENT grandchild_elem (#PCDATA)>
All you can tell from this DTD is that a valid document in this
application has a single root element,
one or more
child_elem child elements, each of which may
have 0, 1, or more
grandchild_elem children. The problem
with your question, assuming I'm reading it right, is in the
statement, "...for every DTD there exists a node tree." Actually, no,
for every DTD there exists at least one+,
?, the number of possible node trees
increases sharply, all the way (if the
* operators are used) to infinity.
Your mention of an RDBMS, however, makes me wonder if what you're really after is how to model a database structure based on the general structure described by a DTD -- that is, how to map one to the other. For such questions, it's hard to avoid running into the name Ron Bourret (developer of the XML and Databases informational site). His feature on XML.com (from May, 2001) is a great place to start.
Q: Where can I get XML-based currency quotes?
On my Web site, I publish prices in CHF (Swiss Francs). I also want to publish the prices in Euros. So every time the page is requested the actual quote must be retrieved, but where can I get that quote?
A: There are multiple sources for this kind of information, but a simple one with which I'm familiar is called CEWS (the Currency Exchange Web Service), from duzine.com. (Registered developers can obtain a single-CPU license to use CEWS for development work. This license provides current quotes for 21 international currencies, including Swiss Francs and Euros as well as dollars, pounds, Japanese yen, Dutch guilders, and so on.)
An example posted on the CEWS page illustrates the simple structure of a CEWS message:
<?xml version="1.0" ?> <CEWS> <DATE>21 August 2001</DATE> <BASE>EUR</BASE> <CURRENCY_LIST> <ATS>13.7603</ATS> <AUD>1.7110</AUD> <CAD>1.4107</CAD> <CHF>1.5166</CHF> ... </CURRENCY_LIST> </CEWS>
DATE element indicates the date for which this
quote was correct. The
BASE element designates the unit
of currency to which the quote applies. The child elements of the
CURRENCY_LIST element correspond to currency name
abbreviations. Shown above are the Austrian Schilling, Australian
Dollar, Canadian Dollar, and -- yes -- Swiss Franc, which on the
indicated date were worth 13.7603, 1.7110, 1.4107, and 1.5166 Euros,
As the CEWS page points out, this message structure is extremely compact and simple to process. It doesn't include a SOAP wrapper, for example, and needn't be validated against a DTD or XML Schema.
Similar solutions are provided by Evergreen.com, Oanda, Cloanto, and numerous other vendors. As you might expect, few of these services are offered for free if you want to do anything with the data other than develop software to use it. You might also take a look at /n Software's IPWorks Internet Toolkit, which is available across a wide variety of platforms and includes a currency-conversion demo. Finally, for SOAP-based currency conversion, see the XMethods "Euro Conversor" site.
John E. Simpson is the author of Just XML (Prentice Hall PTR; 2nd edition forthcoming) and a regular contributor to XML mailing lists, particularly XML-L. He has been an application developer for more than 20 years, and is currently the Webmaster for several non-profit organizations based in Florida. He once closed a bar with Joseph Heller, Kurt Vonnegut Jr., and William Styron.