Understanding AS3 and XML

When you are about to design a Flash application using dynamic content, such as an image gallery or a tile based game, XML is one of the best ways to handle external content because it’s very easy to create, understand, read, and update.

AS3 now uses a system called ECMAScript for XML (E4X) that will do most of the dirty job, allowing us to focus on the application we are goingo to write.

From Wikipedia: ECMAScript for XML (E4X) is a programming language extension that adds native XML support to ECMAScript (which includes ActionScript, DMDScript, JavaScript, JScript). The goal is to provide an alternative to DOM interfaces that uses a simpler syntax for accessing XML documents. It also offers a new way of making XML visible. Before the release of E4X, XML was always accessed at an object level. E4X instead treats XML as a primitive (like characters, integers, and booleans). This implies faster access, better support, and acceptance as a building block (data structure) of a program.

So let’s start playing with it, and let me show you the one of the worst XML you can find… a collection of Sokoban levels (guess why?)

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
40
41
42
43
44
45
46
47
<?xml version="1.0" encoding="ISO-8859-1"?>
<SokobanLevels xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="SokobanLev.xsd">
  <Title>Fly</Title>
  <Description>Simple Microban-style levels.</Description>
  <Email>bsimpson_910@hotmail.com</Email>
  <LevelCollection Copyright="Tim LeFevre" MaxWidth="15" MaxHeight="11">
    <Level Id="The Fly_1" Width="11" Height="11">
      <L>   ####</L>
      <L>   #  #</L>
      <L>  ##  ####</L>
      <L>###.$.$. #</L>
      <L>#  $.$.$ ##</L>
      <L>#  .$@$.  #</L>
      <L>## $.$.$  #</L>
      <L> # .$.$.###</L>
      <L> ####  ##</L>
      <L>    #  #</L>
      <L>    ####</L>
    </Level>
    <Level Id="The Fly_2" Width="13" Height="11">
      <L>    ####</L>
      <L>    #  #</L>
      <L>  ###  ####</L>
      <L>### .$.$. #</L>
      <L>#   $.$.$ ###</L>
      <L>#   .$#$.   #</L>
      <L>### $.$.$   #</L>
      <L>  # .$.$. ###</L>
      <L>  ####  ###</L>
      <L>     # @#</L>
      <L>     ####</L>
    </Level>
    <Level Id="The Fly_3" Width="15" Height="11" Copyright="1106 1087 Jordi Doménech">
      <L>     ####</L>
      <L>    ##  ##</L>
      <L>    #    ###</L>
      <L>#####$.$.$ ###</L>
      <L>#   $.#.#.$  ##</L>
      <L>#    $.@.$    #</L>
      <L>##  $.#.#.$   #</L>
      <L> ### $.$.$#####</L>
      <L>   ###    #</L>
      <L>     ##  ##</L>
      <L>      ####</L>
    </Level>
  </LevelCollection>
</SokobanLevels>

This is one of the worst XML you can find because it has an unknown number of nodes (I don’t know how many levels has a level collection) and some nodes with multiple attributes.

But you’ll see parsing it with AS3 will be very easy anyway.

Let’s see how to read the xml file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.URLRequest;
	import flash.net.URLLoader;
	public class sokoxml extends Sprite {
		public function sokoxml() {
			var loader:URLLoader=new URLLoader;
			var req:URLRequest=new URLRequest("sokobanxml.xml");
			loader.addEventListener(Event.COMPLETE,on_xml_completed);
			loader.load(req);
		}
		public function on_xml_completed(event:Event):void {
			var xml_to_parse:XML=new XML(event.target.data);
			trace(xml_to_parse)
		}
	}
}

This would outputs the entire XML file in the same way it was created. Let’ see how does it work:

Lines 8-9: Setting the XML file to load

Line 10: Adding a listener to check if the XML has been completely loaded

Line 11: Loading the file

Line 14: Transforming the file in a XML variable. Not it’s ready to be parsed

The first things we want to look for in an XML file are elements. The elements method lists the elements of an XML object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.URLRequest;
	import flash.net.URLLoader;
	public class sokoxml extends Sprite {
		public function sokoxml() {
			var loader:URLLoader=new URLLoader  ;
			var req:URLRequest=new URLRequest("sokobanxml.xml");
			loader.addEventListener(Event.COMPLETE,on_xml_completed);
			loader.load(req);
		}
		public function on_xml_completed(event:Event):void {
			var xml_to_parse:XML=new XML(event.target.data);
			for each (var item:XML in xml_to_parse.elements()) {
				trace(item.name()+": "+item+"\n--------------");
			}
		}
	}
}

