BizTalk Utilities CV ,   Jobs ,   Code library  
 
Home Page
XSLT
VoiceXML with XSLT
XSLT Quickly
The Functional Programming Language XSLT - A proof through examples
The Understanding XSL Game
Dynamic functions: Functional combination, partial application and lambda expressions
XSLT Stylesheets, find over 100 example XSLT stylesheets
XSLT - Creating Links and Cross-References
XML in a Nutshell - XPath
The XPath Function: node-set unparsed-entity-url ( string )
The XPath Function: boolean true ( )
The XPath Function: string translate ( string, string, string )
The XPath Function: object system-property ( string )
The XPath Function: number sum ( node-set )
The XPath Function: string substring ( string, number, number? )
The XPath Function: string substring-before ( string, string )
The XPath Function: string substring-after ( string, string )
The XPath Function: string string ( object? )
The XPath Function: number string-length ( string? )
The XPath Function: boolean starts-with ( string, string )
The XPath Function: number round ( number )
<< XQuery
.NET and XML >>

By :Mark Wilson
I am the creator of TopXML. I am available for international and local (Australia) contracts. I am a Solution Architect/Business Analyst. I have worked in IT in several countries (NZ, Australia, South Africa, UK) building and training teams for government and very large non-governmental organizations. I am ex-Microsoft Consulting Services. I wrote the first book on Microsoft XML published in 2000 called XML Programming with VB and ASP. Most recently I have been building tools for the SEO industry. Ask me for a 37 point SEO health-checkup for your website.
First posted :11/09/2004
Times viewed :4975

 

Using XPath Predicates

cover image This is an excerpt from Chapter 4 of the New Riders book called Inside XSLT written by Steven Holzner.

PLEASE NOTE:  To work with the XSLT and XPath in this document TopXML recommends you use the demo version of Xselerator XSL Editor

The following example uses the logical operator >. This rule applies to all <PLANET> elements after position 5:

<xsl:template match="PLANET[position() > 5]">

    <xsl:value-of select="."/>

</xsl:template>

There is also a true function that always returns a value of true, and a false function that always returns a value of false. You can also use the not function to reverse the logical sense of an expression, as in the following case, where I'm selecting all but the last <PLANET> element:

<xsl:template match="PLANET[not(position() = last())]">

    <xsl:value-of select="."/>

</xsl:template>

Finally, the lang function returns true or false depending on whether the language of the context node (which is given by xml:lang attributes) is the same as the language you pass to this function.

Predicates: Numbers

In XPath, numbers actually are stored in double floating point format. (Technically speaking, all XPath numbers are stored in 64-bit IEEE 754 floating-point double format.) All numbers are stored as doubles, even integers such as 5, as in the example you just saw:

<xsl:template match="PLANET[position() > 5]">

    <xsl:value-of select="."/>

</xsl:template>

You can use several operators on numbers:

  • + addition
  • - subtraction
  • * multiplication
  • div division (the / character, which stands for division in other languages, is already heavily used in XML, XSL, and XPath)
  • mod returns the modulus of two numbers (the remainder after dividing the first by the second).

For example, the element <xsl:value-of select="180 + 420"/> inserts the string "600" into the output document. The following example selects all planets whose day (measured in Earth days) divided by its mass (where the mass of the earth = 1) is greater than 100:

<xsl:template match="PLANETS">

    <HTML>

        <BODY>

            <xsl:apply-templates select="PLANET[DAY div MASS > 100]"/>

        </BODY>

    </HTML>

</xsl:template>

XPath also supports these functions that operate on numbers:

  • ceiling(). Returns the smallest integer larger than the number you pass it.
  • floor(). Returns the largest integer smaller than the number you pass it.
  • round(). Rounds the number you pass it to the nearest integer.
  • sum(). Returns the sum of the numbers you pass it.

For example, here's how you can find the average mass of the planets in planets.xml:

Listing 4.8 Finding average planetary mass

