Tutorials, extensions, and source files for ActionScript, Flash, and other Adobe products.

 

Pages: 1 | 2 | 3 | 4 | 5

XML (E4X)

E4X is a new approach (ECMAScript specification) for working XML data that has been integrated into ActionScript 3.  The default XML class now uses this implementation over that of the older XML classes.  The old XML class in ActionScript 3 has been renamed to XMLDocument (flash.xml.XMLDocument). The old XMLNode class is still XMLNode (flash.xml.XMLNode).

E4X provides a simpler, more intuitive method for accessing XML content using familiar dot syntax.  Instead of looping through all child nodes within your XML, you can reference elements by name and even filter nodes using simple conditions.  Additionally, XML can now be written inline in ActionScript.

// XML writtin inline in ActionScript 3
// no quote characters needed
var xml:XML = <inline>
			<example />
		  </inline>;

This makes XML much easier to use when embedded in ActionScript as it no longer needs to be written as a continuous string.

There are two primary, top level ActionScript classes that Flash uses to work with E4X XML; XML (XML) and XMLList (XMLList).  XML instances contain one root node which can contain any number of child nodes.  XMLList instances are a collection or list of XML instances or XML nodes.

Note: Inline XMLLists

Writing inline XML requires the use of a single root node to define the body of the XML document.  XMLLists in ActionScript have no root node; they're an array-like structure containing multiple XML objects. This makes inlining XMLLists difficult, but not impossible. The format uses non-standard XML - an unnamed XML node - to represent a root node substitution.

// Inline XMLList
var list:XMLList = 
    <>
        <item>
            <title>Item2</title>
        </item>
        <item>
            <title>Item2</title>
        </item>
    </>;
trace(list.length()); // 2

For any XML instance, you can reference an XMLList of its children using the children() method which is similar to the childNodes property within the older XML model.  E4X provides an additional option which allows you to reference children by name.  Instead of getting all children of an XML node, you can get an XMLList of element children with a specified name simply by using that name as a property of the XML instance with the XML dot (.) operator (child accessor).

// Child elements accessible as
// properties of XML instance
var sport:XML =
	<sport>
		<name>Basketball</name>
		<players>men</players>
		<players>women</players>
		<nationalTV>NBC</nationalTV>
		<nationalTV>ESPN</nationalTV>
	</sport>;

trace(sport.players is XMLList); // true
trace(sport.players);
/* output:
<players>men</players>
<players>women</players>
*/

By referencing players from the sport XML, an XMLList of all child elements with the name players is returned. If you then want to access the first node of that list, you would access it through the players XMLList with the array access operators ([]) and the index of the element as you would an array.

// Child elements accessible as
// properties of XML instance
var sport:XML =
	<sport>
		<name>Basketball</name>
		<players>men</players>
		<players>women</players>
		<nationalTV>NBC</nationalTV>
		<nationalTV>ESPN</nationalTV>
	</sport>;

trace(sport.players[0] is XML); // true
trace(sport.players[0]); // traces "men"

Notice that the trace output shows the text node within the first players node within the XMLList. Even though the players element is the actual reference for sport.players[0], the default trace output (toString()) is actually the text contents of that element.  If you want to see the full XML element, you will need to use XML.toXMLString().

// Show XML source string using toXMLString()
// default value (toString()) just shows text
var sport:XML =
	<sport>
		<name>Basketball</name>
		<players>men</players>
		<players>women</players>
		<nationalTV>NBC</nationalTV>
		<nationalTV>ESPN</nationalTV>
	</sport>;

trace(sport.players[0].toString()); // traces "men"
trace(sport.players[0].toXMLString()); // traces "<players>men</players>"

Note: XML.text()

The XML.text() returns the text contents of an XML element and is similar to the default (toString()) string value of that node.

Similar to the child accessing dot operator, E4X introduces a similar descendant accessor operator for accessing descendants, or children and all childrens children. This takes the form of two dot operators used in succession (..) followed by the name of the descendant element nodes you wish returned in an XMLList.

