Jun
05
2009

Using XML in ActionScript 3 with E4X

XML is an industry standard for data exchange between various applications written in different development languages and running on different operating systems. It allows describing complex and hierarchical data in simple and logical terms. ActionScript 3 comes with E4X (ECMAScript for XML) which is a programming language extension that adds simpler and easier to read approach for working with XML. In this article I concentrate on how to load, parse and modify XML data from Flash.

Quick Tip - loading and reading an XML file

Lets say that you want to create a music player that reads an XML playlist file to get the track, artist and album names and paths to the corresponding mp3 files. Also each track has the attribute with rating for each track. An example XML below:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<playlist>
<track rating="9">
<title>The Smile</title>
<artist>Schiller</artist>
<album>Day And Night</album>
<location>d:\music\schiller - The Smile.mp3</location>
</track>
</playlist>

To read the XML file we use the same methods as when reading a text file: URLLoader, URLRequest and we listen for a Complete event. When XML is loaded, we assign the XML source data to an XML object. With XMLList object we create an array of XML elements. This way we can loop through each node.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var myLoader:URLLoader = new URLLoader();
var xmlData:XML;

myLoader.addEventListener(Event.COMPLETE, readMyXML);
myLoader.load(new URLRequest("myPlayList.xml"));

function readMyXML(e:Event):void
{
xmlData = new XML(e.target.data);

var playList:XMLList = xmlData.track;

for(var i:int = 0; i<myPlayList.length(); i++)
{
trace("**************");
trace("Track number: "+(i+1));
trace("Rating: "+playList[i].attribute("rating"));
trace("Track Name: "+playList[i].title);
trace("Artist: "+playList[i].artist);
trace("Album Name: "+playList[i].album);
trace("Location: "+playList[i].location);
}
}

0. Article contents

  1. Understanding XML
  2. Loading XML
  3. Creating an XML object
  4. Reading XML
  5. Adding elements to an XML object
  6. Removing elements and attributes from an XML object

1. Understanding XML

The XML is a hierarchical data structure containing various components. The main XML components are:

  • XML declaration - information about XML document themselves like encoding type.
  • Root node - this is a must have for every XML document. Root node opens and closes the XML tree.
  • Elements - the units of XML data which begin and end with a starting and ending tag, like <track></track>. Elements can contain other elements (child elements). There is also an empty element tag, like <track/>.
  • Attributes - the data nodes within the opening element tag: <track rating="10"></track>
  • Text nodes - the optional text content that is nested between the opening and closing tags of element.
  • XML tree - the hierarchy of nodes in XML data

Illustration below shows XML components in our example XML playlist:

XML  components illustration


2. Loading XML

Loading an XML file is similar to loading a text file (read article here). We also need to use URLLoader and URLRequest objects to load the external file. To make sure the loaded file is a text file (not binary) you can set dataFormat property of URLLoader as URLLoaderDataFormat.TEXT. When loading process is complete and we know that by listening for a COMPLETE event, we try to parse loaded data to an XML object. If anything goes wrong, for example file wasn't formatted correctly, catch will print the error to output window.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLLoaderDataFormat;

public class MyXMLLoader
extends Sprite

{
var myLoader:URLLoader = new URLLoader();
var xmlData:XML;

public function MyXMLLoader()
{
//make sure
the downloaded data is received as a text

myLoader.dataFormat = URLLoaderDataFormat.TEXT;
//when loading
complete, run loadingComplete function

myLoader.addEventListener(Event.COMPLETE,
loadingComplete);

//start
loading process

myLoader.load(new URLRequest("myPlayList.xml"));
}

function loadingComplete(e:Event):void
{
//try to parse
loaded data to an XML object

try
{
var xmlData = new XML(e.target.data);
//if parsed
correctly, trace the XML tree

trace(xmlData.toXMLString());
}
catch(error:TypeError)
{
trace("Couldn't
parse source to XML, error: "
+error.message);

}
}
}
}

 

Note that if you want to load XML from a different domain (i.e. different web server) you may need to allow access with crossdomain.xml file (same story as with external swfs from other domains).

To get an XML from a remote source like PHP or .NET call use the code below:

this.urlSendVars.sendAndLoad("http://yourdomain.com/yourphpfile.php", 
this.urlResultVars,
"GET");