<?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 mass is:

<xsl:value-of select="sum(child::PLANET/child::MASS) div count(child::PLANET)"/>

        </BODY>

    </HTML>

</xsl:template>

</xsl:stylesheet>

Strings

In XPath, strings are made up of Unicode characters, as you'd expect. A number of functions are specially designed to work on strings:

  • string(object object1). Converts an object into a string.
  • starts-with(string string1, string string2). Returns true if the first string starts with the second string.
  • contains(string string1, string string2). Returns true if the first string contains the second one.
  • substring(string string1, number offset, number length). Returns length characters from the string, starting at offset.
  • substring-before(string string1, string string2). Returns the part of string1 up to the first occurrence of string2.
  • substring-after(string string1, string string2). Returns the part of string1 after the first occurrence of string2.
  • string-length(string string1). Returns the number of characters in string1.
  • normalize-space(string string1). Returns string1 after leading and trailing whitespace is stripped and multiple consecutive whitespace is replaced with a single space.
  • translate(string string1, string string2, string string3). Returns string1 with all occurrences of the characters in string2 replaced by the matching characters in string3.
  • concat(string string1, string string2,...). Returns all strings concatenated (that is, joined) together.

And there's another string function you should know about that is actually part of XSLT, not XPath:

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

In the following example, I match text nodes whose text starts with 'E' to match the Earth, and add the text '(the World)' to the description of Earth, making it 'Earth (the World)'. To do that, I use the predicate "text()[starts-with(., 'E')]":

Listing 4.9 Using the starts-with function

<?xml version="1.0"?>

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/PLANETS">

        <HTML>

            <HEAD>

        .

        .

        .

            </BODY>

        </HTML>

    </xsl:template>

    <xsl:template match="PLANET">

       <TR>

          <TD><xsl:apply-templates select="NAME"/></TD>

          <TD><xsl:apply-templates select="MASS"/></TD>

          <TD><xsl:apply-templates select="RADIUS"/></TD>

          <TD><xsl:apply-templates select="DAY"/></TD>

       </TR>

   </xsl:template>

       <xsl:template match="text()[starts-with(., 'E')]">

        <xsl:text>(the World)</xsl:text>

    </xsl:template>

    <xsl:template match="NAME">

        <xsl:value-of select="."/>

        <xsl:text> </xsl:text>

        <xsl:value-of select="@UNITS"/>

        <xsl:apply-templates/>

    </xsl:template>

        .

        .

        .

    <xsl:template match="DAY">

        <xsl:value-of select="."/>

        <xsl:text> </xsl:text>

        <xsl:value-of select="@UNITS"/>

    </xsl:template>

   </xsl:stylesheet>

And here's the result note that the caption for Earth has become "Earth (the World)":

<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>

            </TR>

            .

            .

            .

            <TR>

                <TD>Earth (the World)</TD>

                <TD>1 (Earth = 1)</TD>

                <TD>2107 miles</TD>

                <TD>1 days</TD>

            </TR>

        </TABLE>

    </BODY>

</HTML>

Using text predicates.

Predicates: Result Tree Fragments

XSLT 1.0 added result tree fragments to the data types supported by XPath. Result tree fragments were just tree fragments that you could assign to XSLT variables, and never were put to much use. About all you could do with them was to evaluate their string value. Support for them was removed in the XSLT 1.1 working draft, so presumably, they'll no longer be in use in XSLT 2.0.

Abbreviated Syntax for Predicates

You can abbreviate predicate expressions by omitting "position() =" if you like. For example, [position() = 3] becomes [3], [position() = last()] becomes [last()], and so on. Using the abbreviated syntax makes XPath expressions in predicates much easier to use. Here are some examples:

  • PLANET[2]. Returns the second <PLANET> child of the context node.
  • PLANET[last()]. Returns the last <PLANET> child of the context node.
  • /PLANETS/PLANET[2]/NAME[1]. Returns the first <NAME> element of the second <PLANET> element of the <PLANETS> element.
  • PLANET[5][@UNITS = "million miles"]. Returns the fifth <PLANET> child of the context node, only if that child has a UNITS attribute with value "million miles". Can also be written as PLANET[@UNITS = "million miles"][5].

