Traversing XML structures

Flash Player 9 and later, Adobe AIR 1.0 and later

One of the powerful features of XML is its ability to provide complex, nested data via a linear string of text characters. When you load data into an XML object, ActionScript parses the data and loads its hierarchical structure into memory (or it sends a run-time error if the XML data is not well formed).

The operators and methods of the XML and XMLList objects make it easy to traverse the structure of XML data.

Use the dot (.) operator and the descendent accessor (..) operator to access child properties of an XML object. Consider the following XML object:

var myXML:XML =  
    <order> 
        <book ISBN="0942407296"> 
            <title>Baking Extravagant Pastries with Kumquats</title> 
            <author> 
                <lastName>Contino</lastName> 
                <firstName>Chuck</firstName> 
            </author> 
            <pageCount>238</pageCount> 
        </book> 
        <book ISBN="0865436401"> 
            <title>Emu Care and Breeding</title> 
            <editor> 
                <lastName>Case</lastName> 
                <firstName>Justin</firstName> 
            </editor> 
            <pageCount>115</pageCount> 
        </book> 
    </order>

The object myXML.book is an XMLList object containing child properties of the myXML object that have the name book. These are two XML objects, matching the two book properties of the myXML object.

The object myXML..lastName is an XMLList object containing any descendent properties with the name lastName. These are two XML objects, matching the two lastName of the myXML object.

The object myXML.book.editor.lastName is an XMLList object containing any children with the name lastName of children with the name editor of children with the name book of the myXML object: in this case, an XMLList object containing only one XML object (the lastName property with the value "Case").

Accessing parent and child nodes

The parent() method returns the parent of an XML object.

You can use the ordinal index values of a child list to access specific child objects. For example, consider an XML object myXML that has two child properties named book. Each child property named book has an index number associated with it:

myXML.book[0] 
myXML.book[1]

To access a specific grandchild, you can specify index numbers for both the child and grandchild names:

myXML.book[0].title[0]

However, if there is only one child of x.book[0] that has the name title, you can omit the index reference, as follows:

myXML.book[0].title

Similarly, if there is only one book child of the object x, and if that child object has only one title object, you can omit both index references, like this:

myXML.book.title

You can use the child() method to navigate to children with names based on a variable or expression, as the following example shows:

var myXML:XML =  
        <order> 
            <book> 
                <title>Dictionary</title> 
            </book> 
        </order>; 
 
var childName:String = "book"; 
 
trace(myXML.child(childName).title) // output: Dictionary

Accessing attributes

Use the @ symbol (the attribute identifier operator) to access attributes in an XML or XMLList object, as shown in the following code:

var employee:XML =  
    <employee id="6401" code="233"> 
        <lastName>Wu</lastName> 
        <firstName>Erin</firstName> 
    </employee>; 
trace(employee.@id); // 6401

You can use the * wildcard symbol with the @ symbol to access all attributes of an XML or XMLList object, as in the following code:

var employee:XML =  
    <employee id="6401" code="233"> 
        <lastName>Wu</lastName> 
        <firstName>Erin</firstName> 
    </employee>; 
trace(employee.@*.toXMLString());  
// 6401 
// 233

You can use the attribute() or attributes() method to access a specific attribute or all attributes of an XML or XMLList object, as in the following code:

var employee:XML =  
    <employee id="6401" code="233"> 
        <lastName>Wu</lastName> 
        <firstName>Erin</firstName> 
    </employee>; 
trace(employee.attribute("id")); // 6401 
trace(employee.attribute("*").toXMLString());  
// 6401 
// 233 
trace(employee.attributes().toXMLString());  
// 6401 
// 233

Note that you can also use the following syntax to access attributes, as the following example shows:

employee.attribute("id") 
employee["@id"] 
employee.@["id"]

These are each equivalent to employee.@id. However, the syntax employee.@id is the preferred approach.

Filtering by attribute or element value

You can use the parentheses operators— ( and ) —to filter elements with a specific element name or attribute value. Consider the following XML object:

var x:XML =  
    <employeeList> 
        <employee id="347"> 
            <lastName>Zmed</lastName> 
            <firstName>Sue</firstName> 
            <position>Data analyst</position> 
        </employee> 
        <employee id="348"> 
            <lastName>McGee</lastName> 
            <firstName>Chuck</firstName> 
            <position>Jr. data analyst</position> 
        </employee> 
    </employeeList>

The following expressions are all valid:

  • x.employee.(lastName == "McGee")—This is the second employee node.

  • x.employee.(lastName == "McGee").firstName—This is the firstName property of the second employee node.

  • x.employee.(lastName == "McGee").@id—This is the value of the id attribute of the second employee node.

  • x.employee.(@id == 347)—The first employee node.

  • x.employee.(@id == 347).lastName—This is the lastName property of the first employee node.

  • x.employee.(@id > 300)—This is an XMLList with both employee properties.

  • x.employee.(position.toString().search("analyst") > -1)—This is an XMLList with both position properties.

If you try to filter on attributes or elements that do not exist, an exception is thrown. For example, the final line of the following code generates an error, because there is no id attribute in the second p element:

var doc:XML =  
            <body> 
                <p id='123'>Hello, <b>Bob</b>.</p> 
                <p>Hello.</p> 
            </body>; 
trace(doc.p.(@id == '123'));

Similarly, the final line of following code generates an error because there is no b property of the second p element:

var doc:XML =  
            <body> 
                <p id='123'>Hello, <b>Bob</b>.</p> 
                <p>Hello.</p> 
            </body>; 
trace(doc.p.(b == 'Bob'));

To avoid these errors, you can identify the properties that have the matching attributes or elements by using the attribute() and elements() methods, as in the following code:

var doc:XML =  
            <body> 
                <p id='123'>Hello, <b>Bob</b>.</p> 
                <p>Hello.</p> 
            </body>; 
trace(doc.p.(attribute('id') == '123')); 
trace(doc.p.(elements('b') == 'Bob'));

You can also use the hasOwnProperty() method, as in the following code:

var doc:XML =  
            <body> 
                <p id='123'>Hello, <b>Bob</b>.</p> 
                <p>Hello.</p> 
            </body>; 
trace(doc.p.(hasOwnProperty('@id') && @id == '123')); 
trace(doc.p.(hasOwnProperty('b') && b == 'Bob'));

Using the for..in and the for each..in statements

ActionScript 3.0 includes the for..in statement and the for each..in statement for iterating through XMLList objects. For example, consider the following XML object, myXML, and the XMLList object, myXML.item. The XMLList object, myXML.item, consists of the two item nodes of the XML object.

var myXML:XML =  
    <order> 
        <item id='1' quantity='2'> 
            <menuName>burger</menuName> 
            <price>3.95</price> 
        </item> 
        <item id='2' quantity='2'> 
            <menuName>fries</menuName> 
            <price>1.45</price> 
        </item> 
    </order>;

The for..in statement lets you iterate over a set of property names in an XMLList:

var total:Number = 0; 
for (var pname:String in myXML.item) 
{ 
    total += myXML.item.@quantity[pname] * myXML.item.price[pname]; 
}

The for each..in statement lets you iterate through the properties in the XMLList:

var total2:Number = 0; 
for each (var prop:XML in myXML.item) 
{ 
    total2 += prop.@quantity * prop.price; 
}