Actionscript 3.0辦好一組類,基於ECMAScript for XML(E4X)說明(ECMA-357 2第二版)。這些類功能強大,使用簡單,對處理XMLdata數據非常有用。使用E4X,使用XML數據更快的開發代碼稱爲可能,對比以前的編程技術。作爲一個好處,你生產的代碼更容易讀懂了。
- 介紹XML
- 對XML元素與屬性賦值
- 通過傳遞數據引用創建XML對象
- 裝配與改變XML對象
- 查詢XML數據
介紹XML
許多服務器端應用程序使用XML數據結構,那麼你可以在ActionScript中使用XML類來創建優雅的富互聯網應用程序。例如那些鏈接到Web service的應用。web service是連接應用程序的重要的方法,例如,一個Adobe Flash Player9 應用程序和一個在web服務器上的應用程序通過公共標準,例如Simple Object Access Protocol(SOAP)
在Adobe Flex中,ECMAScript for XML說明書定義了一系列的類和功能來處理XML數據。這些類和函數的集合被稱爲E4X.兩個主要的類是XML和XMLList。
注意:在ActionScript 2.0中有一個XML類。在ActionScript 3.0中,它被重命名爲XMLDocument這樣就不會與新的,作爲E4X一部分的XML類發生衝突了。在ActionScript 3.0中,上一個版本遺留的類——XMLDocument,XMLNode,XMLParser和XMLTag——都被包含在flash.xml包中,主要是爲了向下兼容。E4X類是核心類;你需要導入包才能使用他們。本快速說明沒有設計對傳統的ActionScript 2.0的類逐一細說。想了解他們,查看flash.xml包,在Flex 3 Language Reference中
在下邊的例子中,你創建了一個XML文檔,命名爲myBooks。創建一個XML文檔在ActionScript,通過在Actionscript塊中書寫XML並賦值給一個變量。由於在Flex中,XML是本地數據類型,就像Number或Boolean一樣。
myBooks的XML文檔包含兩個book元素(element)(也被稱爲node(節點))。第一個book元素擁有4個子元素,名稱值title,author,amazonUrl和pageCount。
要通過XML實例訪問元素,使用點標示(.)就像存取一個對象的屬性一樣。那麼,舉個例子,要獲得book節點的引用,你要寫成myBooks.book。這就返回了一個XMLList時間,他包含了myBooks的XML中的2個book節點。要存取列表中指定節點,你需要使用數組表示法。例如,要獲得第一本數節點的引用,可以寫爲myBooks.book[0]。如果你使用過ActionScript中的對象和數組,你應該對點操作符和數組語法風格比較熟悉。然而,E4X比這更進一步,可以按指定屬性名在XML中搜索節點。
下邊的例子中,使用搜索ISBN屬性獲得第一本書的引用。屬性在 E4X中at-sign(@)爲前綴描述屬性,寫爲@ISBN。語句myBooks.book.(@ISBN==”159059181”)翻譯爲“找到一個屬性ISBN值等於159059181的book節點”。另一個例子描述更高級的查找技術。
例子
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
width="440" height="400"
initialize="initializeHandler();"
><mx:Script>
<![CDATA[
[Bindable] public var a:XMLList;
[Bindable] public var b:XMLList;
[Bindable] public var c:XMLList;
[Bindable] public var d:XMLList;// 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>
</books>
private function initializeHandler():void{
// An XML list that contains both book nodes.
a = myBooks.book;
// Keith Peters
b = myBooks.book[0].author;
// 470c = myBooks.book.(@ISBN=="1590595181").pageCount;
// Delete the first book node.
delete myBooks.book[0];
// Send in the Idiots...
d = myBooks.book[0].title;
}
]]>
</mx:Script>
<!-- User interface -->
<mx:Panel
title="XML lookup results"
paddingBottom="10" paddingLeft="10" paddingRight="10" paddingTop="10"><mx:Text text="{'a: ' + a}" width="300"/>
<mx:Label text="{'b: ' + b}"/>
<mx:Label text="{'c: ' + c}"/><mx:Label text="{'d: ' + d}"/>
</mx:Panel></mx:Application>
運行結果
對XML元素與屬性賦值
使用@和點( . )操作符不只可以從XML結構中讀取數據的值,也可以爲其賦值。
在下邊的例子中,創建一個XML結構master-detail視圖。master視圖包含一個DataGrid 組件,用來顯示書的列表。detail視圖包含控件,用來編輯master視圖中當前選中的圖書。
master 和detail視圖使用數據綁定和E4X來讀取和更新XML中的數據。
下邊的例子通過一下方法使用E4X:
- myBooks.book引用book元素的XMLList對象。
- myBook.book[selectedBookIndex]引用當前被選中的book元素。
- myBook.book[selectedBookIndex].title引用當前被選中book元素的子元素title
要使用當前被選中圖書的title更新TestInput控件tiltleInput,需要綁定TestInput控件tiltleInput的text屬性到myBooks.book[selectedBookIndex].title。類似地,要使用用戶最終輸入更新XML,當TestInput控件tiltleInput的text屬性改變時,將TestInput控件tiltleInput的text屬性的值賦值給myBooks.book[selectedBookIndex].title。
detail視圖中的另一個組件使用同樣的方法正確的工作。
例子
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
width="500" height="470"
creationComplete="myDataGrid.selectedIndex=0;"
><mx: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>
]]>
</mx:Script>
<!-- Keep track of the currently selected book -->
<mx:Number id="selectedBookIndex">{myDataGrid.selectedIndex}</mx:Number>
<!-- User interface -->
<mx:Panel
title="Assigning XML data"
paddingBottom="10" paddingLeft="10" paddingRight="10" paddingTop="10"
><!-- 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>
<mx:Component>
<mx:LinkButton
label="Visit"
click="navigateToURL(new URLRequest(data.amazonUrl), 'blank');"
/>
</mx: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%">
<mx: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%">
<mx: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%">
<mx: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%">
<mx:TextInput
id="amazonUrlInput"
width="100%"
text="{myBooks.book[selectedBookIndex].amazonUrl}"
change="{myBooks.book[selectedBookIndex].amazonUrl = amazonUrlInput.text}"
/></mx:FormItem>
</mx:Form>
</mx:Panel>
</mx:Application>
運行結果
通過傳遞數據引用創建XML對象
前邊介紹XML的例子展示了使用XML文檔初始化XML對象的方法。當創建一個XML文檔時,你也可以通過引用(來自其他變量的引用)傳遞數到XML對象中,通過大括號擴起來的變量值引用。
如果你創建的XML結構不是有效的XML,你會看到類型錯誤的運行時錯誤。
下邊的例子動態的創建了一個XML對象。它基於用戶提供的變量來創建屬性名,屬性值,標籤名和標籤內容。例子檢查了TypeError的情況,通過使用try…catch塊環繞XML初始化代碼。
例子
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
width="500" height="400"
initialize="createXML();"
>
<mx: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>;
}}
]]>
</mx:Script>
<!-- User interface -->
<mx:Panel
title="Passing XML data by reference"
layout="horizontal"
><mx:Form>
<mx:FormItem label="Tag name:">
<mx:TextInput id="tagName" change="createXML();"/>
</mx:FormItem><mx:FormItem label="Attribute name:">
<mx:TextInput id="attributeName" change="createXML();"/></mx:FormItem>
<mx:FormItem label="Attribute value:">
<mx:TextInput id="attributeValue" change="createXML();"/>
</mx:FormItem><mx:FormItem label="Tag content:">
<mx:TextInput id="content" change="createXML();"/>
</mx:FormItem>
<mx:HRule width="100%"/>
<!-- Display the resulting XML -->
<mx:TextArea
editable="false"
width="300" height="50"
text="{xml.toXMLString()}"
/></mx:Form>
</mx:Panel>
</mx:Application>
運行結果
裝配與改變XML對象
出可以對XML元素與對象賦值外,你也可以通過使用E4X組裝與傳遞XML對象。
下邊的例子中,你可以使用prependChild()和append()方法來向XML對象的列表中的前邊或後邊增減一個屬性。類似的,使用insertChildBefore()方法和insertchildAfter()方法來增加屬性在指定的屬性之前或之後。要刪除元素,使用delete()方法從XML中移除節點。
例子
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
width="500" height="600"
creationComplete="myDataGrid.selectedIndex=0; validateForm();">
<mx: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 item's
// new index will be one greater, so select that.)
var newSelectedIndex:uint = (addPosition.selectedIndex == BEFORE) ? selectedBookIndex + 1: selectedBookIndex;
myDataGrid.selectedIndex = newSelectedIndex;
}
// Delete selected bookprivate 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;
}]]>
</mx:Script>
<!-- User interface -->
<mx:Panel
title="Assigning XML data"
paddingBottom="10" paddingLeft="10" paddingRight="10" paddingTop="10"
><!-- 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>
<mx:Component>
<mx:LinkButton
label="Visit"
click="navigateToURL(new URLRequest(data.amazonUrl), 'blank');"
/>
</mx: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%">
<mx:TextInput
id="isbnInput"
width="100%"
text="1590596196"
change="validateForm();"
/></mx:FormItem>
<mx:FormItem label="Title:" width="100%">
<mx:TextInput
id="titleInput"
width="100%"
text="Object Oriented Actionscript for Flash 8"
change="validateForm();"
/></mx:FormItem>
<mx:FormItem label="Author:" width="100%">
<mx:TextInput
id="authorInput"
width="100%"
text="Peter Elst"
change="validateForm();"
/></mx:FormItem>
<mx:FormItem label="Amazon Url" width="100%">
<mx:TextInput
id="amazonUrlInput"
width="100%"
text="http://tinyurl.com/qxon2"
change="validateForm();"
/></mx:FormItem>
</mx:Form>
<mx:TextArea id="deb" width="100%"/>
<mx:ControlBar><mx:Button
label="Add book"
click="addBookHandler();"
enabled="{formIsValid}"
/>
<mx:ComboBox id="addPosition" enabled="{formIsValid}"><mx:String>before</mx:String>
<mx:String>after</mx:String>
</mx:ComboBox>
<mx:ComboBox
id="addRelativeTo"
enabled="{formIsValid}"
><mx:String>selected item.</mx:String>
<mx:String>the list.</mx:String>
</mx:ComboBox>
<mx:VRule height="15"/><mx:Button
label="Delete book"
click="deleteBookHandler();"
enabled="{selectionExists}"
/>
</mx:ControlBar></mx:Panel>
</mx:Application>
查詢XML數據
在E4X中,查詢XML數據有幾種方式。下邊的例子展示了一下幾種方式:
- 使用邏輯操作搜索數字範圍(例如,所有大於300頁的圖書)
- 在元素或屬性上查詢字符串(例如,標題包含ldiots的圖書)
- 從重複的節點請求元素(例如,從books中title元素)
當你擁有了查詢結果,你可以使用for..in和 for each..in循環來遍歷結果。
例子
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
viewSourceURL="src/XMLQuery/index.html"
width="450" height="290"
initialize="initializeHandler();"
><mx: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><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 = "<ul>";
for each (var book:XML in resultA){
tempString += "<li>" + truncate(book.title, lineLength) + "</li>";
}tempString += "</ul>";
aText.htmlText = tempString;
//
// 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.htmlText = "<ul><li>" + truncate(resultB.title, lineLength) + "</li></ul>";
//// Get the titles of all the books.
//
var resultC:XMLList;
resultC = myBooks.book..title;
// Display the titles using a for..in loop
tempString = "<ul>";
for (var bookTitle:String in resultC){
tempString += "<li>" + truncate(resultC[bookTitle], lineLength) + "</li>";
}tempString += "</ul>";
cText.htmlText = tempString;
}
// 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 backward 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;
}]]>
</mx:Script>
<!-- User interface --><mx:Panel
title="XML lookup results" width="100%"
paddingBottom="10" paddingLeft="10" paddingRight="10" paddingTop="10"><mx:Label text="Query A - Books found:" fontWeight="bold"/>
<mx:Text id="aText" width="100%"/><mx:Label text="Query B - Books found:" fontWeight="bold"/>
<mx:Text id="bText" width="100%"/><mx:Label text="Query C - Books found:" fontWeight="bold"/>
<mx:Text id="cText" width="100%"/></mx:Panel>
</mx:Application>