That completes this look at the three parts of step patterns: axes, node tests, and predicates. They are the building blocks of match patterns. The best way to learn how to create patterns is by example, and many examples are coming up. First, though, it's important to cover one or two quick topics. As you may recall from the discussion of the formal definition of a match pattern, you can create patterns that match elements by ID or by key in addition to using step patterns.

Matching by ID

In addition to making patterns out of step patterns that specify an axis, node test, and predicate, you can also use the id() pattern to match elements that have a specific ID value. To use this pattern, you must give elements an ID attribute, and you must declare that attribute of type ID, as you can do in a DTD or schema. The following example rule adds the text of all elements that have the ID "favorite":

<xsl:template match = "id('favorite')">

    <H3><xsl:value-of select="."/></H3>

</xsl:template>

Here's what a DTD for planets.xml might look like that declares an ID and sets it to "favorite":

<?xml version="1.0"?>

<?xml-stylesheet type="text/xml" href="#stylesheet"?>

<!DOCTYPE PLANETS [                           

<!ELEMENT PLANET (CUSTOMER)*>                

<!ELEMENT CUSTOMER (NAME,MASS,RADIUS,DAY)>         

<!ELEMENT NAME (#PCDATA)>                  

<!ELEMENT MASS (#PCDATA)>                  

<!ELEMENT RADIUS (#PCDATA)>                      

<!ELEMENT DAY (#PCDATA)>                      

<!ATTLIST PLANET

    id ID #REQUIRED>

]>

<PLANETS>

    <PLANET id="favorite">

        <NAME>Mercury</NAME>

        <MASS UNITS="(Earth = 1)">.0553</MASS>

        <DAY UNITS="days">58.65</DAY>

        <RADIUS UNITS="miles">1516</RADIUS>

        <DENSITY UNITS="(Earth = 1)">.983</DENSITY>

        <DISTANCE UNITS="million miles">43.4</DISTANCE><!--At perihelion-->

    </PLANET>

        .

        .

        .

Some XSLT processors don't match by ID because they don't read DDS or XML schema. (This is one of the issues that XSLT 2.0 is supposed to address how to make ID information available.) However, there's another option: you can match by key.

Possible Support for IDREFs

In addition to making it easier to work with IDs, W3C is even considering adding support for IDREFs in XSLT 2.0. In particular, given an ID, the XSLT processor could provide you with a list of all elements that have an IDREF or IDREFS attribute that refers to that ID. (Note that currently, however, you can do the same thing using <xsl:key> and the "key()" pattern.)

Matching by Key

Keys give you an easy way to identify elements, and you can match specific keys with the pattern "key()". Chapter 9 discusses how to use keys in detail, but I'll include a quick example here.

You use the <xsl:key> element to create a key. This element is a top-level element, so it appears outside templates and as a child element of <xsl:stylesheet>. In the following example, I use a key to match planets whose COLOR attribute is set to "BLUE", which means Earth:

<?xml version="1.0"?>

<?xml-stylesheet type="text/xml" href="planets.xsl"?>

<PLANETS>

        .

        .

        .

    <PLANET COLOR="BLUE">

        <NAME>Earth</NAME>

        <MASS UNITS="(Earth = 1)">1</MASS>

        <DAY UNITS="days">1</DAY>

        <RADIUS UNITS="miles">2107</RADIUS>

        <DENSITY UNITS="(Earth = 1)">1</DENSITY>

        <DISTANCE UNITS="million miles">128.4</DISTANCE><!--At perihelion-->

    </PLANET>

</PLANETS>

Now I can create a key named COLOR that matches <PLANET> elements by checking their COLOR attribute:

<?xml version="1.0"?>

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:key name="COLOR" match="PLANET" use="@COLOR"/>

        .

        .

        .

Now I can use the pattern "key()" to match <PLANET> elements with the COLOR attribute set to "BLUE" as follows:

<?xml version="1.0"?>

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:key name="COLOR" match="PLANET" use="@COLOR"/>

    <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>

                    </TR>

                    <xsl:apply-templates select="key('COLOR', 'BLUE')"/>

                </TABLE>

            </BODY>

        </HTML>

    </xsl:template>

        .

        .

        .

And here's the result as you can see, Earth was the only planet that matched the pattern I used:

<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>

            </TR>

            <TR>

                <TD>Earth</TD>

                <TD>1 (Earth = 1)</TD>

                <TD>2107 miles</TD>

                <TD>1 days</TD>

            </TR>

        </TABLE>

    </BODY>

</HTML>

Using the Or Operator

Using the Or operator, |, you can match to a number of possible patterns, which is very useful when your documents get a little more involved. In the following example, I want to display <NAME> and <MASS> elements in bold, which I do with the HTML <B> tag. To match either <NAME> or <MASS> elements, I'll use the Or operator in a new rule:

<?xml version="1.0"?>

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/PLANETS">

        <HTML>

            <HEAD>

            .

            .

            .

            </BODY>

        </HTML>

    </xsl:template>

       <xsl:template match="PLANET">

       <TR>

          <TD><xsl:apply-templates select="NAME"/></TD>

          <TD><xsl:apply-templates select="MASS"/></TD>

          <TD><xsl:apply-templates select="RADIUS"/></TD>

          <TD><xsl:apply-templates select="DAY"/></TD>

       </TR>

   </xsl:template>

       <xsl:template match="NAME | MASS">

        <B>

            <xsl:apply-templates/>

        </B>

    </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:stylesheet>

Here are the results; note that the name and mass values are both enclosed in <B> elements:

<HTML>

    <HEAD>

        <TITLE>

            The Planets Table

        </TITLE>

    </HEAD>

    <BODY>

        .

        .

        .

            <TR>

                <TD><B>Mercury</B></TD>

                <TD><B>.0553</B></TD>

                <TD>1516 miles</TD>

                <TD>58.65 days</TD>

            </TR>

            <TR>

                <TD><B>Venus</B></TD>

                <TD><B>.815</B></TD>

                <TD>3716 miles</TD>

                <TD>116.75 days</TD>

            </TR>

            <TR>

                <TD><B>Earth</B></TD>

                <TD><B>1</B></TD>

                <TD>2107 miles</TD>

                <TD>1 days</TD>

            </TR>

        </TABLE>

    </BODY>

</HTML>

You can use any valid pattern with the | operator, such as expressions like "PLANET | PLANET//NAME", and you can use multiple | operators, like "NAME | MASS | DAY", and so on.


Rate this article on a scale of 1 to 10

Your vote :  


 

Recent Jobs

Integration Specialist Needed - Wor
Virtualization Server Infrastructur
A great opportunity to Digital Vide
here is a greate opportunity as a S
A great opportunity as a Network En

View all Jobs (Add yours)
View all CV (Add yours)




swimming pool contractor
chicago web site design
halloween masks
Web Hosting
help desk support
Christian Dior sunglasses
answering service


    Email TopXML  

Front Page Daily Stuff TopXML Forum XML blogs XML Newsgroups BizTalk Biztalk Utilities Biztalk Utilities Tutorial B2B SAP XML Microsoft .NET Dotnet System XML Soapformatter SQLXML XMLserializer XQuery PHP PHP SimpleXML PHP XML Dom PHP XML RPC PHP XSLT Java Java Java XML Xalan Microsoft ASP ASP Schemas XML SQL Server XML XMLDom XSL XSL Tutorial XSLT Stylesheets General Javascript CSS XHTML WAP