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




ActionScript 3. 0 Cookbook
ActionScript 3.0 Cookbook: Solutions for Flash Platform and Flex Application Developers
ISBN: 0596526954
EAN: 2147483647
Year: 2007
Pages: 351

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