Recipe 20.14.
Searching XML
Problem
You
want to search an XML
object for nodes or attributes that meet certain criteria.
Solution
Use the E4X syntax along with predicate
filtering on an XML object to pick out certain values
from an XML tree.
Discussion
This chapter examines how E4X syntax with
XML objects simplifies reading and writing values in an XML
tree. As simple as E4X is to use, it is also extremely powerful.
E4X syntax is similar to using XPath for searching XML
documents. If you are familiar with XPath concepts, using
the advanced features of E4X (such as its predicate filtering)
should come naturally. Predicate filtering allows you to pick out
element nodes that meet a certain Boolean expression condition
using the syntax .( condition ),
as you'll see later in this recipe.
Let's start by creating an XML object
from an XML literal:
var foodgroup:XML = <foodgroup>
<fruits>
<fruit color="red">Apple</fruit>
<fruit color="orange">Orange</fruit>
<fruit color="green">Pear</fruit>
<fruit color="red">Watermelon</fruit>
<servings>3</servings>
</fruits>
<vegetables>
<vegetable color="red">Tomato</vegetable>
<vegetable color="brown">Potato</vegetable>
<vegetable color="green">Broccoli</vegetable>
<servings>2</servings>
</vegetables>
</foodgroup>;
When you know the name of element nodes, you
simply dot down to reach them. For example, to return a list of all
of the <fruit> element nodes, use the following E4X
expression:
var fruitList:XMLList = foodgroup.fruits.fruit;
If you're interested in a particular
<fruit> element node, you can access the node by
specifying an index value using bracket notation:
var theApple:XML = foodgroup.fruits.fruit[0];
If you don't know (or care) about the full path
from the root node to the node (or nodes) for which you are
searching, use the double-dot operator to indicate that you want to
locate all matching nodes at any level in the XML tree. For
example, the following returns all <vegetable> nodes
regardless of where they are in the hierarchy:
var vegetableList:XMLList = foodgroup..vegetable;
An asterisk (*) is a wildcard for "any
node." For example, the following E4X expression returns assigns to
servings an XMLList containing all
<servings> element nodes that are children of any
nodes that are, in turn, children of the <foodgroup>
node:
var servings:XMLList = foodgroup.*.servings;
The @ sign is used to signify an
attribute. The following example generates an XMLList
containing the values of the color attributes for the
<fruit> nodes:
var colorValues:XMLList = foodgroup.fruits.fruit.@color;
Now let's look at predicate filtering. Predicate
filtering uses the syntax .( condition
) to pick out element nodes that meet the condition
specified. The condition is specified via a Boolean expression. The
filtering acts on the XML or XMLList object that
precedes the predicate filter expression.
For example, let's say you want to pick out all
of the <fruit> element nodes where the
color attribute is red. This can be accomplished
by first generating an XMLList of all of the
<fruit> element nodes using E4X dot-down syntax, and
then filtering with the Boolean expression @color ==
"red":
/* Displays:
<fruit color="red">Apple</fruit>
<fruit color="red">Watermelon</fruit>
*/
trace( foodgroup..fruit.( @color == "red" ) );
In this example, two things are happening in the
expression passed to the TRace statement:
-
The foodgroups..fruit portion returns
an XMLList of all of the <fruit> element
nodes that appear in the XML tree.
-
Predicate filtering is applied on the
XMLList of <fruit> nodes and a new
XMLList is created that contains only those
<fruit> elements matching the filtering expression,
in this case, the elements with color attributes equal to
red. You can see that both of the fruit element nodes with
red as the value of the color attribute appear in
the TRace output.
The preceding example selected the red
<fruit> element nodes, but what if we wanted to
select any node that had red as the value for the
color attribute? Use the asterisk to look for any node,
along with a predicate filter that looks for the existence of a
color attribute, and if the color attribute
exists, checks to make sure its value is red:
/* Displays:
<fruit color="red">Apple</fruit>
<fruit color="red">Watermelon</fruit>
<vegetable color="red">Tomato</vegetable>
*/
trace( foodgroup..*.( hasOwnProperty( "@color" ) && @color == "red" ) );
The Boolean expression used as the condition can
be any expression that results in a Boolean true or
false value. In the preceding example,
hasOwnProperty checks to make sure the element has an
attribute for color, and if so tests the value of the
color attribute to see if its value is red. Only
when the condition evaluates to true is the element added
to the XMLList that the E4X expression returns.
So far, predicate filtering has only been done
with attributes; however, it can also be used to specify that a
certain element node needs to have a particular text node as a
value. This is particularly useful when you have an XML document
that has repeated element nodes that contain child nodes. For
example, here is how to display the value of the color
attribute for whichever <fruit> element node has a
<name> element node with a text node value of
Apple:
var fruits:XML = <fruits>
<fruit color="red">
<name>Apple</name>
</fruit>
<fruit color="orange">
<name>Orange</name>
</fruit>
<fruit color="green">
<name>Pear</name>
</fruit>
<fruit color="red">
<name>Watermelon</name>
</fruit>
</fruits>;
// Displays: red
trace( fruits.fruit.(name == "Apple").@color );
As you can see, predicate filtering is quite
powerful, and it gets even more powerful when combined with regular
expressions. The following example uses a regular expression to
find all of the <fruit> element nodes that have a
<name> child node containing a text node starting
with a vowel:
var fruits:XML = <fruits>
<fruit color="red">
<name>Apple</name>
</fruit>
<fruit color="orange">
<name>Orange</name>
</fruit>
<fruit color="green">
<name>Pear</name>
</fruit>
<fruit color="red">
<name>Watermelon</name>
</fruit>
</fruits>;
/* Displays:
<fruit color="red">
<name>Apple</name>
</fruit>
<fruit color="orange">
<name>Orange</name>
</fruit>
*/
trace( fruits.fruit.( /^[aeiouAEIOU].*/.test( name ) ) );
The preceding code snippet creates a regular
expression (between the / and /) that in plain
English reads as "start with a vowel, upper- or lowercase, and be
followed by any character any number of times." The test( )
method is invoked on the regular expression to test it against the
parameter passed inin this case, the <name> element
node, which is converted to its text node value for the particular
<fruit> element being evaluated. Because the
test( ) method returns a Boolean value, it's safe to use as
part of predicate filter Boolean condition.
Regular expressions are covered in detail in
Chapter
13.
See Also
Recipes 20.7,
20.8,
20.9, and Chapter
13
|