Implementing XPath for Wireless Devices
by Bilal Siddiqui
June 05, 2002
In this two-part article, I will discuss XPath and its use in Wireless
applications. In the first part, we will consider applying XPath to both XML and
its Wireless counterpart WBXML (Wireless Binary XML). We will also discuss the
design of an XPath processing engine suitable for small wireless devices. This
discussion will focus on XPath 1.0, which is a W3C recommendation.
What can XPath do for us?
XPath allows us to process XML and search for specific nodes in an XML
document. To a certain extent, XPath is to XML what SQL is to relational
databases, although the forthcoming W3C XML Query language takes that further.
This section gives a brief overview of XPath.
Listing 1 (opens in
a new window) is a Web Services Definition Language file. WSDL is an XML-based
grammar to describe Web Service interfaces. The root element in Listing 1 is
definitions. It has a name attribute, which indicates
the name of the web service that it is defining. In our case it is
BillingService.
While processing a WSDL file, we want to extract the name of a web
service. The following simple XPath query will extract the name:
./child::node()[1]/attribute::name
You can see the XPath query has three sections, separated by slashes. The
first section is only a dot (.). A dot at the start of a query means you want to
start your search from the beginning of the XML document.
The second section (child::node()[1]) says: Find the first
child element node. The combined effect of the first and second portions
(./child::node()[1]) is like saying: Find the first element of this
XML document.
The third section (attribute::name) says: Find the value of an
attribute name. The combined effect of the query is like saying:
Find value of the name attribute of the first element in the XML
document. Applying this query to the WSDL file of Listing 1 returns, BillingService.
The following query is similar to the one discussed above and finds the
target namespace of this WSDL file:
./child::node()[1]/attribute::targetNamespace
Now let's take this idea further and see a more complex example. Have a look
at the following XPath query and try to translate it into simple English:
./child::node()[1]/child::service/child::port/child::soap:address/attribute::location
In simple English, it says:
Find the first child node of the XML document, then find a
service child element within the first child, then find a
port element inside the service element, then look inside the
port element for an address element that belongs to
the SOAP namespace and return the value of its location attribute.
Important XPath definitions
Location step, context node, node-set and location path:
The parts of an XPath query are evaluated sequentially. Each part is referred
to as a location step. The results of the first location step
are given to the next location step and so on till you reach the end of the
query. The result of each location step is a set of nodes referred to as a
node-set. Each node in a node-set is known as a
context-node during evaluation.
In this way, every location step produces a node-set, which is handed over to
the next location step. Each node in the node-set will be treated as a
context-node and another node-set will be evaluated by the next location step
and so on. A series of location steps forms the complete location
path.
Axes and node-test:
Specifying a child after a slash means you want to look for child
elements. Specifying attribute after a slash means you are looking for
attributes. Child and attribute key words are called axes. An
axis specifies the direction of your search. Other XPath axes include
descendant (means you are not only interested in immediate child
elements but you are also interested in grand children, their children, and so
on), parent (immediate parent), ancestor (parent,
grand parent, great grand parent, and so on) etc.
You will also notice that every axis is followed by double colon mark (::,
referred to as a resolution operator in programming languages), which is
followed by the name of the node that we are looking for
(targetNamespace in attribute::targetNamespace). The
name that appears after the resolution operator is called a
node-test (a test that our required nodes will pass).
Abbreviated Syntax
The query ./child::node()[1]/attribute::name that we considered
earlier can also be written as ./child::node()[1]/@name. Here we
have abbreviated attribute::name as
@name. @ is the short name of the attribute axis.
Another common abbreviation is the omission of child axis. You don't need to
specify child axis at all because XPath takes child as the default
axis. Therefore, the following two queries will produce the same result:
./child::node()[1]/child::service/child::port/child::soap:address/attribute::location
./node()[1]/service/port/soap:address/attribute::location
What does an XPath query return?
The previous queries returned values of attributes. XPath can also return
XML elements. Let's apply the following XPath query to the WSDL file of Listing 1:
./node()[1]/child::message[@name="MonthNumber"]
This query looks for the first node (definitions element) in the
WSDL file. Then it looks for all children of the definitions
element that have the name message. There is a special condition
attached with the message element that the query looks for
([@name="MonthNumber"]). The condition states that the message
element should have an attribute named MonthNumber.
There are two message elements in the WSDL file of Listing 1. The query
returns the message element whose name attribute is
MonthNumber.
Multiple XPath queries working together
What if we want to find a message element with a dynamically evaluated name?
For example, in WSDL processing applications, we often need to look for a
message element, whose name attribute matches the
value of message attribute of an input or
output element.
In Listing 1, we have
one input element and two output elements, each of
which has a message attribute. The value of each of the message
attributes matches the name attribute of a message
element.
All input and output elements of Listing 1 reside inside two
operation elements. Each operation element has a
name attribute. The WSDL processing scenario provides us with the
name of an operation element and asks us to find the message element associated
with its input or output.
The following query performs this job for us, broken over two lines for
clarity:
./node()[1]/child::message[@name=//node()/portType/*[
@name="getBillForMonth"]/child::*/@message]
Compare this query with the last one. You will find that the only difference
is that the hard coded value "MonthNumber" has been replaced with another XPath
query:
//node()/portType/*[@name="getBillForMonth"]/child::*/@message
This example shows that XPath syntax allows us to combine multiple XPath
queries into a single complex query. This helps us in many real life
applications where the XML processing task requires finding particular
information and then, depending on the information read, jumping to another
location in the same XML file.
XPath for Wireless
XML works for wireless applications the same way it works for the wired
Web. The WSDL processing requirements inside a wireless client are the same as
those inside desktop clients.
However, in order to address the bandwidth limitation issues, WAP Forum has proposed a compressed version
of XML called Wireless Binary XML (WBXML), which is in use inside WAP-enabled
devices.
As we have already seen, XPath is designed to operate on the tree-like XML
structure. WBXML maintains the same abstract XML structure, although its
concrete expression differs, so there is no conceptual difference in applying an
XPath query to an XML file or its WBXML counterpart.
In order to demonstrate this point, let's examine how an XML document is
transformed to WBXML.
A sample XML to WBXML transformation
Consider the simple XML file of Listing 2. We have kept it very simple in order to cover only the
basics of WBXML.
A WBXML file starts with a WBXML version declaration. We are using WBXML
version 1.3, which is represented by a single byte 0x03. The full translation is
presented in Listing
3)
The next byte represents the DTD. We are working with an unknown DTD, which is
represented by 0x01.
The third byte is for the character set. We are using the US ASCII character
set, represented by 0x03 (third byte in WBXML stream of Listing 3).
The fourth byte is the length of the string table, a technique used to reuse
strings that occur at multiple places in an XML file. String tables can help in
better compression by writing a string in the table only once and then referring
to it from various places. We do not have any strings occurring more than once
in our simple XML file, so we will not use a string table; we record its length
as 0x00 (fourth byte of WBXML file).
Next is the operation element. We are using the following bytes (called tag
tokens) to represent our XML elements:
| Operation element | 0x05 |
| Input element | 0x06 |
| Output element | 0x07 |
WBXML assumes that all concerned parties know the meaning of tag tokens, in
the same as all concerned understand (or don't) the meaning of XML tags. So we
can directly use the tag token and assume that a tag token can be translated
back to the name of an element wherever needed.
Therefore, our next byte is 0x05 (operation element) followed by 0x06 (input
element).
Next is the string "My input" that needs to appear inline in a WBXML file.
0x03 marks the start of an inline string. A NULL character (0x00) marks the end
of the string.
Notice that the same byte value 0x03 was used earlier to mark the WBXML
version and character set declarations. This does not produce any ambiguity,
because the first three bytes of a WBXML file are reserved for XML, DTD, and
charset declarations. The actual WBXML data payload begins with the fourth
byte.
0x01 marks all end tags. The combination of start tags (0x05, 0x06 and 0x07)
and end tags (0x01) produce the same nested structure as that of the original
XML file.
The rest of the WBXML file follows the same logic. Readers can trace Listing
3 to follow the sequence. This example illustrates that WBXML reduces the size
of an XML fileq without changing its structure.
Design of a small footprint XPath engine
We will now consider the design of a small footprint XPath engine which
operates on top of the DOM. DOM implementations are available for major wireless
platforms, such as WinCE and Java 2 Micro Edition (J2ME).
Consider the two pseudo-code classes XPathExpression (Listing 4) and LocationStep (Listing 5). These two classes together
implement an XPath engine.
XPathExpression's
constructor takes two string type arguments: an XML file and an XPath
expression. It loads the XML file into a DOM object. It also tokenizes the XPath
expression string into an array of smaller strings, where each small string
represents an XPath location step. While tokenizing, it also translates
abbreviated XPath syntax into normal unabbreviated expressions.
The next step is to execute a loop in which each location step string is
passed to the LocationPath constructor, one after the other. After
the creation of a LocationPath object, its getResult
method is called.
Every call to the getResult method needs an array of nodes (a
node-set) as an argument. The getResult method will carry out the
search on each node in the node-set and return another array of nodes (another
node-set) that matches the search requirements of the location path.
The resulting node-set from each location step is fed to the next location
step, until all location steps in the XPath query are consumed.
Now let's have a look at what's happening inside the getResult
method of the LocationPath class. The constructor
has already resolved the location step into axis and node-test strings. The
getResult method will detect which axis we have to work with and
call the appropriate sequence of operations. We have only implemented the child
and descendant axes as a sample guideline. Parent and ancestor axes can also be
implemented in a similar manner.
Summary
In this article I presented various XPath queries and discussed how to apply
them to XML and WBXML formats. I also presented a basic design of an XPath
engine. Next time, we will take this design further and include support for
more XPath features.
Resources
- Read the official XPath specification at W3.org.
- Download this XPath Tester and
try XPath queries on XML files.
- We discussed WSDL in this article. Following two articles from the same
author provide WSDL authoring and processing details:
- Deploying Web
services with WSDL: Part 1
- Designing
a WSDL Client, Discovering Web Services
- Want to learn more about Compressing
XML?
-
Effective
XML processing with DOM and XPath in Java.
-
On XML.com, visit Bob DuCharme's Transforming XML column for more articles on XPath.
- kXML is an open source implementation
of SAX and DOM for J2ME.