by Adobe

adobe_logo_bio

Created

23 March 2010

 Requirements
 
User level
Required products
All Flex (Download trial)
ActionScript 3.0 includes a group of classes based on the ECMAScript for XML (E4X) specification (ECMA-357 edition 2). These classes feature powerful and easy-to-use functionality for working with XML data. Using E4X, you can develop code with XML data faster than was possible with previous programming techniques. As an added benefit, the code you produce is easier to read.
 
This Quick Start covers the following topics:
 

 
Introduction to XML

Many server-side applications use XML to structure data, so you can use the XML classes in ActionScript to create sophisticated rich Internet applications, such as those that connect to web services. A web service is a means to connect applications—for example, an Adobe Flash Player application and an application on a web server—through a common standard such as the Simple Object Access Protocol (SOAP).
 
The ECMAScript for XML specification in Adobe Flex defines a set of classes and functionality for working with XML data. These classes and functionality are known collectively as E4X. The two main classes you use in Flex are the XML and XMLList classes.
 
In the following example, you create an XML literal called myBooks by using ActionScript. You can create XML literals in ActionScript by writing XML in an ActionScript block and assigning it to a variable, because XML is a native data type in Flex, just like Number or Boolean.
 
The myBooks XML literal in the following example contains two book elements (also known as nodes). The first book element has four child elements, with the names title, author, amazonUrl, and pageCount.
 
To access the elements in an XML instance, you use dot notation (.), just like you do when accessing the property of an object. So, for example, to get a reference to the list of book nodes, you write myBooks.book. This returns an XMLList instance that contains the two book nodes in the myBooks XML. To access a specific node in a list of identical nodes, you use array notation. To get a reference to the first book node, for example, you write myBooks.book[0]. You should already be familiar with this use of the dot operator and array-style syntax if you have worked previously with Objects and Arrays in ActionScript. E4X, however, goes further than this and enables you to search the XML for a node with a specific attribute value.
 
In the following example, you get a reference to the first book by searching for the value of its ISBN attribute. Attributes in E4X are referenced by prefixing them with the at-sign, as in @ISBN. The statement myBooks.book.(@ISBN=="0865436401") translates to "find the book node where the ISBN attribute has a value of 0865436401." The other examples describe more advanced, query techniques.
 
 
Example
 
 
<?xml version="1.0" encoding="utf-8"?> <!-- XMLBasics.mxml --> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" creationComplete="initializeHandler();"> <fx:Script> <![CDATA[ [Bindable] public var a:XMLList; [Bindable] public var b:XMLList; [Bindable] public var c:XMLList; [Bindable] public var d:XMLList; // XML is a native data type in Flex. private var myOrder:XML = <order> <book ISBN="0942407296"> <title>Baking Extravagant Pastries with Kumquats</title> <author> <lastName>Contino</lastName> <firstName>Chuck</firstName> </author> <pageCount>238</pageCount> </book> <book ISBN="0865436401"> <title>Emu Care and Breeding</title> <editor> <lastName>Case</lastName> <firstName>Justin</firstName> </editor> <pageCount>115</pageCount> </book> </order> private function initializeHandler():void { // An XML list that contains both book nodes. a = myOrder.book; // Chuck b = myOrder.book[0].author.firstName; // 115 c = myOrder.book.(@ISBN=="0865436401").pageCount; // Delete the first book node. delete myOrder.book[0]; // Emu Care and Breeding d = myOrder.book[0].title; } ]]> </fx:Script> <!-- User interface --> <s:Panel title="XML lookup results"> <s:layout> <s:VerticalLayout paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"/> </s:layout> <s:Label text="{'a: ' + a}"/> <s:Label text="{'b: ' + b}"/> <s:Label text="{'c: ' + c}"/> <s:Label text="{'d: ' + d}"/> </s:Panel>

 
Assigning values to XML elements and attributes

You use the @ and dot (.) operators not only to read data values from an XML structure, but also to assign data values to it.
 
In the following example, you create a master-detail view of an XML structure. The master view contains a DataGrid component that displays a list of books. The detail view has controls for editing the book that is currently selected in the master view.
 
The master and detail views use data binding and E4X to both read data from the XML and to update data in the XML.
 