3. Creating an XML object

XML can be created from ActionScript 3 using XML object directly inside the code. Note that there is no need for the quotation marks, Flash compiler automatically recognises that you define the XML content. Quotation marks would suggest to Flash that this is a String not an XML data.

1
2
3
4
5
6
7
8
9
10
var playlist:XML = <playlist>
<track rating="8">
<title>Herzschlag</title>
<artist>Schiller</artist>
<album>Sehnsucht</album>
<location>d:\Schiller - Herzschlag.mp3</location>
</track>
</playlist>;

trace(playlist.track[0].title); //prints song title: Herzschlag

 

You can also create an XML passing String to the XML constructor object. This way you can add some dynamic data to the string if necessary.

1
2
3
4
5
6
7
8
var trackTitle:String = "More";

var myStringXML:String = "<playlist><track rating=\"8\"><title>"+trackTitle+"</title></track></playlist>";

var playlist:XML = new XML(myStringXML)

trace(playlist.track[0].title); //prints "More"
trace(playlist.track[0].@rating); //prints "8"

Alternatively, to populate dynamic data inside of the XML literal use curly braces:

1
2
3
4
5
6
7
8
9
10
11
var trackTitle:String = "I've Seen it 
All"
;

var trackRating:uint = 7;

var playlist:XML = <playlist>
<track rating={trackRating}>
<title>{trackTitle}</title>
</track>
</playlist>;

trace(playlist.track[0].title); //prints "I've seen it all"
trace(playlist.track[0].@rating); //prints "7"

4. Reading XML

I'm going to use the XML document below for our examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var xmlData:XML;

var playlist:XML = <playlist>
<track rating="6">
<title>Porque Te Vas</title>
<album>Sensucht</album>
</track>
<track rating="7">
<title>More</title>
<album>Tag Und Nacht</album>
</track>
<track rating="7">
<title>Traume</title>
<album>Weltreise</album>
</track>
</playlist>;

 

To read the entire XML document use toXMLString() method. This will print all XML elements including root node, text nodes and corresponding attributes if any.

trace(playlist.toXMLString());

 

To read the root node use name() method.

trace(playlist.name()); //returns playlist

 

To read the children of the parent element use children() method. Above code will print children of second track only.

trace(playlist.track[1].children());

Prints:

<title>More</title>
<album>Tag Und Nacht</album>

 

You can also use XMLList object which differs to XML object in that it can contain one or more XML objects or elements. An XML object is a single root object as it contains only one root node.

To read a text node of particular element not knowing it's name use child() method. This will trace in our case title of the first track: Porque Te Vas.

var myList:XMLList = playlist.track[0].child(0);

trace(myList);

 

If you know the name of the node, in our case "title" you can read the text node as below. This will also trace: Porque Te Vas.
trace(playlist.track[0].title);

 

To read an XML element's attribute use the @ symbol. A code below will output 6 which is the rating for track one.

trace(playlist.track[0].@rating);

 

To read an attribute without knowing its name use wildcard operator.

trace(playlist.track[0].@*[0]); //will trace first attribute of the first track 
(in our case 6)

trace(playlist.track[0].@*); //will trace all attributes of the first track (if
there are more than one)

trace(playlist.track[0].@*.length()); //will trace number of attributes in the
fist track (in our case 1)

 

To read only "titles" elements from the all "track" elements, use for-each loop and walk through using elements() method:
for each (var element:XML in playlist.elements())
{
trace(element.title);
}

this will print only titles:

Porque Te Vas
More
Traume

 

To read only the "titles" from the all "tracks" you can also use double dot operator. Note that you don't even need to know how deep the "title" elements are within the XML tree. This method works for any level of nesting.

trace(playlist..title);

Will print:

<title>Porque Te Vas</title>
<title>More</title>
<title>Traume</title>

 

To read only "titles" of "tracks" that have rating equal to 7 use the below code:

var myList:XMLList = playlist.track.(@rating == 7).title;

trace(myList.toXMLString());

Above will print only two tracks with rating equal to 7:

<title>More</title>
<title>Traume</title>

5. Adding elements to an XML object

To add a new element to an existing XML object, use the dot operator. Note that if the same element already exists, it will be overwritten.