With the for each loop we can parse all elements even if we do not know how many of them we have in our XML file. The output is pretty similar to the original file because elements method does not care if an element has children.

Title: Fly
--------------
Description: Simple Microban-style levels.
--------------
Email: bsimpson_910@hotmail.com
--------------
LevelCollection: <LevelCollection Copyright="Tim LeFevre" MaxWidth="15" MaxHeight="11" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Level Id="The Fly_1" Width="11" Height="11">
    <L>####</L>
    <L>#  #</L>
    <L>##  ####</L>
    <L>###.$.$. #</L>
    <L>#  $.$.$ ##</L>
    <L>#  .$@$.  #</L>
    <L>## $.$.$  #</L>
    <L># .$.$.###</L>
    <L>####  ##</L>
    <L>#  #</L>
    <L>####</L>
  </Level>
  <Level Id="The Fly_2" Width="13" Height="11">
    <L>####</L>
    <L>#  #</L>
    <L>###  ####</L>
    <L>### .$.$. #</L>
    <L>#   $.$.$ ###</L>
    <L>#   .$#$.   #</L>
    <L>### $.$.$   #</L>
    <L># .$.$. ###</L>
    <L>####  ###</L>
    <L># @#</L>
    <L>####</L>
  </Level>
  <Level Id="The Fly_3" Width="15" Height="11" Copyright="1106 1087 Jordi Doménech">
    <L>####</L>
    <L>##  ##</L>
    <L>#    ###</L>
    <L>#####$.$.$ ###</L>
    <L>#   $.#.#.$  ##</L>
    <L>#    $.@.$    #</L>
    <L>##  $.#.#.$   #</L>
    <L>### $.$.$#####</L>
    <L>###    #</L>
    <L>##  ##</L>
    <L>####</L>
  </Level>
</LevelCollection>
--------------

So the first thing to do is to create a recursive function, because we don’t know if a child of an element has its own children, that can have children, and so on. That’s why I am approaching this problem with recursive functions.

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
package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.URLRequest;
	import flash.net.URLLoader;
	public class sokoxml extends Sprite {
		public function sokoxml() {
			var loader:URLLoader=new URLLoader  ;
			var req:URLRequest=new URLRequest("sokobanxml.xml");
			loader.addEventListener(Event.COMPLETE,on_xml_completed);
			loader.load(req);
		}
		public function on_xml_completed(event:Event):void {
			var xml_to_parse:XML=new XML(event.target.data);
			for each (var item:XML in xml_to_parse.elements()) {
				recursive(item);
			}
		}
		public function recursive(xml:XML) {
			trace("NAME: "+xml.name());
			trace("VALUE: "+xml.children());
			trace("-----------------");
		}
	}
}

As you can see, the result does not change…

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
40
41
42
43
44
45
46
47
48
49
50
NAME: Title
VALUE: Fly
-----------------
NAME: Description
VALUE: Simple Microban-style levels.
-----------------
NAME: Email
VALUE: bsimpson_910@hotmail.com
-----------------
NAME: LevelCollection
VALUE: <Level Id="The Fly_1" Width="11" Height="11" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <L>####</L>
  <L>#  #</L>
  <L>##  ####</L>
  <L>###.$.$. #</L>
  <L>#  $.$.$ ##</L>
  <L>#  .$@$.  #</L>
  <L>## $.$.$  #</L>
  <L># .$.$.###</L>
  <L>####  ##</L>
  <L>#  #</L>
  <L>####</L>
</Level>
<Level Id="The Fly_2" Width="13" Height="11" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <L>####</L>
  <L>#  #</L>
  <L>###  ####</L>
  <L>### .$.$. #</L>
  <L>#   $.$.$ ###</L>
  <L>#   .$#$.   #</L>
  <L>### $.$.$   #</L>
  <L># .$.$. ###</L>
  <L>####  ###</L>
  <L># @#</L>
  <L>####</L>
</Level>
<Level Id="The Fly_3" Width="15" Height="11" Copyright="1106 1087 Jordi Doménech" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <L>####</L>
  <L>##  ##</L>
  <L>#    ###</L>
  <L>#####$.$.$ ###</L>
  <L>#   $.#.#.$  ##</L>
  <L>#    $.@.$    #</L>
  <L>##  $.#.#.$   #</L>
  <L>### $.$.$#####</L>
  <L>###    #</L>
  <L>##  ##</L>
  <L>####</L>