// Child (.) vs. descendant (..) access
var channel:XML =
	<channel>
		<title>Feed Title</title>
		<description>Feed description.</description>
		<item>
			<title>Item 1 Title</title>
			<description>Item 1 description.</description>
		</item>
		<item>
			<title>Item 2 Title</title>
			<description>Item 2 description.</description>
		</item>
	</channel>

trace(channel.title.toXMLString()); // traces "<title>Feed Title</title>"
trace(channel..title.toXMLString());
/* output
<title>Feed Title</title>
<title>Item 1 Title</title>
<title>Item 2 Title</title> */

You can see that in using child access the elements returned are only those within the first level of the hierarchy beneath the target element (here being channel).. The the descendant accessor all of the appropriately named elements beneath the target element are returned

Attributes in XML are also accessible via dot syntax. To differentiate element names from attribute names, attributes have an additional attribute identifier operator (@) prefixed to the attribute name.

// Access attributes with the @
// attribute identifier operator
var item:XML =
	<item name="attribute">
		<name>element</name>
	</item>;

trace(item.name); // traces "element"
trace(item.@name); // traces "attribute"

Attributes (which are technically also nodes) can also be returned in an XMLList

// Attributes as an XMLList
var images:XML =
	<images>
		<img href="image1.jpg" />
		<img href="image2.jpg" />
		<img href="image3.jpg" />
	</images>;

trace(images.img.@href is XMLList); // traces true
trace(images.img.@href); // traces "image1.jpgimage2.jpgimage3.jpg"
trace(images.img.@href[0]); // traces "image1.jpg"

Because you can reference elements (and attributes) by name using dot syntax with E4X XML, there's a greater likelihood that there will be a conflict between XML node names and real properties within the XML instance in ActionScript. To prevent this from happening properties of the XML class are instead methods.  You may have already noticed that the old childNodes property is now the children() method. As a method, you wouldn't have a conflict if you had XML with elements of the name children. Another example is the name() method which returns the name of the XML element node

// XML node access vs method access
var family:XML =
	<family>
		<name>Hendersons</name>
	</family>;

trace(family.name); // traces "Hendersons"
trace(family.name()); // traces "family"

Sometimes you may find that you have XML whose element names are not valid ActionScript variable names. If that is the case, you will need to use the XML.elements() method allowing you to access elements by name using a string.  For attributes a similar method XML.attribute() method exists.

// Reference nodes by name using 
// elements() and attribute()
var values:XML =
	<values for-example="see below">
		<complex-name>simple value</complex-name>
	</values>;

// trace(values.@for-example); // Syntax error
trace(values.attribute("for-example")); // traces "see below"
// trace(values.complex-name); // Syntax error
trace(values.elements("complex-name")); // traces "simple value"

For child elements, using the array access operator ([]) will also work much in the same way as the elements method.

Conditional Filtering

By referencing child elements from an XML or XMLList instance by name, you're essentially filtering child nodes by name.  You can also filter an XMLList using simple conditions.  These conditions follow a node reference with a dot (.) and a condition within the filtering predicate operator (()) where a condition can be used to filter an XMLList further.

Note: Filtering is not for children

Though a dot is being used to access the filtering predicate operator, it does not mean you are accessing children of that XMLList being filtered.  The filtering predicate operator filters the instance on which it is used much like the slice() method removes elements of an Array instance.

Consider the following XML

var grades:XML =
	<grades>
		<student>
			<name>Tom</name>
			<quiz num="1"><score>60</score></quiz>
			<quiz num="2"><score>90</score></quiz>
		</student>
		<student>
			<name>Brett</name>
			<quiz num="1"><score>100</score></quiz>
		</student>
		<student>
			<name>Michelle</name>
			<quiz num="1"><score>90</score></quiz>
		</student>
	</grades>;

The following statements use conditional filtering to trace results from this XML.

// Find students with the name Michelle
trace(grades.student.(name == "Michelle"));
/* output:
<student>
	<name>Michelle</name>
	<quiz num="1">
		<score>90</score>
	</quiz>
</student>
*/

Here a filter is used on the XMLList produced by grades.student. This contains a list of elements with the name student that exist within the grades XML. Within that list, the filter finds all student nodes where referencing name from that node results in a value of "Michelle". This results in one student node where, as you can see, the name element contains the text "Michelle".

