XPath Data Types

XPath Data Types

There are four XPath data types, not just the node set type that match patterns must return:

  • Node sets

  • Booleans

  • Numbers

  • Strings

Result Tree Fragments

XSLT 1.0 also added result tree fragments to the XPath data types. As mentioned in Chapter 4, result tree fragments were just tree fragments that you could assign to XSLT variables . Support for them was removed in the XSLT 1.1 working draft, which means they probably wont be a part of XSLT 2.0. You can treat result tree fragments as data types by using <xsl:variable> , which well see in Chapter 9.

The following sections look at these various types in turn .

XPath Node Sets

As its name implies, a node set is simply a set of nodes. A node set may include a number of nodes, a single node, or be empty. Because the primary purpose of XPath is to locate sections of documents, XPath expressions that return node sets are the most popular type of expressions. For example, the XPath expression child::PLANET returns a node set of all <PLANET> elements that are children of the context node. The expression child::PLANET/child::NAME returns a node set of all <NAME> elements that are children of <PLANET> elements of the context node. These kinds of XPath expressions are called location paths (W3C calls them the most important construct in XPath), and Ill devote a lot of this chapter to helping you understand location paths.

To select a node or nodes from a node set, or to handle those nodes, you can use the following XPath functions that work on node sets, which you first saw in Chapter 4 (and which youll see in the next chapter in more detail):

  • count(node-set) . This function returns the number of nodes in node-set . Note that if you omit node-set , this function uses the context node.

  • id(string ID) . This function returns a node set of the element whose ID matches the string passed to the function, or an empty node set if no element has the specified ID. Also, note that you can specify multiple IDs separated by whitespace, and this function returns a node set of the elements with those IDs.

  • last() . This function returns the number of the last node in a node set.

  • local-name(node-set) . This function returns the local name of the first node in the node set. Omitting node-set makes this function use the context node.

  • name(node-set) . This function returns the full, qualified name of the first node in the node set. Omitting node-set makes this function use the context node.

  • namespace-uri(node-set) . This function returns the URI of the namespace of the first node in the node set. Omitting node-set makes this function use the context node.

  • position() . This function returns the position of the context node in the context node set (starting with 1).

The following example, from Chapter 6, used the count function to return the number of nodes in a node set. In this case, the node set is made up of all the <PLANET> elements in planets.xml, and I get that node set with the location path \\PLANET (which, as a location path , is also an XPath expression):

 <xsl:stylesheet   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   version="1.0">  <xsl:output method="xml" indent="yes"/>  <xsl:template match="*">      <xsl:copy>          <xsl:apply-templates/>      </xsl:copy>  </xsl:template>  <xsl:template match="PLANET">      <xsl:copy use-attribute-sets="numbering">          <xsl:apply-templates/>      </xsl:copy>  </xsl:template>  <xsl:attribute-set name="numbering">      <xsl:attribute name="number"><xsl:number/></xsl:attribute>      <xsl:attribute name="total"><xsl:value-of select="count(//PLANET)"/></xsl:attribute>  </xsl:attribute-set>  </xsl:stylesheet> 

Heres the result; note that every <PLANET> element has both a number and total attribute, and the total attribute holds the total number of <PLANET> elements in the document:

 <?xml version="1.0" encoding="UTF-8"?>  <PLANETS>      <PLANET number="1" total="3">          <NAME>Mercury</NAME>          <MASS>.0553</MASS>          <DAY>58.65</DAY>          <RADIUS>1516</RADIUS>          <DENSITY>.983</DENSITY>          <DISTANCE>43.4</DISTANCE>      </PLANET>      <PLANET number="2" total="3">          <NAME>Venus</NAME>          <MASS>.815</MASS>          <DAY>116.75</DAY>          <RADIUS>3716</RADIUS>          <DENSITY>.943</DENSITY>          <DISTANCE>66.8</DISTANCE>      </PLANET>      <PLANET number="3" total="3">          <NAME>Earth</NAME>          <MASS>1</MASS>          <DAY>1</DAY>          <RADIUS>2107</RADIUS>          <DENSITY>1</DENSITY>          <DISTANCE>128.4</DISTANCE>      </PLANET>  </PLANETS> 

Among the node-set functions, note in particular the name and local-name functions. These give you a way of determining the name of the current element; for example, if the current element is <DAY> , local-name returns DAY. To see how this can be useful, look at the following stylesheet, where I use tags such as <PLANETS> , <PLANET> , and <DATA> as literal result elements:

 <?xml version="1.0"?>  <xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">      <xsl:output method="xml"/>      <xsl:template match="PLANETS">          <PLANETS>              <xsl:for-each select="PLANET">                  <PLANET>                      <xsl:for-each select="*">                          <DATA>                              <xsl:value-of select="."/>                          </DATA>                      </xsl:for-each>                  </PLANET>              </xsl:for-each>          </PLANETS>      </xsl:template>  </xsl:stylesheet> 