</Level>
-----------------

Now it’s time to scan each element and see if it has children. The children method lists the children of the XML object in the sequence in which they appear. So, now for each element I am scanning for children, and for each child (if any), for children of that child, and so on.

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
package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.URLRequest;
	import flash.net.URLLoader;
	public class sokoxml extends Sprite {
		public function sokoxml() {
			var loader:URLLoader=new URLLoader  ;
			var req:URLRequest=new URLRequest("sokobanxml.xml");
			loader.addEventListener(Event.COMPLETE,on_xml_completed);
			loader.load(req);
		}
		public function on_xml_completed(event:Event):void {
			var xml_to_parse:XML=new XML(event.target.data);
			for each (var item:XML in xml_to_parse.elements()) {
				recursive(item);
			}
		}
		public function recursive(xml:XML) {
			trace("NAME: "+xml.name());
			if (xml.children()==xml) {
				trace("VALUE: "+xml.children());
				trace("-----------------");
			} else {
				trace("This node has children:");
				for each (var item:XML in xml.children()) {
					recursive(item);
				}
			}
		}
	}
}

Now the XML has been completely parsed, I can see if a node has children and list them properly

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
NAME: Title
VALUE: Fly
-----------------
NAME: Description
VALUE: Simple Microban-style levels.
-----------------
NAME: Email
VALUE: bsimpson_910@hotmail.com
-----------------
NAME: LevelCollection
This node has children:
NAME: Level
This node has children:
NAME: L
VALUE: ####
-----------------
NAME: L
VALUE: #  #
-----------------
NAME: L
VALUE: ##  ####
-----------------
NAME: L
VALUE: ###.$.$. #
-----------------
NAME: L
VALUE: #  $.$.$ ##
-----------------
NAME: L
VALUE: #  .$@$.  #
-----------------
NAME: L
VALUE: ## $.$.$  #
-----------------
NAME: L
VALUE: # .$.$.###
-----------------
NAME: L
VALUE: ####  ##
-----------------
NAME: L
VALUE: #  #
-----------------
NAME: L
VALUE: ####
-----------------
NAME: Level
This node has children:
NAME: L
VALUE: ####
-----------------
NAME: L
VALUE: #  #
-----------------
NAME: L
VALUE: ###  ####
-----------------
NAME: L
VALUE: ### .$.$. #
-----------------
NAME: L
VALUE: #   $.$.$ ###
-----------------
NAME: L
VALUE: #   .$#$.   #
-----------------
NAME: L
VALUE: ### $.$.$   #
-----------------
NAME: L
VALUE: # .$.$. ###
-----------------
NAME: L
VALUE: ####  ###
-----------------
NAME: L
VALUE: # @#
-----------------
NAME: L
VALUE: ####
-----------------
NAME: Level
This node has children:
NAME: L
VALUE: ####
-----------------
NAME: L
VALUE: ##  ##
-----------------
NAME: L
VALUE: #    ###
-----------------
NAME: L
VALUE: #####$.$.$ ###
-----------------
NAME: L
VALUE: #   $.#.#.$  ##
-----------------
NAME: L
VALUE: #    $.@.$    #
-----------------
NAME: L
VALUE: ##  $.#.#.$   #
-----------------
NAME: L
VALUE: ### $.$.$#####
-----------------
NAME: L
VALUE: ###    #
-----------------
NAME: L
VALUE: ##  ##
-----------------
NAME: L
VALUE: ####
-----------------

The last thing I have to do is checking for attributes. An element can have one or more attributes, or can have no attributes. Also, I don’t know their names and values. That’s when attribute method comes and saves me. attribute returns a list of attribute values for the given XML object.

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
package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.URLRequest;
	import flash.net.URLLoader;
	public class sokoxml extends Sprite {
		public function sokoxml() {
			var loader:URLLoader=new URLLoader  ;
			var req:URLRequest=new URLRequest("sokobanxml.xml");
			loader.addEventListener(Event.COMPLETE,on_xml_completed);
			loader.load(req);
		}
		public function on_xml_completed(event:Event):void {
			var xml_to_parse:XML=new XML(event.target.data);
			for each (var item:XML in xml_to_parse.elements()) {
				recursive(item);
			}
		}
		public function recursive(xml:XML) {
			trace("NAME: "+xml.name());
			for each (var att:XML in xml.attributes()) {
				trace("Attribute: "+att.name()+" = "+att);
			}
			if (xml.children()==xml) {
				trace("VALUE: "+xml.children());
				trace("-----------------");
			} else {
				trace("This node has children:");
				for each (var item:XML in xml.children()) {
					recursive(item);
				}
			}
		}
	}
}