// Find quizes whose num attribute equals 1
trace(grades.student.quiz.(@num == "1"));
/* output:
<quiz num="1">
	<score>100</score>
</quiz>
<quiz num="1">
	<score>60</score>
</quiz>
<quiz num="1">
	<score>90</score>
</quiz>
*/

This example instead filters the quiz elements within grades.students. It looks for the quiz elements with the num attrubute whose value is 1. Since the filter is used on the quiz reference, only quiz elements are returned.

// Find students who are not Michelle that
// have less than 2 quiz elements
trace(grades.student.(name != "Michelle" && quiz.length() < 2));
/* output:
<student>
	<name>Brett</name>
	<quiz num="1">
		<score>100</score>
	</quiz>
</student>
*/

The condition in this filter has two parts, one that makes sure the student's name is not Michelle and another that requires the student have less than two quiz elements. This leaves only the student element with the name Brett which is what the filter returns.

// Find students with a single score
// element having a value of 90
trace(grades.student.(quiz.score == 90));
/* output
<student>
<name>Michelle</name> <quiz num="1"> <score>90</score> </quiz> </student> */

You can see here that you can go further down the heirarchy within the scope of the condition this returns student elements with a quiz.score value of 90. Though the student element with the name Tom also has a score element with the score 90, it is not caught in this filter because quiz.score evaluates as an XMLList with two elements whose value is not comparable to 90. It works for Michelle because the quiz element in that student has one score node whose value is 90. When that value is evaluated for the condition it is seen as the value 90 and the condition can evaluate to true

// Find students with any score
// element that has a value of 90
trace(grades.student.(quiz.(score == 90).length()));
/* output
<student>
	<name>Tom</name>
	<quiz num="1">
		<score>60</score>
	</quiz>
	<quiz num="2">
		<score>90</score>
	</quiz>
</student>
<student>
	<name>Michelle</name>
	<quiz num="1">
		<score>90</score>
	</quiz>
</student>
*/

This example does what the previous did not; it finds all students that have any score element with the value 90. This is accomplished by a nested filter. The first filter is used to make sure that the results returned results in a collection of student elements. Within that filter you have another filter used with the quiz XMLList which filters that list by scores whose values equal 90. If any score exists within that quiz element with a value of 90, the filter will result in a quiz XMLList with one or more elements meaning length() will return 1 or greater, which in the original students filter, condition resolves as true. Both the Tom and Michelle student nodes have a score element with the value of 90 meaning quiz.(score == 90) creates an XMLList with a length of 1 for each of them (containing the respective score element). These nodes then get added to the XMLList resulting from the student filter.

With any filter you have the option of using literal values (used in the examples far) as well as ActionScript variables just so long as the variable name does not match the name of an element within the XMLList used in the filter.

// Find students with a single score
// element having a value of scoreCheck
var scoreCheck:int = 90;
trace(grades.student.(quiz.score == scoreCheck));
/* output
<student> <name>Michelle</name> <quiz num="1"> <score>90</score> </quiz> </student> */

This filter is equivalent to using (quiz.score == 90) assuming grades.student has no child elements with the name scoreCheck.

Every filter condition essentially boils down to:

var result:XMLList = new XMLList();
for each (var node:XML in target) {
	with (node) {
		if (condition) {
			result += node;
		}
	}
}
return result;

Where target is the XMLList the filter is used with, condition the condition within the filter, and result is the resulting XMLList from using the filter (result = target.(condition)). Any non-node reference within the filter condition will resolve to the respective ActionScript defined variable value (if one exists).

Dynamic XML definitions

You can also use ActionScript variables directly within inline XML to be used in defining that XML. For that you would use XML braces operator ({}). Variable values within the braces in XML is evaluated as its ActionScript value allowing ActionScript variables to be used in place of what would be inline XML values. The braces can be used to define element names, attribute names, attribute values, and text element values. An example that does all is as follows:

