XML support was first added to Flash
5, it is not supported on Flash 4. This tutorial explains how a Flash movie can retrieve data
from an external XML file using Flash MX or later version.
BACKGROUND
Why would there be a need to read from a file? Suppose you have a movie that
displays a "news of the day." You could put the
content of the news on the swf file, but if you do so, every time the news changes,
you'll need to edit the Flash file. That's very
impractical. So the benefits are: 1) To save time (avoid editing and recompiling of the Flash
file); and 2) to separate your
application from the data (encapsulation, implementation hiding).
You can use regular text file
to pass external data - see here.
Yet another way to pass external date is to embed the data using the
query string
or using FlashVars.
Why use XML? The benefit of using XML is that you can format
the data more elegantly; and also, since XML has been widely
used nowadays, the exact same XML file could potentially be used
for other purpose. Since XML is very good for representing
hierarchical form of data, so you avoid the messy format
required by using the other file format.
The main drawback of using XML is
the added complication in the ActionScript coding; and also there's
a bug some Flash 5 players that might not read XML file
the way intended (see more on this below).
REQUIREMENTS
To understand this tutorial, it's
recommended that you know:
How to access variables in Flash via a textbox.
Some general Flash Action Scripting knowledge and syntax.
Some basic XML knowledge (hierarchical structure, formatting).
This tutorial will be using Flash MX, but is also applicable on
Flash MX 2004. Flash 5 can also be used, but with caution - see note
below.
Note: This tutorial requires Flash
6/MX
plug-in/player to be installed to run properly. Although XML is supported from Flash 5;
there's a problem on some older Flash
5 player
that causes a movie not to be able to read some XML files correctly. This
problem occurred in Flash player version 5.0.41 and was fixed on
version 5.0.42. If
the example above does not run on your machine, you should
download the latest flash player from Macromedia. If you
need to support Flash 5 players, then see this
link
for some workarounds.
SAMPLE XML FILE
The XML file to be used in
this tutorial is called "news.xml" It can be
downloaded here: news.xml.
The structure of the XML file is shown below. Note that we're
not using a DTD because Flash will ignore it anyway.
Note that the root element of the XML file is <news>. A
<news> element contains a <header>, a <content>, and
an <info> element. An <info> element itself has a
"more_info" attribute, and contains a <comment> and an
<author> element.
Note about XML editing
If you use one of the special character below as a value of an element,
you should use the name entities:
Symbol
Name
Entities
>
>
"
"
'
'
&
&
<
<
For example, to replace the <header> with "Vacation
is Good & Healthy" then do it like this:
<header>Vacation is Good &
Healthy</header>
Not like this:
<header>Vacation is Good &
Healthy</header>
CREATING THE SKELETON FLASH MOVIE
Let's start with a sample Flash movie. This movie will
display a
"news of the day" movie using data from the above XML file. Here's what frame 1 looks like:
The movie has 4 textboxes:
the one which shows "header"
the one which shows "Loading data..."
the one which shows "author"
the text "My News Page" is a static textbox (we'll not
be doing much with this particular textbox)
Each textbox is a DynamicText, and is associated with a
variable name (specified in Var: in the property dialog, like
below).
The "header" textbox is associated with Var: header.
This box will hold the news <header>. Make sure that this
button is off so that
user can't select (highlight) the text. It will look weird if
you allow user to select the text.
The "Loading data" textbox is associated with Var:
content. This box will hold the news <content>.
The "author" textbox is associated with Var: author.
This box will hold the name of the news <author>.
We'll ignore the other XML elements such as <date> and
<comment> for now.
LOADING THE XML FILE IN
FLASH
Below is the code that loads the XML object
var xmlData=new XML();
xmlData.ignoreWhite=true;
xmlData.load("news.xml");
The first line creates an XML object. The
next line tells Flash to ignore white spaces encountered in between
XML elements. Generally, you'll always want to set this to true,
or Flash won't read the XML file correctly. The last line is
the code that actually loads the XML file.
It
is worth noting that when you load external file like this, you should
not make the assumption that the file is loaded immediately.
Because Flash does not know how large the file is, it usually runs
this kind of process in the background while the movie continues to
run. However, Flash is thoughtful enough to notify us when
the file is loaded by what is termed: event handlers.
In the XML case, the event handler is named onLoad (ie: when
the file is loaded, Flash will call onLoad). We will
implement onLoad code soon.
Note: for this
tutorial, the XML file must be located in the same domain as the swf
file. If you need to access an XML file from a different domain,
then you need to enable access across different domain by writing
a <cross-domain-policy> in the XML file. An example is as
follows:
If you need to do the cross-domain-policy, then you can find
more info on the Macromedia site.
PARSING THE XML FILE IN
FLASH
Now that we know how to load the file, it won't be much of a use
without knowing what to do with it. But before we process the
file, we need to know that the file is loaded. We do that by
overriding the onLoad event handler with our own function which
will process the XML file. Our handler function is called processXMLData.
So the code becomes:
function processXMLData(success)
{
if (success)
{
}
else
{
content="Today's news is not found";
}
}
var xmlData=new XML();
xmlData.ignoreWhite=true;
xmlData.onLoad=processXMLData;
xmlData.load("news.xml");
stop();
I also tell Flash to stop the movie because I don't
want to continue until processXMLData is called. (This
doesn't mean that you should always stop the movie after
loading. It really depends on your movie. In my simple
example, I do want to stop the movie because the movie can't do
anything else until the file is loaded).
Note
that we must put processXMLData beforethe onLoad=processXMLData
portion just to be safe.We will
write the actual code for processXMLData a bit later;
currently, it does nothing. The parameter success is a
boolean value that indicates whether the file is successfully loaded
or not. It will be false if the file is not found.
New, let's see how to access the XML data. When processXMLData is called, a reference
to the XML object is passed. This reference can be accessed
with the keyword "this." Let's see what it
looks like:
You can get this kind of output by running the
debugger and putting a breakpoint in processXMLData.
According to the diagram above, "this"
has 1 child nodes, which is named "news." This
corresponds to the <news> element in the XML
file. We already know from the XML file that the
<news> node should have 3 child nodes (<header>,
<content>, and <info>). Let's extend the tree
view and see.
Sure
enough, there they are. The order is always the same as in
the XML file. So, child[0] is for <header>, childNodes[1]
is for <content>, and childNodes[2] is for
<info>.
But why are all the nodeValues
null? This is the way the XML object in Flash works: the nodeValue
for an element is stored as a child node. So, the node value for the
<header> will be the child node of the "header"
node. We can get the value of the header like this:
newsNode=this.childNodes[0]; // get news node first
headerNode=newsNode.childNodes[0]; // get header node
header=headerNode.childNode[0].nodeValue // get value
If you've never used XML in Flash before, all this may
seem confusing. I suggest writing some code and try to access
some of the elements. The debugger is very helpful here because
you can see the tree view like above.
Once
you get used to the concept, you should be able to reference any
element in the XML file without going through this step by step
process. For now, we have enough information to use to code the
Flash movie.
function processXMLData(success)
{
if (success)
{
header=this.childNodes[0].childNodes[0].childNodes[0].nodeValue;
content=this.firstChild.childNodes[1].firstChild.nodeValue;
}
else
{
content="Today's news is not found";
}
}
var xmlData=new XML();
xmlData.ignoreWhite=true;
xmlData.onLoad=processXMLData;
xmlData.load("news.xml");
stop();
The way I'm accessing the nodes on that piece of code
doesn't seem very consistent, but that's ok, I'm doing things this way
to illustrate a purpose.
Run the movie, it
should be like this:
Cool.
Since we have named the textboxes with the name "header" and
"content," they automatically show the value we've just
assigned. So now we only need to add the code to print the
<author> node value.
The <author> node is
one level deeper than <title> and it is nested below
<info> node, so the long code to access the node is:
So let's write that code, and while at it, let's
rewrite the way we access the the nodes as well to make the code a bit
cleaner:
function processXMLData(success)
{
if (success)
{
var newsNode=this.firstChild;
var headerNode=newsNode.childNodes[0];
var contentNode=newsNode.childNodes[1];
var infoNode=newsNode.childNodes[2];
var authorNode=infoNode.childNodes[1];
header=headerNode.firstChild.nodeValue;
content=contentNode.firstChild.nodeValue;
author=authorNode.firstChild.nodeValue;
}
else
{
content="Today's news is not found";
}
}
var xmlData=new XML();
xmlData.ignoreWhite=true;
xmlData.onLoad=processXMLData;
xmlData.load("news.xml");
stop();
Gosh, that code is still quite messy. Our
XML file is quite simple, but what if someone forgot to put
the nodes in the right order? What if someone puts
<header> after <content>? Let's make the code
cleaner and be able to handle more nodes. Also, it will be a
nice XML practice.
Let's write a function to return a node.
function findNode(node, nodeName)
{
if (node.nodeName==nodeName)
return node;
for (var i=0; node.childNodes && i<node.childNodes.length; i++)
{
var foundNode=findNode(node.childNodes[i], nodeName);
if (foundNode!=null)
return foundNode;
}
return null;
}
function getValue(node)
{
if (node && node.firstChild)
return node.firstChild.nodeValue;
return "";
}
The findNode function check if the passed node
has the specified nodeName. If not, the code then
loops through the children of the node and check again (call the
same function). Eventually, the named node will either be
found or not. If not, then null will be
returned. (Note: This function would not work correctly if we
have multiple nodes with the same name on the XML file. For
that, you'll need to specify an index of which node to return - but this
is as far as I'll go for this tutorial.)
The getValue
function simply returns the value of a node. It's a shorthand
for typing
"firstChild.nodeValue". That
minimizes the mess that we get every time we just want to get
a value of a node.
So let's incorporate these changes:
function processXMLData(success)
{
if (success)
{
var rootNode=this.firstChild;
var headerNode=findNode(rootNode, "header");
header=getValue(headerNode);
var contentNode=findNode(rootNode, "content");
content=getValue(contentNode);
var authorNode=findNode(rootNode, "author");
author=getValue(authorNode);
}
else
{
content="Today's news is not found";
}
}
function findNode(node, nodeName)
{
if (node.nodeName==nodeName)
return node;
for (var i=0; node.childNodes && i<node.childNodes.length; i++)
{
var foundNode=findNode(node.childNodes[i], nodeName);
if (foundNode!=null)
return foundNode;
}
return null;
}
function getValue(node)
{
if (node && node.firstChild)
return node.firstChild.nodeValue;
return "";
}
var xmlData=new XML();
xmlData.ignoreWhite=true;
xmlData.onLoad=processXMLData;
xmlData.load("news.xml");
stop();
Before we go, let's try to use the "more_info"
attribute in the <info> node and print the value. Add a
textbox below "author" and set Var: moreInfo.
You
can get the attribute of a node in Flash using
node.attributes.attributeName, where attributeName is
"more_info" in our example.
function processXMLData(success)
{
if (success)
{
var rootNode=this.firstChild;
var headerNode=findNode(rootNode, "header");
header=getValue(headerNode);
var contentNode=findNode(rootNode, "content");
content=getValue(contentNode);
var authorNode=findNode(rootNode, "author");
author=getValue(authorNode);
var infoNode=findNode(rootNode, "info");
if (infoNode)
{
moreInfo=infoNode.attributes.more_info;
}
}
else
{
content="Today's news is not found";
}
}
// ...
// the rest of the code is unchanged and is not shown here