And finally the XML has been fully parsed, attributes included

NAME: Title
VALUE: Fly
-----------------
NAME: Description
VALUE: Simple Microban-style levels.
-----------------
NAME: Email
VALUE: bsimpson_910@hotmail.com
-----------------
NAME: LevelCollection
Attribute: Copyright = Tim LeFevre
Attribute: MaxWidth = 15
Attribute: MaxHeight = 11
This node has children:
NAME: Level
Attribute: Id = The Fly_1
Attribute: Width = 11
Attribute: Height = 11
This node has children:
NAME: L
VALUE: ####
-----------------
NAME: L
VALUE: #  #
-----------------
NAME: L
VALUE: ##  ####
-----------------
NAME: L
VALUE: ###.$.$. #
-----------------
NAME: L
VALUE: #  $.$.$ ##
-----------------
NAME: L
VALUE: #  .$@$.  #
-----------------
NAME: L
VALUE: ## $.$.$  #
-----------------
NAME: L
VALUE: # .$.$.###
-----------------
NAME: L
VALUE: ####  ##
-----------------
NAME: L
VALUE: #  #
-----------------
NAME: L
VALUE: ####
-----------------
NAME: Level
Attribute: Id = The Fly_2
Attribute: Width = 13
Attribute: Height = 11
This node has children:
NAME: L
VALUE: ####
-----------------
NAME: L
VALUE: #  #
-----------------
NAME: L
VALUE: ###  ####
-----------------
NAME: L
VALUE: ### .$.$. #
-----------------
NAME: L
VALUE: #   $.$.$ ###
-----------------
NAME: L
VALUE: #   .$#$.   #
-----------------
NAME: L
VALUE: ### $.$.$   #
-----------------
NAME: L
VALUE: # .$.$. ###
-----------------
NAME: L
VALUE: ####  ###
-----------------
NAME: L
VALUE: # @#
-----------------
NAME: L
VALUE: ####
-----------------
NAME: Level
Attribute: Id = The Fly_3
Attribute: Width = 15
Attribute: Height = 11
Attribute: Copyright = 1106 1087 Jordi Doménech
This node has children:
NAME: L
VALUE: ####
-----------------
NAME: L
VALUE: ##  ##
-----------------
NAME: L
VALUE: #    ###
-----------------
NAME: L
VALUE: #####$.$.$ ###
-----------------
NAME: L
VALUE: #   $.#.#.$  ##
-----------------
NAME: L
VALUE: #    $.@.$    #
-----------------
NAME: L
VALUE: ##  $.#.#.$   #
-----------------
NAME: L
VALUE: ### $.$.$#####
-----------------
NAME: L
VALUE: ###    #
-----------------
NAME: L
VALUE: ##  ##
-----------------
NAME: L
VALUE: ####
-----------------

This was the hardest situation, when you don’t exactly know the structure of your XML tree.

But a good PROgrammer always knows the structure of the data he’s playing with, so next time we’ll see how to parse this sokoban level in order to draw it on the screen.

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (11 votes, average: 4.73 out of 5)
Loading ... Loading ...
If you found this post useful, please consider a small donation.
» Flash Templates provided by Template Monster are pre-made web design products developed using Flash technology.
They can be easily customized to meet the unique requirements of your project.

5 Responses

  1. jeanpier says:

    Good post Emanuele! I think this is a very interesting post.
    You can use XML as a var inside the flash movie too so you don’t have to load externally.
    Maybe some default levels are in game and extra levels loaded from server so users can contribute with levels to the game and the game can be played still if the “XML server” is down.

  2. But of course, loading things at runtime is a bad practice if you cannot store more than 1 file on a server (I use Flex and the Embed metatag)

  3. [...] one of those bookmarks, hopefully it will help some of you Learn To Use XML With Actionscript 3.0. view article Bookmark this [...]

  4. Leandro says:

    Great! I have searched for this about a good time…
    I’m working with result from API yahoo and it code is terrible! Your exemple done, but for some reason the tags are changing between name and values…
    Do you imagine what’s happening?
    If you want and can I would send data to you see.
    Thanks.

Leave a Reply

flash games company