However, this treats markup as simple text. I can use <xsl:element> to create new elements instead, using local-name to find the name of context nodes this way:

 <?xml version="1.0"?>  <xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">      <xsl:output method="xml"/>      <xsl:template match="PLANETS">          <xsl:element name="{local-name(.)}">              <xsl:for-each select="PLANET">                  <xsl:element name="{local-name(.)}">                      <xsl:for-each select="*">                          <xsl:element name="DATA">                              <xsl:value-of select="."/>                          </xsl:element>                      </xsl:for-each>                  </xsl:element>              </xsl:for-each>          </xsl:element>      </xsl:template>  </xsl:stylesheet> 

Some XSLT authors think of XPath expressions only in terms of expressions that return node sets. However, XPath expressions also return Booleans, numbers, or strings, and those results are used in such places as <xsl:param> , <xsl: with-param > , <xsl:number> , <xsl:value-of> , <xsl: sort > , attribute value templates, and the predicates of location paths. In the preceding example, I assigned the XPath expression count(//PLANET) , which returns a number, not a node set, to the select attribute of the <xsl:value-of> element, to insert a number into a document. Ill take a closer look at the numbers you can handle using XPath expressions next.

XPath Numbers

In XPath, numbers are stored in double floating point format. Formally defined, all XPath numbers are supposed to be stored in 64-bit IEEE 754 double floating point format, and all numbers are stored in double precision floating point format.

You can use the following XPath operators on numbers, as you first saw in Chapter 4 during the discussion of XPath predicates:

  • + addition

  • - subtraction

  • * multiplication

  • div division (The / character, which stands for division in other languages, is already used in XML and XPath.)

  • mod finding the modulus of two numbers (the remainder after dividing the first by the second)

For example, the element <xsl:value-of select="15 + 75"/> inserts the string 90 into the output document. The following example selects all planets whose day (measured in Earth days) multiplied by its distance from the Sun (measured in millions of miles) is greater than 60,000:

 <xsl:template match="PLANETS">      <HTML>          <BODY>              <xsl:apply-templates select="PLANET[DAY * MASS > 60000]"/>          </BODY>      </HTML>  </xsl:template> 

XPath also supports these functions that operate on numbers:

  • ceiling() . This function returns the smallest integer larger than the number you pass it.

  • floor() . This function returns the largest integer smaller than the number you pass it.

  • round() . This function rounds the number you pass it to the nearest integer.

  • sum() . This function returns the sum of the numbers you pass to it.

For example, heres an example that determines the average distance from the Sun (in million miles) of the planets in planets.xml:

 <?xml version="1.0"?>  <xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  <xsl:output method="xml"/>  <xsl:template match="PLANETS">      <HTML>          <BODY>              The average planetary distance from the Sun is:              <xsl:value-of select="sum(child::PLANET/child::DISTANCE) div graphics/ccc.gif count(child::PLANET)"/>          </BODY>      </HTML>  </xsl:template>  </xsl:stylesheet> 

XPath Strings

In XPath, strings are made up of Unicode characters by default. As you first saw in Chapter 4 during the discussion of XPath expressions in match predicates, a number of functions are specially designed to work on strings (youll see them in more detail in the next chapter):

  • concat(string string1 , string string2 , ...) . This function returns all strings concatenated together.

  • contains(string string1 , string string2 ) . This function returns true if the first string contains the second one.

  • format-number(number number1 , string string2 , string string3 ) . This function returns a string holding the formatted string version of number1 , using string2 as a formatting string (you create formatting strings as you would for Javas java.text.DecimalFormat method), and string3 as the optional locale string.

  • normalize-space(string string1 ) . This function returns string1 after leading and trailing whitespace is stripped and multiple consecutive whitespace is replaced with a single space.

  • starts-with (string string1 , string string2 ) . This function returns true if the first string starts with the second string.

  • string-length (string string1 ) . This function returns the number of characters in string1 .

  • substring(string string1 , number offset , number length ) . This function returns length characters from the string, starting at offset .

  • substring-after(string string1 , string string2 ) . This function returns the part of string1 after the first occurrence of string2 .

  • substring-before (string string1 , string string2 ) . This function returns the part of string1 up to the first occurrence of string2 .

  • translate(string string1 , string string2 , string string3 ) . This function returns string1 with all occurrences of the characters in string2 replaced by the matching characters in string3 .

In the following example, I search all attributes for the word miles, and if found, I add the text You should switch to kilometers. in the result document:

Listing 7.1 Searching Attributes for Text
 <?xml version="1.0"?>  <xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">      <xsl:template match="/PLANETS">          <HTML>              <HEAD>                  <TITLE>                      The Planets Table                  </TITLE>              </HEAD>              <BODY>                  <H1>                      The Planets Table                  </H1>                  <TABLE BORDER="2">                      <TR>                          <TD>Name</TD>                          <TD>Mass</TD>                          <TD>Radius</TD>                          <TD>Day</TD>                          <TD>Distance</TD>                      </TR>                      <xsl:apply-templates/>                  </TABLE>              </BODY>          </HTML>      </xsl:template>      <xsl:template match="PLANET">         <TR>            <TD><xsl:value-of select="NAME"/></TD>            <TD><xsl:apply-templates select="MASS"/></TD>            <TD><xsl:apply-templates select="RADIUS"/></TD>            <TD><xsl:apply-templates select="DAY"/></TD>            <TD><xsl:apply-templates select="DISTANCE"/></TD>         </TR>     </xsl:template>      <xsl:template match="MASS">          <xsl:value-of select="."/>          <xsl:text> </xsl:text>          <xsl:value-of select="@UNITS"/>      </xsl:template>      <xsl:template match="RADIUS">          <xsl:value-of select="."/>          <xsl:text> </xsl:text>          <xsl:value-of select="@UNITS"/>      </xsl:template>      <xsl:template match="DAY">          <xsl:value-of select="."/>          <xsl:text> </xsl:text>          <xsl:value-of select="@UNITS"/>      </xsl:template>      <xsl:template match="DISTANCE">          <xsl:value-of select="."/>          <xsl:text> </xsl:text>          <xsl:value-of select="@UNITS"/>      </xsl:template>      <xsl:template match="//*[contains(@UNITS, 'miles')]">          <xsl:value-of select="."/>          <xsl:text> </xsl:text>          <xsl:text>You should switch to kilometers.</xsl:text>      </xsl:template>  </xsl:stylesheet> 

Heres the result document:

 <HTML>      <HEAD>          <TITLE>              The Planets Table          </TITLE>      </HEAD>      <BODY>          <H1>              The Planets Table          </H1>          <TABLE BORDER="2">              <TR>                  <TD>Name</TD>                  <TD>Mass</TD>                  <TD>Radius</TD>                  <TD>Day</TD>                  <TD>Distance</TD>              </TR>              <TR>                  <TD>Mercury</TD>                  <TD>.0553 (Earth = 1)</TD>                  <TD>1516 You should switch to kilometers.</TD>                  <TD>58.65 days</TD>                  <TD>43.4 You should switch to kilometers.</TD>              </TR>              <TR>                  <TD>Venus</TD>                  <TD>.815 (Earth = 1)</TD>                  <TD>3716 You should switch to kilometers.</TD>                  <TD>116.75 days</TD>                  <TD>66.8 You should switch to kilometers.</TD>              </TR>              <TR>                  <TD>Earth</TD>                  <TD>1 (Earth = 1)</TD>                  <TD>2107 You should switch to kilometers.</TD>                  <TD>1 days</TD>                  <TD>128.4 You should switch to kilometers.</TD>              </TR>          </TABLE>      </BODY>  </HTML> 

In addition to working with node sets, numbers, and strings, you can also work with Boolean true/false values.

XPath Booleans

XPath Boolean expressions evaluate to either true or false, and theyre usually used only in predicates. Numbers are considered false if theyre zero, and true otherwise . An empty string, , is also considered false, and all other strings are considered true. Node sets are considered true if theyre not empty.

You can use a number of logical operators to produce true/false results in XPath, as you saw in overview in Chapter 4:

  • != means is not equal to

  • < means is less than (use &lt; in XML documents)

  • <= means is less than or equal to (use &lt;= in XML documents)

  • = means is equal to (C, C++, Java, JavaScript programmers take note: this operator is one = sign, not two)

  • > means is greater than

  • >= means is greater than or equal to

You can also use the keywords and or or to connect Boolean clauses with a logical And or Or operation, and the not function to reverse the logical sense of an expression, as in this case, where Im selecting all <PLANET> elements that are not the first or last such element:

 <xsl:template match="PLANET[not(position() = 1) and not(position() = last())]">      <xsl:value-of select="."/>  </xsl:template> 

Heres an example you first saw in Chapter 5 that uses the logical operator not and the operators = and != :

 <xsl:template match="PLANET">      <xsl:if test="NAME[not(text())]">          <xsl:message terminate="yes">              Each planet must have a name!          </xsl:message>      </xsl:if>      <xsl:value-of select="NAME"/>      <xsl:choose>          <xsl:when test="position()!=last()">, </xsl:when>          <xsl:when test="position()=last()-1">and </xsl:when>          <xsl:otherwise>.</xsl:otherwise>      </xsl:choose>  </xsl:template> 

In addition, there is a true function that always returns true, and a false function that always returns a value of false. Theres also a lang function that you can use to check the language set in the documents xml:lang attribute; this function returns a value of true if the language you pass it is the same as the language set in the document.

As you see, there are all kinds of XPath expressions, including ones that return nodes, numbers, strings, and Boolean values. The most important type of XPath expression is the location path ; creating location paths is what XPath was originally designed for, and Ill spend the remainder of this chapter working with them.



Inside XSLT
Inside Xslt
ISBN: B0031W8M4K
EAN: N/A
Year: 2005
Pages: 196

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net