Recipe 20.14. Searching XMLProblemYou want to search an XML object for nodes or attributes that meet certain criteria. SolutionUse 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
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
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
var theApple:XML = foodgroup.fruits.fruit[0];
If you don't know (or care) about the full
var vegetableList:XMLList = foodgroup..vegetable;
An asterisk (
*
) is a wildcard for "any node." For example, the following E4X expression returns
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
/* 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
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 AlsoRecipes 20.7, 20.8, 20.9, and Chapter 13 |