// Dynamic XML definition with braces
var elementName:String = "elem";
var attributeName:String = "attr";
var attributeValue:String = "value";
var textNode:String = "text";
var xml:XML = 
	<{elementName} {attributeName}={attributeValue}>
		{textNode}
	</{elementName}>;
trace(xml.toXMLString()); // traces "<elem attr="value">text</elem>"

If you need to be more complex in your XML definitions given variables that might contain data including full tags, you would need to instead create XML from a string using the XML constructor.

// XML generation with strings
var tagContents:String = "elem att=\"value\"";
var xml:XML = new XML("<" + tagContents + "/>");
trace(xml.toXMLString()); // traces <elem att="value"/>

XML Namespaces

Namespaces are used to separate or identify data. In XML they are used to associate one or more nodes with a certain URI (Uniform Resource Identifier). Elements with a namespace can have the same tag name as other tags but still be separated from them because of their association with a URI.

You will often see namespaces used with RSS feeds. The Apple iTunes podcast feed, for example, uses an itunes namespace to separate iTunes-specific tags from other normal RSS 2.0 tags.

Namespaces can be defined in XML in two ways. One way is to define an attribute named xmlns within an element node with the value of a URI. That node and all of its children then belong to the namespace identified by that URI. URI values are usually web site addresses. They don't have to be valid addresses, but since only one body can own any one domain, they make good candidates in making sure people will not be using the same URI for different purposes. This is known as setting a default namespace.

<xml>
	<parent xmlns="http://www.example.com/uri/">
		<child />
	</parent>
</xml>

