Processing XML in Flex 2.0 Using The For Each Statement And The Descendent Accessor (..) Operator
This example demonstrates using the for each..in statement and the descendent accessor (..) operator to easily traverse an XML object in Flex 2.0 and ActionScript 3.0. Additionally, I'll touch on the importance of class Namespace when using the e4x XML processing format.
You can view a Flex demonstration application releated to this blog entry (right click to view source) here:
/flex/xmlForEach/bin/xmlForEach.html
My references for this demonstration are:
Traversing XML structures, Programming ActionScript 3.0, http://livedocs.macromedia.com/flex/2/docs/00001917.html
for each..in statement syntax, Programming ActionScript 3.0, http://livedocs.macromedia.com/flex/2/docs/00001831.html
Class Namespace, Flex 2.0 Language Reference, http://livedocs.macromedia.com/flex/2/langref/Namespace.html
E4X: A new approach to XML processing, Programming ActionScript 3.0,
http://livedocs.macromedia.com/flex/2/docs/00001912.html#119488
I had previously discussed using XML in Flex and the e4x XML processing capabilities here:
/blog/index.cfm/2006/10/13/Processing-XML-in-Flex-20 which is the first in a series of tutorials I did in using XML in Flex 2.0 applications.
In this example, I demonstrate using two methods that make it easier to move around and get information out of an XML document. First, let's examine the XML that I'm playing with. I'm using a CFC function that receives an artist or band name. The function connects to a REST application on MusicBrainz. The MusicBrainz service will return an XML file that provides all the releases by the artist (or band). Once the CFC gets the data from MusicBrainz, it creates an XML object and returns it to the Flex application.
My first step in any application that uses XML, where I am not the one creating the XML, is to examine the XML document. So after getting the XML back from the CFC function, I convert it to an XMLString and display it in a TextArea component.
/*This function is called automatically when
the results are returned by calling the CFC's getArtistXML method
*/
public function handleResult(event:ResultEvent):void{
//the result returned is treated by Flex as generic Object
//since the Object is really XML (that is what the CFC method returns)
//we can use the event.result Object to create an XML class object
xmlResult = new XML(event.result); //XMLResult.toXMLString() is bound to a TextArea
(later in the Flex app is this line)
<mx:TextArea x="37" y="74" width="422" height="250" id="xmlText" text="{xmlResult.toXMLString()}"/>
So when I type in an artist's name and click on the button, I will see the entire XML document returned. Now I can study this document to identify two key aspects: Is the XML returned referencing a name space? and What are the names of the nodes that I will need to use?. Below is part of the XML returned if the user types in Bon Jovi (hey he was big in the late 80's/early 90's).
<InvalidTagdata xmlns="http://musicbrainz.org/ns/mmd-1.0#" xmlns:ext="http://musicbrainz.org/ns/ext-1.0#">
<release-list>
<release ext:score="99" id="71b27fc1-8d18-431a-81b5-7d7efd6f3222" type="Album Official">
<title>Crush</title>
<text-representation language="ENG" script="Latn"/>
<asin>B00004TQX0</asin>
<artist id="5dcdb5eb-cb72-4e6e-9e63-b7bace604965">
<name>Bon Jovi</name>
</artist>
<release-event-list>
<event date="2000"/>
</release-event-list>
<disc-list count="2"/>
<track-list count="12"/>
</release>
<release ext:score="99" id="0e584652-f3f7-4e77-8694-beccbcba2841" type="Album Official">
<title>Crush</title>
<text-representation language="ENG" script="Latn"/>
<asin>B00004TQX0</asin>
<artist id="5dcdb5eb-cb72-4e6e-9e63-b7bace604965">
<name>Bon Jovi</name>
</artist>
<release-event-list>
<event date="2000"/>
</release-event-list>
<disc-list count="4"/>
<track-list count="13"/>
</release>
The line metadata xmlns="http://musicbrainz.org/ns/mmd-1.0#" identifies the name space used by this XML service. I don't even pretend to really understand name spaces. You can click on my name space reference above to try to find out more, but I've yet to find a "Dummy's Guide to XML Namespaces." If you know of one, please post a link to it in a comment. What this line does tell me is that I will need to declare an object of type Namespace in my Flex application if I want to process this XML using the e4x formatting (see link above for more about e4x and XML). The code below sets the Namespace up.
use namespace musicBrainzNS;
In my testing, if I don't have the above code, I cannot use the following techniques to process the XML.
For this demonstration, I just want to pull out the title and release date of each release. Examining the XML document, the release node is the node that repeats for each release. I can use the descendent accessor (..) operator (also called the double dot operator) to easily pull out every node named "release" that occurs anywhere in the XML document. If I combine using the .. operator with the for each statement I can loop over every release node and pull out the data I need.
for each (var release:XML in xmlResult..release) {
//create an Object that we can add to our ArrayCollection
var releaseObj:Object = new Object();
releaseObj.title = release.title ;
The for loop above will repeat for each release node found in the xmlResult object (which is the XML object I created using the XML returned by the CFC function). The for loop will pull out the next release node and assign it to an XML object I named release for each loop iteration. All child nodes (for example title) are included when the release node is pulled out. I can then use the dot (.) operator to reference any direct child nodes of that release XML object. So to get the title node, I just do release.title.
It is a little more difficult to get the release date value. If you examine the XML document, you'll note that the release date is actually an attribute (named date) of the event node. The event node is a child of the release-event-list node, and the release-event-list node is a child of the release node. The .. operator makes it easier to get the date attribute.
//find the event node under this release XML node and get the date
//attribute for the even node
releaseObj.date = release..event.@date ;
The above code says find the event node that is somewhere in the release XML object and then get the value of the event node's date attribute (the @ specifies an attribute). Without the .. operator I would have to remember and type a complicated path to the event node.
Some sources stated that the .. operator will through an exception (error) if you try to access a node that does not exist. If you study the entire XML document you will see that there are some release nodes where there is no event node. In my source code, I surround the code above in a try-catch block to handle that possible error. I also display "unknown" where there was no event node with a date attribute.
Flex 2.0 and ActionScript 3.0 provide excellent capabilities for using XML. The for each..in statement and the descendent accessor (..) operator enable Flex programmers to easily traverse XML documents and extract data from them.
Very Good, thanks for this Tutorial.
Best,
Ramón
The link to the demo is in my blog entry above. You can right click on the application to view the source code.
Was there something else you wanted?
getArtistXML method?
Thanks,
<cffunction name="getArtistXML" access="remote" returntype="xml">
<cfargument name="artistName" type="string" required="yes">
<cfset var artistXML = "">
<!---get the XML file that lists all releases by the above artist--->
<cfhttp url="http://musicbrainz.org/ws/1/release/?type=xml&...; method="get" throwonerror="yes" />
<!---process the XML File--->
<cfset artistXML = XmlParse(cfhttp.FileContent) >
<cfreturn artistXML >
</cffunction>
I am struggling with sending two arguments to a cfc. Using your example how would you code to send two text arguments to the cfc on click of the button?
I changed the button to click="getArtistData()"
I added a second text box id="categoryName", and my two cfarguments in my cfc to:
<cfargument name="currArtist" type="string" required="yes">
<cfargument name="currCategory" type="string" required="yes">
I tried:
public function getArtistData() {
var currArtist:string = artistName.text;
var currCategory:string = categoryName.text;
myService.getArtistXML.send();
}
I get the error in Flex of:
1008:return value for function getArtistData has not type declaration.
Also, I am not sure this will get the values for my two arguments into the cfc correctly. Can you point me in the right direction?
/blog/index.cfm/2007/...
That blog entry also includes references to some Adobe documentation on using Flex and CF that you might find useful.
For instance:
MemberService.getMemberByUserNameAndPassword('testName','testPassword');
<cffunction name="getMemberByUserNameAndPassword" access="public" output="false" returntype="Member"
<cfargument name="aUserName" type="string" required="true" />
<cfargument name="aPassword" type="string" required="true" />
...
</cffunction>
So you could send as many variables you wanted as long as you had the <cfargument> in the proper order.
Thanks again.
your namespace clue solved my problems with a yahoo API after hours of searhing. I had posted on the Adobe Flex Forum for help but hadn't received anything yet. I gave you credit for the solution with a link to the tutorial at http://www.adobe.com/cfusion/webforums/forum/messa...
After getting everything to work following this example, I tried to use an XMLList instead of the Array. My datagrid rows change on a simple search for a common item like "dog", but nothing displays in the columns. The text in the nodes are wrapped with CDATA, so I don't know if that is causing the issue. My example is at http://67.199.18.39/test.html. I can't seem to figure out the fix. Can you point me in the right direction?
Take a look at the structure of the XML coming back from the CFC and how you're trying to create the XMLList object.
<mx:Text text="{data.productName}" /> returns: <b>Product </b> Title
<mx:image source="{data.url}" /> The image is not viewable. The data.url = "<![CDATA[http://f5c.yahoofs.com/shopping/mcid4_89929/simg]]>"
How do I display the html properly in mx:text, and how do I "strip away" or only return the "url" for the image?
I have found where coders want to add cdata tags to their html but not where they remove them.