1
2
3
4
5
6
7
8
9
var playlist:XML = <playlist>
<track rating="4">
<title>Porque Te Vas</title>
</track>
</playlist>;

playlist.track.album = "Sehnsucht";

trace(playlist.track[0].toXMLString());

 

In the code above, playlist.track.album = "Sehnsucht" creates a new element Album and a text node within it. Traced output:

<track rating="6">
<title>Porque Te Vas</title>
<album>Sehnsucht</album>
</track>

 

You can also create an empty element this way:

playlist.track.album = "";

or

playlist.track.album = <album />

 

To create an element attribute use @. Note that if track element already has that attribute, it will be replaced.

playlist.track.@rating = "10";

 

Creating a new track element without replacing the first one:

1
2
3
4
5
6
7
8
9
10
11
var playlist:XML = <playlist>
<track rating="6">
<title>Porque Te Vas</title>
</track>
</playlist>;

playlist.track[1] = <track />;
playlist.track[1].title = "Breathe";
playlist.track[1].@rating = 7;

trace(playlist.toXMLString());

 

Gives as the output:

<playlist>
<track rating="6">
<title>Porque Te Vas</title>
</track>
<track rating="7">
<title>Breathe</title>
</track>
</playlist>

 

To have more control over adding the new elements, you can use insertChildAfter() and insertChildBefore(). Both methods take two parameters, the inserting point (an existing XML node) and a new data to insert.

Lets say we want to add a new track element between existing two.

1
2
3
4
5
6
7
8
9
10
11
12
var playlist:XML = <playlist>
<track rating="6">
<title>Porque Te Vas</title>
</track>
<track rating="6">
<title>Breathe</title>
</track>
</playlist>;

playlist.insertChildBefore(playlist.track[1], <track/>);

trace(playlist.toXMLString());

 

Gives: (<track /> is a new empty "track" element.)

<playlist>
<track rating="6">
<title>Porque Te Vas</title>
</track>
<track/>
<track rating="6">
<title>Breathe</title>
</track>
</playlist>

 

Other way to do this is to use prependChild() and appendChild(), where the first method adds the new element as a first one and second as the last one.

1
2
3
4
5
6
7
8
9
10
11
12
var playlist:XML = <playlist>
<track rating="6">
<title>Porque Te Vas</title>
</track>
<track rating="6">
<title>Breathe</title>
</track>
</playlist>;

playlist.prependChild(<track rating="10"></track>);

trace(playlist.toXMLString());

 

What gives: (<track rating="10"/> was added as a first element)

<playlist>
<track rating="10"/>
<track rating="6">
<title>Porque Te Vas</title>
</track>
<track rating="6">
<title>Breathe</title>
</track>
</playlist>

 

If you don't know the name of the new element yet, you can make it dynamic using alternative bracket notation:

1
2
3
4
5
6
7
8
9
10
var playlist:XML = <playlist>

<track rating="6">


<title>Porque Te Vas</title>


</track>

</playlist>;

var elementName:String = "album";
playlist.track[elementName] = "Sensucht";

trace(playlist.toXMLString());

 

what gives:

<playlist>
<track rating="6">
<title>Porque Te Vas</title>
<album>Sensucht</album>
</track>
</playlist>



6. Removing elements and attributes from an XML object

Removing "rating" attribute from the first "track" element:

delete playlist.track[0].@rating;

 

Removing "rating" attribute from the all "track" elements:

delete playlist.track.@rating;

Removing all attributes from the all "track" elements:

delete playlist.track.@*;

 

Removing the "title" element from the first "track" element:

delete playlist.track[0].title;

 

Removing the "title" elements from all the "track" elements:

delete playlist.track.title;

 

Removing the whole track element with all its children:

delete playlist.track;

 

Removing the title "text" node from the first "track" element (the "title" element will be empty):

delete playlist.track.title.text()[0];

 

Conclusion

Although XML is a subject for many heavy books, I hope that this short article will help you implementing it in your Flash projects.

Comments 

 
0 #1 Dr Krish 2010-06-09 11:23
Hi there
Thanks for this great tutorial. Yet I wonder how to loop through nested xml data like this one.
--------------


Porque Te Vas
Sensucht

2 Minutes
Video Available



-------------------
hope there is way
appreciate your time and help.
krish
Quote
 

AS3 Tips

I'm with Adobe - Facebook group