The following examples uses E4X in these ways:
 
  • myBooks.book refers to the XMLList of book elements
  • myBooks.book[selectedBookIndex] references the currently selected book element index
  • myBooks.book[selectedBookIndex].title references the value of the title element of the currently selected book element
To update the titleInput TextInput control with the title of the currently selected book, you bind the text property of the titleInput TextInput control to myBooks.book[selectedBookIndex].title. Similarly, to update the XML with the user's latest input, you assign the value of the text property of the TextInput control to myBooks.book[selectedBookIndex].title when the text in the titleInput TextInput control changes.
 
The other components in the detail view work exactly the same way as the titleInput control.
 
 
Example
 
 
<?xml version="1.0" encoding="utf-8"?> <!-- XMLAssigning.mxml --> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="500" height="470" creationComplete="myDataGrid.selectedIndex=0;"> <fx:Script> <![CDATA[ // Model: XML structure describing // some of the books in my collection. [Bindable] private var myBooks:XML = <books> <book ISBN="1590595181"> <title>Foundation ActionScript Animation: Making Things Move</title> <author>Keith Peters</author> <amazonUrl>http://tinyurl.com/npuxt</amazonUrl> </book> <book ISBN="1582346194"> <title>Send in the Idiots: Stories from the Other Side of Autism</title> <author>Kamran Nazeer</author> <amazonUrl>http://tinyurl.com/lo5ts</amazonUrl> </book> </books> ]]> </fx:Script> <fx:Declarations> <!-- Keep track of the currently selected book --> <fx:Number id="selectedBookIndex">{myDataGrid.selectedIndex}</fx:Number> </fx:Declarations> <!-- User interface --> <s:Panel title="Assigning XML data"> <s:layout> <s:VerticalLayout paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"/> </s:layout> <!-- Master view: Display all books --> <mx:DataGrid id="myDataGrid" dataProvider="{myBooks.book}"> <mx:columns> <mx:DataGridColumn dataField="@ISBN" headerText="ISBN" width="85"/> <mx:DataGridColumn dataField="title" headerText="Title"/> <mx:DataGridColumn dataField="author" headerText="Author"/> <mx:DataGridColumn dataField="amazonUrl" headerText="Web site"> <mx:itemRenderer> <fx:Component> <mx:LinkButton label="Visit" click="navigateToURL(new URLRequest(data.amazonUrl), 'blank');"/> </fx:Component> </mx:itemRenderer> </mx:DataGridColumn> </mx:columns> </mx:DataGrid> <!-- Detail view: Display currently selected book for editing --> <mx:Form width="100%" autoLayout="false"> <mx:FormHeading label="Edit book details"/> <mx:FormItem label="ISBN:" width="100%"> <s:TextInput id="isbnInput" width="100%" text="{myBooks.book[selectedBookIndex].@ISBN}" change="{myBooks.book[selectedBookIndex].@ISBN = isbnInput.text}"/> </mx:FormItem> <mx:FormItem label="Title:" width="100%"> <s:TextInput id="titleInput" width="100%" text="{myBooks.book[selectedBookIndex].title}" change="{myBooks.book[selectedBookIndex].title = titleInput.text}"/> </mx:FormItem> <mx:FormItem label="Author:" width="100%"> <s:TextInput id="authorInput" width="100%" text="{myBooks.book[selectedBookIndex].author}" change="{myBooks.book[selectedBookIndex].author = authorInput.text}"/> </mx:FormItem> <mx:FormItem label="Amazon Url" width="100%"> <s:TextInput id="amazonUrlInput" width="100%" text="{myBooks.book[selectedBookIndex].amazonUrl}" change="{myBooks.book[selectedBookIndex].amazonUrl = amazonUrlInput.text}"/> </mx:FormItem> </mx:Form> </s:Panel> </s:Application>

 
Creating XML objects by passing data by reference

The Introduction to XML example demonstrates one method of initializing an XML object by using an XML literal. When creating an XML literal, you can also pass data by reference (from other variables) into an XML object by referencing a variable name enclosed in curly braces.
 
If the XML structure you are creating is not valid XML, you see a TypeError run-time error.
 
The following example dynamically creates an XML object based on the values provided by the user for the tag name, attribute name, attribute value, and tag contents. The example checks for the TypeError condition by surrounding the XML initialization code with a try...catch block.
 
 
Example
 
 
<?xml version="1.0" encoding="utf-8"?> <!-- XMLDataByReference.mxml --> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="500" height="400" initialize="createXML();"> <fx:Script> <![CDATA[ [Bindable] public var xml:XML private function createXML():void { try { // Create the XML object using the values provided // by the user for the tag name, attribute name, // attribute value and the tag's contents. xml = <{tagName.text} {attributeName.text}={attributeValue.text} > {content.text} </{tagName.text}>; } catch (e:TypeError) { // Type error encountered while trying to create the // XML object. The form must not be valid. Inform // the user. xml = <note>Fill the form to see the tag here.</note>; } } ]]> </fx:Script> <!-- User interface --> <s:Panel title="Passing XML data by reference"> <s:layout> <s:HorizontalLayout/> </s:layout> <mx:Form> <mx:FormItem label="Tag name:"> <s:TextInput id="tagName" change="createXML();"/> </mx:FormItem> <mx:FormItem label="Attribute name:"> <s:TextInput id="attributeName" change="createXML();"/> </mx:FormItem> <mx:FormItem label="Attribute value:"> <s:TextInput id="attributeValue" change="createXML();"/> </mx:FormItem> <mx:FormItem label="Tag content:"> <s:TextInput id="content" change="createXML();"/> </mx:FormItem> <mx:HRule width="100%"/> <!-- Display the resulting XML --> <s:TextArea editable="false" width="300" height="50" text="{xml.toXMLString()}"/> </mx:Form> </s:Panel> </s:Application>

 
Assembling and transforming XML objects

In addition to assigning values to XML elements and attributes, you can assemble and transform XML objects by using E4X.
 
In the following example, you use the prependChild() and the appendChild() methods to add a property to the beginning or end of an XML object's list of properties. Similarly, you use the insertChildBefore() method and the insertChildAfter() method to add a property before or after a specified property. To delete an item, use the delete() method to remove a node from the XML.
 
 
Example
 
 
<?xml version="1.0" encoding="utf-8"?> <!-- XMLAssembleTransform.mxml --> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="500" height="600" creationComplete="myDataGrid.selectedIndex=0; validateForm();"> <fx:Script> <![CDATA[ import mx.controls.Alert; // Constants private const SELECTED_ITEM:uint = 0; private const THE_LIST:uint = 1; private const BEFORE:uint = 0; private const AFTER:uint = 1; // Flag: is the form valid? [Bindable] private var formIsValid:Boolean; // Flag: does a selection exist in the data grid? [Bindable] private var selectionExists:Boolean; // Holds the index of the next item in the // data grid following the deletion of a book item. private var newSelectedIndex:int; // Model: XML structure describing // some of the books in my collection. [Bindable] private var myBooks:XML = <books> <book ISBN="1590595181"> <title>Foundation ActionScript Animation: Making Things Move</title> <author>Keith Peters</author> <amazonUrl>http://tinyurl.com/npuxt</amazonUrl> </book> <book ISBN="1582346194"> <title>Send in the Idiots: Stories from the Other Side of Autism</title> <author>Kamran Nazeer</author> <amazonUrl>http://tinyurl.com/lo5ts</amazonUrl> </book> </books> // Add a new book private function addBookHandler():void { // Create a new XML Object from the form information var newBook:XML = <book ISBN={isbnInput.text}> <title>{titleInput.text}</title> <author>{authorInput.text}</author> <amazonUrl>{amazonUrlInput.text}</amazonUrl> </book>; // Save the selected book's index so it can be reselected // in the grid once the new book is added. var selectedBookIndex:uint = myDataGrid.selectedIndex; // Does the user want to add the new // book relative to the selected book in the // data grid or relative to the list itself? if (addRelativeTo.selectedIndex == SELECTED_ITEM) { // Add the new item relative to the selected item. var selectedBook:XML = myBooks.book[selectedBookIndex]; // Does the user want to add it before or after // the selected item? if (addPosition.selectedIndex == BEFORE) { // Add new item before selected item myBooks = myBooks.insertChildBefore(selectedBook, newBook); } else { // Add new item after selected item myBooks = myBooks.insertChildAfter(selectedBook, newBook); } } else { // Add the new item relative to the whole list of books. if (addPosition.selectedIndex == BEFORE) { // Add new item at the start of the list myBooks = myBooks.prependChild(newBook); } else { // Add new item at the end of the list myBooks = myBooks.appendChild(newBook); } } // Select the previously selected item in the grid // so the user doesn't lose their position. (If a new book was // added before the currently selected item, that items // new index will be one greater, so select that.) var newSelectedIndex:uint = (addPosition.selectedIndex == BEFORE) ? selectedBookIndex + 1: selectedBookIndex; myDataGrid.selectedIndex = newSelectedIndex; } // Delete selected book private function deleteBookHandler():void { // Save the currently selected index var selectedBookIndex:int = myDataGrid.selectedIndex; // Delete the currently-selected book delete (myBooks.book[selectedBookIndex]); // Reselect the next logical item in the data grid newSelectedIndex = (selectedBookIndex==0) ? 0 : selectedBookIndex - 1; // Change the selected index of the data grid // at a later frame. See note on changeDataGridIndex() // method for more details on this workaround. callLater ( changeDataGridIndex ); } // This is a workaround for a known issue with // List-based components where deleting an item // from the control's dataProvider leaves the // selectedIndex at an incorrect value. The workaround // is to reassign the data provider at least a // frame later and to change the index there. private function changeDataGridIndex ():void { // Reassign the data grid's data provider myDataGrid.dataProvider = myBooks.book; // Set the selected index myDataGrid.selectedIndex = newSelectedIndex; // Validate the form to make sure that there // is actually a selection (that the grid is // not empty.) validateForm(); } // Perform simple form validation private function validateForm():void { // Is the form valid? formIsValid = isbnInput.text != "" && titleInput.text != "" && authorInput.text != "" && amazonUrlInput.text != ""; // Does a selection exist in the data grid? selectionExists = myDataGrid.selectedIndex != -1; } ]]> </fx:Script> <!-- User interface --> <s:Panel title="Assigning XML data" width="100%"> <s:layout> <s:VerticalLayout paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"/> </s:layout> <!-- List of books --> <mx:DataGrid id="myDataGrid" dataProvider="{myBooks.book}" change="validateForm();"> <mx:columns> <mx:DataGridColumn dataField="@ISBN" headerText="ISBN" width="85"/> <mx:DataGridColumn dataField="title" headerText="Title"/> <mx:DataGridColumn dataField="author" headerText="Author"/> <mx:DataGridColumn dataField="amazonUrl" headerText="Web site"> <mx:itemRenderer> <fx:Component> <mx:LinkButton label="Visit" click="navigateToURL(new URLRequest(data.amazonUrl), 'blank');"/> </fx:Component> </mx:itemRenderer> </mx:DataGridColumn> </mx:columns> </mx:DataGrid> <!-- New book form. Prepopulated with a book for easier testing. --> <mx:Form width="100%" autoLayout="false"> <mx:FormHeading label="New book details"/> <mx:FormItem label="ISBN:" width="100%"> <s:TextInput id="isbnInput" width="100%" text="1590596196" change="validateForm();"/> </mx:FormItem> <mx:FormItem label="Title:" width="100%"> <s:TextInput id="titleInput" width="100%" text="Object Oriented Actionscript for Flash 8" change="validateForm();"/> </mx:FormItem> <mx:FormItem label="Author:" width="100%"> <s:TextInput id="authorInput" width="100%" text="Peter Elst" change="validateForm();"/> </mx:FormItem> <mx:FormItem label="Amazon Url" width="100%"> <s:TextInput id="amazonUrlInput" width="100%" text="http://tinyurl.com/qxon2" change="validateForm();"/> </mx:FormItem> </mx:Form> <s:TextArea id="deb" width="100%"/> <s:controlBarContent> <s:Button label="Add book" click="addBookHandler();" enabled="{formIsValid}"/> <s:ComboBox id="addPosition" enabled="{formIsValid}"> <s:ArrayList> <fx:String>before</fx:String> <fx:String>after</fx:String> </s:ArrayList> </s:ComboBox> <s:ComboBox id="addRelativeTo" enabled="{formIsValid}"> <s:ArrayList> <fx:String>selected item.</fx:String> <fx:String>the list.</fx:String> </s:ArrayList> </s:ComboBox> <s:Button label="Delete book" click="deleteBookHandler();" enabled="{selectionExists}"/> </s:controlBarContent> </s:Panel> </s:Application>

 
Querying XML data

There are several ways to query an XML data source in E4X. The following example describes the following methods:
 
  • Searching numeric ranges by using logical operators (for example, all books with more than 300 pages)
  • Searching for a string within an element or attribute (for example, book with the word "Idiots" in its title)
  • Requesting an element from repeating nodes (for example, the title element for each of the books)
When you have the results of a query, you can use the for..in and for each..in loops to iterate through the results.
 
 
Example
<?xml version="1.0" encoding="utf-8"?> <!-- XMLQuery.mxml --> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="450" height="290" initialize="initializeHandler();"> <fx:Script> <![CDATA[ import flashx.textLayout.elements.ParagraphElement; import flashx.textLayout.elements.SpanElement; import flashx.textLayout.elements.TextFlow; import flashx.textLayout.conversion.TextConverter; import spark.utils.TextFlowUtil; // Model: XML structure describing // some of the books in my collection. [Bindable] private var myBooks:XML = <books> <book ISBN="1590595181"> <title>Foundation ActionScript Animation: Making Things Move</title> <author>Keith Peters</author> <amazonUrl>http://tinyurl.com/npuxt</amazonUrl> <pageCount>470</pageCount> </book> <book ISBN="1582346194"> <title>Send in the Idiots: Stories from the Other Side of Autism</title> <author>Kamran Nazeer</author> <amazonUrl>http://tinyurl.com/lo5ts</amazonUrl> <pageCount>500</pageCount> </book> <book ISBN="1590592212"> <title>Flash 3D Cheats Most Wanted</title> <author>Aral Balkan et. al.</author> <amazonUrl>http://tinyurl.com/gsd7b</amazonUrl> <pageCount>256</pageCount> </book> </books> private function initializeHandler():void { // Line length to truncate strings at when // displaying them var lineLength:uint = 50; // // Find books with more than 300 pages // var resultA:XMLList; resultA = myBooks.book.(pageCount > 300); // Display the found books using a for each..in // loop var tempString:String = new String(); var textFlow1:TextFlow = new TextFlow(); for each (var book:XML in resultA) { tempString += "<p>" + truncate(book.title, lineLength) + "</p>"; } textFlow1 = TextConverter.importToFlow(tempString, TextConverter.TEXT_FIELD_HTML_FORMAT); aText.textFlow = textFlow1; // // Find book with "Idiots" in the title // var resultB:XMLList; resultB = myBooks.book.(title.toString().search("Idiots")>-1); // Display the title of the found book bText.text = truncate(resultB.title, lineLength); // // Get the titles of all the books // var resultC:XMLList; resultC = myBooks.book..title; // Display the titles using a for..in loop tempString = ""; for (var bookTitle:String in resultC) { tempString += "<p>" + truncate(resultC[bookTitle], lineLength) + "</p>"; } textFlow1 = TextConverter.importToFlow(tempString, TextConverter.TEXT_FIELD_HTML_FORMAT); cText.textFlow = textFlow1; } // Helper method: Truncate a string at a given character count. Tries // to do this intelligently by truncating at a space if one exists in // the string (so that words are not truncated in the middle.) private function truncate ( str:String, numChars:uint, symbol:String = "..." ):String { // Don't do anything if the string is shorter than the maximum value. if (str.length <= numChars) return str; // Search backwards for a space in the string, starting with // the character position that was requested. var charPosition:uint = numChars-1; while (str.charAt(charPosition) != " " && charPosition != 0) { charPosition--; } var truncateAt:uint = charPosition == 0 ? numChars : charPosition; // If the space is right before a punctuation mark, crop the // punctuation mark also (or else it looks weird.) var charBefore:String = str.charAt(truncateAt-1); if (charBefore == ":" || charBefore == ";" || charBefore == "." || charBefore == ",") { truncateAt--; } // Truncate the string var newString:String = str.substr(0, truncateAt); newString += symbol; // Return the truncated string return newString; } ]]> </fx:Script> <!-- User interface --> <s:Panel title="XML lookup results" width="100%"> <s:layout> <s:VerticalLayout paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"/> </s:layout> <s:Label text="Query A - Books found:" fontWeight="bold"/> <s:RichText id="aText" width="100%"/> <s:Label text="Query B - Books found:" fontWeight="bold"/> <s:RichText id="bText" width="100%"/> <s:Label text="Query C - Books found:" fontWeight="bold"/> <s:RichText id="cText" width="100%"/> </s:Panel> </s:Application>