In the above example, both parent and child are in the namespace identified by the example.com URI (http://www.example.com/uri/).

Namespace URIs can also be associated with a prefix in XML. When defined with a prefix, only the nodes using that prefix will be considered part of the namespace. Both element nodes and attributes can use the prefix. To define the prefix, you make a xmlns node in a parent element with the prefix name as a suffix of the xmlns attribute.

<xml xmlns:ns="http://www.example.com/uri/">
	<parent>
		<ns:child />
	</parent>
</xml>

In this example, only the child element is in the namespace because only that node is using the ns prefix.

Looking an an example iTunes podcast XML document, you can see how elements prefixed with itunes (for the itunes namespace) are mixed with other elements not within a namespace indicating that they are specific to iTunes.

<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
<channel>

	<title>All About Everything</title>
	<link>http://www.example.com/podcasts/everything/index.html</link>
	<language>en-us</language>
	<copyright>&#x2117; &amp; &#xA9; 2005 John Doe &amp; Family</copyright>
	<itunes:subtitle>A show about everything</itunes:subtitle>
	<itunes:author>John Doe</itunes:author>
	<itunes:summary>All About Everything is a show about everything. Each week we dive into any subject known to man and talk about it as much as we can. Look for our Podcast in the iTunes Music Store</itunes:summary>
	<description>All About Everything is a show about everything. Each week we dive into any subject known to man and talk about it as much as we can. Look for our Podcast in the iTunes Music Store</description>
	<itunes:owner>
		<itunes:name>John Doe</itunes:name>
		<itunes:email>john.doe@example.com</itunes:email>
	</itunes:owner>
	<itunes:image href="http://example.com/podcasts/everything/AllAboutEverything.jpg" />
	<itunes:category text="Technology">
		<itunes:category text="Gadgets"/>
	</itunes:category>
	<itunes:category text="TV &amp; Film"/>

	<item>
		<title>Shake Shake Shake Your Spices</title>
		<itunes:author>John Doe</itunes:author>
		<itunes:subtitle>A short primer on table spices</itunes:subtitle>
		<itunes:summary>This week we talk about salt and pepper shakers, comparing and contrasting pour rates, construction materials, and overall aesthetics. Come and join the party!</itunes:summary>
		<enclosure url="http://example.com/podcasts/everything/AllAboutEverythingEpisode3.m4a" length="8727310" type="audio/x-m4a" />
		<guid>http://example.com/podcasts/archive/aae20050615.m4a</guid>
		<pubDate>Wed, 15 Jun 2005 19:00:00 GMT</pubDate>
		<itunes:duration>7:04</itunes:duration>
		<itunes:keywords>salt, pepper, shaker, exciting</itunes:keywords>
	</item>

</channel>
</rss>

Any elements within the RSS specification that use the same tags used by iTunes will not conflict because of the use of the itunes prefix and its association with the unique iTunes URI.

When working with XML with namespaces in Flash, you will need to make use of namespaces in code in order to access nodes within the XML that use them. Though elements with namespaces may have the same local name, or the tag name not including a namespace prefix (if it exists), their namespace helps define their true name (URI + local name), so for a element with a namespace to be referenced by name, the namespace is needed. In Flash, a node within a namespace is referenced using its namespace and base name in combination with the name qualifier operator (::).

Namespaces in ActionScript 3 are handled through a new Namespace class (top-level Namespace). This class consists of two properties, uri and prefix which relate to the URI of the namespace and the namespace's prefix if it has one. The URI is really the more important part a Namespace instance as the URI is what defines the full node name. The prefix is just a type of shortcut to associate the URI with a node.

// Create a new Namespace instance
var myNamespace:NameSpace = new Namespace("http://www.example.com/uri");

Once you have a namespace defined, you can use it to reference XML nodes within the namespace specified by that URI through the use of the name qualifier operator.

The name qualifier operator in ActionScript works much like the namespace prefix does within the actual XML. In XML, the prefix comes in front of the node name separating itself from the name with a colon (:). In ActionScript, you're now just using two colons (::).

// XML with xpl namespace
var xml:XML = 
	<xml xmlns:xpl="http://www.example.com/uri/">
		<text>Hello World</text>
		<xpl:text>Hello Namespace</xpl:text>
	</xml>;
	
// Make a namespace instance based
// on the xpl namespace's URI
var xplNs:Namespace = new Namespace("http://www.example.com/uri/");

// Accessing the text element
trace(xml.text); // traces "Hello World"

// Use the name qualifier operator to
// reference the text tag using the namespace
// and the element's base name
trace(xml.xplNs::text); // traces "Hello Namespace"

Here you can see how the namespace separates the text within the two text elements. The xplNs Namespace instance is used to access the text element in the namespace by using the name qualifier to fully qualify that element's full name.

If you want to get both text elements, or all elements within a node with the local name of text, you can use a filter checking those elements' local name. You can access the local name of an XML node through the QName instance (top-level QName) returned by the XML method name(). QName instances describe a node's full name including both the local name (localName property) and URI (uri property). To have the filter work for all of the XML node's children, you will need to access the node's chilren through the children() method.

// XML with xpl namespace
var xml:XML = 
	<xml xmlns:xpl="http://www.example.com/uri/">
		<text>Hello World</text>
		<xpl:text>Hello Namespace</xpl:text>
	</xml>;

// Get all elements with local name of text
trace(xml.children().(name().localName == "text"));
/* output
<text xmlns:xpl="http://www.example.com/uri/">Hello World</text>
<xpl:text xmlns:xpl="http://www.example.com/uri/">Hello Namespace</xpl:text>
*/

Notice that the namespace definition is retained through the results with the inclusion of the xmlns attribute in the text nodes. This allows the namespaces to remain valid even when working with portions of the original XML in ActionScript.

There may be times when you are not able to know the namespace URIs used in the XML you will need to work with. Without the URI's you may have trouble accessing nodes because you couldn't access them through theit qualified names (though you can still easily access them through an index with a parent node's children() list, etc.). If you do not know what namespaces are used within the XML, you can retrieve them using the namespace() and namespaceDeclarations() methods.

The XML namespace() method returns a single Namespace instance that represents the Namespace of that XML node. The namespaceDeclarations() method is similar, but instead provides a list of all the namespaces defined within the XML element - the default namespace (if exists) and all namespaces with their defined prefix. If an element node does not have any xmlns attributes defined for it, namespaceDeclarations() will return an empty array. Where namespace() will return an inherited default namespace value if defined in a parent node, namespaceDeclarations() will only return definitions set within the element from which it's used.

The following XML is an XML stylesheet (XSL) used to transform the format of XML from one format to another. It has two namespaces, one for the the XSL and the other for the (X)HTML being generated.

var xsl:XML = <xsl:stylesheet version="1.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns="http://www.w3.org/TR/xhtml1/strict">
<xsl:template match="/">
	<html>
		<head>
			<title>Expense Report Summary</title>
		</head>
		<body>
			<p>Total Amount: <xsl:value-of select="expense-report/total"/></p>
		</body>
	</html>
</xsl:template>
</xsl:stylesheet>;
// Find the namespaces defined in the root node
trace(xsl.namespaceDeclarations());
// traces http://www.w3.org/1999/XSL/Transform,http://www.w3.org/TR/xhtml1/strict

// Define namespace variables from declarations
var namespaces:Array = xsl.namespaceDeclarations();
var xslNs:Namespace = namespaces[0];
var xhtmlNs:Namespace = namespaces[1];

// Get the html element's namespace
trace(xsl.xslNs::template.xhtmlNs::html.namespace());
// traces http://www.w3.org/TR/xhtml1/strict

// Get the value-of element's namespace
trace(xsl.xslNs::template.xhtmlNs::html..xslNs::["value-of"].namespace());
// traces http://www.w3.org/1999/XSL/Transform

Notice that with this XSL file, that the default namespace is the one using the xhtml URI. This means by default, all XML elements are considered XHTML. Those that are not are prefixed with the xsl prefix identifying them as being part of the XSL namespace, including the root xsl node in which the xsl namespace was defined. To get to the html element both namespaces were used in the E4X path to reference it.

The above example also shows how the descendant operator (..) and array access notation ([]) was used to reach the value-of element. Notice that the the brackets used for referencing value-of came after the name qualifier operator. The namespace used to qualify your element name cannot be used inside the brackets. It continues to be separate from the local name - the local name (as a string) being the only part within the brackets. This format, however, will not work in conjunction with the elements() or descendants() methods. In those cases, you would have to pass in a QName instance to those methods that specifies both the local name as well as the namespace URI. To access the element using descendants instead of the descendant operator, you would use the following

// descendants method with namespace
xsl.xslNs::template.xhtmlNs::html.descendants(new QName(xslNs.uri, "value-of"))

As you start to reference XML nodes in this manner with name spaces, you can find that references may start to get excessively long, especially if you have to prefix each node with a namespace qualifier when, for example, your XML document uses even a single default namespace. To make this process a little easier, you have the option of defining a default xml namespace in ActionScript using the default xml namespace directive.

The default xml namespace directive acts sort of like a variable that you set to specify a type of automatic namespace for XML references. This would allow you to use E4X references for XML that uses a single namespace without the need of prefixing each node with the appropriate qualifier.

// Get the title
trace(xsl.xslNs::template.xhtmlNs::html.xhtmlNs::head.xhtmlNs::title);
// traces Expense Report Summary

// set xhtmlNs as the default namespace
default xml namespace = xhtmlNs;

// Get the title now with default namespace
trace(xsl.xslNs::template.html.head.title);
// traces Expense Report Summary

So far the examples have covered only element nodes within namespaces. Don't forget that attributes too can be associated with a namespace. Just because an attribute is within an element defined in a namespace doesn't mean that attribute is also in that namespace (unless part of a default namespace).

// XML with xpl namespace
var xml:XML = 
	<xml xmlns:xpl="http://www.example.com/uri/">
		<xpl:text value="Hello World" xpl:value="Hello Namespace" />
	</xml>;

// Define namespace with xpl URI
var xplNs:Namespace = new Namespace("http://www.example.com/uri/");

// Get value of text attribute
// with and without namespace
trace(xml.xplNs::text.@value); // traces Hello World
trace(xml.xplNs::text.@xplNs::value); // traces Hello Namespace

You can see that even though both attributes are part of the text node in the xpl namespace, they are not inherently also part of that namespace. The prefix is still required for an attribute of a node to be placed within a namespace (unless a default namespace is defined and that attribute falls under the hierarchy of that definition).

Example: RSS Feed Reader

This example will load an RSS feed from an external source and parse its contents to be displayed, formatted, within a text field in Flash. The source for this example will be the Adobe XML News Aggregator (MXNA) - recent news from the front page (rss).

Note: Loading content sites and crossdomain.xml

When you load text content from an external site outside of the domain (and subdomain) of the SWF, a crossdomain.xml file is necessary to grant access of that content to the SWF requesting it. The MXNA feed, for example, would normally not be accessible from another web site, but weblogs.macromedia.com has crossdomain.xml policy file located at weblogs.macromedia.com/crossdomain.xml allowing access to all Flash SWFs within other domains.

In this example you will see how external XML can be loaded into the Flash Player and how its contents can be read when that content uses multiple namespaces.

Steps:

  1. In the source files, open MXNARssReader.fla in the XML directory. This file contains a text field that will display the MXNA feed as HTML text.
  2. Select the first frame in the ActionScript layer and open the Actions panel.

The variables needed for this example are simply those related to the loading of the feed XML. This includes a URLLoader instance, the URLRequest it uses, and the actual URL of the feed.

  1. Add the following script to the Actions panel.
// Set up variables
var mxnaURL:String = "http://weblogs.macromedia.com/mxna/xml/rss.cfm?query=byMostRecent&languages=1";
var mxnaRequest:URLRequest = new URLRequest(mxnaURL);
var mxnaLoader:URLLoader = new URLLoader();

Note: Testing Without Internet Access

In the source files, there is an XML file named mxna_sample.xml that you can substitute for the above MXNA URL if you want to test this example without being connected to the internet.

var mxnaURL:String = "mxna_sample.xml";

Next handlers are defined for the URLLoader instance. The most important handler is the complete event handler letting us know when the XML has loaded. When that has happened, we can read the XML and display its contents in Flash. To protect against errors, you should also listen for the io and security error events.

The error handler can just trace a message if there's an error for now. Ideally special action should be taken to inform the user that there was an error or the operation should be tried again. In the complete handler, an XML object should be created based on the loaded text and the contents of that XML displayed on the screen (in the text field). For now, that process will be delegated to a function called renderFeedXML().

  1. Add the following script to the Actions panel.
// Assign listeners for downloading feed
mxnaLoader.addEventListener(Event.COMPLETE, feedDownloadedHandler);
// Error listeners
mxnaLoader.addEventListener(IOErrorEvent.IO_ERROR, feedDownloadError);
mxnaLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, feedDownloadError);

// Event handlers
// Called when feed has completely downloaded
function feedDownloadedHandler(event:Event):void {
	
	// use a try-catch block in case
	// xml parsing fails
	try {
	
		// convert the loaded text into XML
		var mxnaXML:XML = XML(mxnaLoader.data);
		
		// if XML succeeded (no error)
		// show it in the text field 
		renderFeedXML(mxnaXML);
		
	}catch(error:Error){
		trace("Error parsing XML: "+error);
	}
	
}
// Called if feed download fails
function feedDownloadError(event:Event):void {
	trace("Error during download: "+event);
}

When the XML loads and is successfully converted to an XML instance from Flash, the renderFeedXML is called. This function is the function that will need to go through the XML and translate it into HTML text that will be displayed in the text field on the screen. At this point it is important to understand the setup of the XML feed.

The MXNA feed is a RSS 1.0 feed. By looking at the root node you can see what namespaces it has defined for it.

<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
	xmlns="http://purl.org/rss/1.0/"
	xmlns:dc="http://purl.org/dc/elements/1.1/">

The URI http://purl.org/rss/1.0/ is the URI for the default namespace. The other namespaces are rdf (http://www.w3.org/1999/02/22-rdf-syntax-ns#) which stands for Resource Description Framework (the R in RSS, though RSS is sometimes also referred to as "Real Simple Syndication"), and dc (http://purl.org/dc/elements/1.1) which stands for Dublin Core, an additional set of metadata added to the feed.

Each post in the feed is stored in an item element that is a direct child of the root rdf node. An example of one of these items looks like this.

<item rdf:about="http://www.senocular.com/?id=2.4">
	<title>New Flash Player 9 Update Posted</title>
	<link>http://weblogs.macromedia.com/mxna/controller.cfm?handler=PostHandler&action=click&postId=198719&nextPage=http%3A%2F%2Fwww%2Esenocular%2Ecom%2F%3Fid%3D2%2E4</link>
	<description><![CDATA[If you haven't already noticed, there's a new version of the Flash Player posted on the Adobe.com website. This version is 9,0,47,0 (you can check your current version from the About Adobe Flash Player page). For more information, see the release notes.]]></description>
	<dc:subject>Fireworks</dc:subject>
	<dc:creator>senocular.com</dc:creator>
	<dc:date>2007-07-11T16:50:12.0-00:00</dc:date>
	<dc:identifier>02DD0C9679274B1F18BFCAE95E2A4009</dc:identifier>
</item>

For this reader we're only going to be interested in the original source link in the about attribute, the title, the description, the creator and the date. As you can see, the about attribute is defined in the rdf namespace. The about attribute is contained within the item tag that has no namespace specified for it. However, since the root rdf element has a default namespace defined for it, that puts item in that one (http://purl.org/rss/1.0/). This also applies to the title and description elements. The remaining elements, creator and date, are part of the dc namespace. When referencing these nodes in ActionScript, the proper namespaces will need to be used. Referencing item, title, and description can be made a little simpler if the default xml namespace is set to be the XML's default namespace.

Because there are multiple item elements within the feed, a loop can be used in renderFeedXML to go through each one and add it to the text field on the stage. A for each..in loop is usually the easiest kind of loop to do this, especially for XML where indexes are not important, just the nodes and their values. The function will call another function renderFeedItem to actually place the content of an individual item within the text field.

  1. Add the following script to the Actions panel.
// Functions
// Writes each feed item to the screen
function renderFeedXML(feed:XML):void {
	
	// get default namespace for feed
	var defaultNs:Namespace = new Namespace("http://purl.org/rss/1.0/");
	// set default namespace to make referencing easier
	default xml namespace = defaultNs;

	// clear screen text
	mxnaText.text = "";

	// write each Feed item to the screen
	// looping through the XMLList with for each
	var items:XMLList = feed.item;
	for each (var item:XML in items){
		renderFeedItem(item);
	}
}
		
// Writes a single feed item to the screen
function renderFeedItem(item:XML):void {
	
	// namespaces
	var rdf:Namespace = new Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#");
	var dc:Namespace = new Namespace("http://purl.org/dc/elements/1.1/");
	var defaultNs:Namespace = new Namespace("http://purl.org/rss/1.0/");
	
	// set default namespace to make referencing easier
	default xml namespace = defaultNs;

	// parse date string [YYYY-MM-DD]
	var date:String = item.dc::date.text().substring(0, 10);

	// write other feed content to text using HTML
	mxnaText.htmlText += "<u><b><a href=\""+item.@rdf::about+"\">"+item.title+"</a></b></u><br />";
	mxnaText.htmlText += "<i>by "+item.dc::creator+" on "+date+"</i><br />";
	mxnaText.htmlText += item.description+"<br /><br />";
}

Notice that default xml namespace was set in both renderFeedXML and renderFeedItem. When you set default xml namespace, it only applies to the current scope. When you are in another function or exit the current function, you will need to set it again (assuming you intend to use it again).

The renderFeedXML and renderFeedItem functions are the functions doing all the work. Once the XML is loaded they will read through the XML and place its relevant content on the screen. The only thing left to do is load the XML.

  1. Add the following script to the Actions panel.
// Download XML feed. Once loaded, the
// parsing and displaying process will start
try {
	mxnaLoader.load(mxnaRequest);
	mxnaText.text = "Loading...";
}catch(error:Error){
	trace("Error during load: "+error);
}

Remember, a try..catch block is needed to catch any errors load() might throw.

  1. When you've completed adding the code, test your movie. You should see something similar to the following.


Figure: MXNA RSS Reader in Flash

More attention can certainly be given to design or completeness, but you can see how easy it can be to get XML content from the web and display it in Flash.

Editing XML

[TODO: complete; remove xml child is now delete]

Continue reading »

Pages: 1 | 2 | 3 | 4 | 5