Menu

Variables and Paths

June 26, 2002

John E. Simpson

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 xsl:if or xsl:when element?

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 name is 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:

<xsl:value-of select="$empname"/>

If the empname variable has a value of "Munchausen," this 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 root 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.)

But 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) &lt;= '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>

Within the xsl:template element above, the value of the booktitle variable will vary depending on the string-value of a given book element's title attribute. So, yes, it varies, but it remains constant for any given book element.

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) &lt;='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 xsl:otherwise element. Furthermore, no matter where you place your xsl:variable, its value is never really changeable. By declaring a variable called somevar twice, above, all you've done is create two variables which just happen to have the same name but entirely different scopes: one within the xsl:when element and one within the xsl:otherwise.

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, root_elem, with 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 node tree, but once the DTD introduces occurrence operators like +, *, and ?, the number of possible node trees increases sharply, all the way (if the + or * 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>

The 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, respectively.

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.