今天我們來看看AS3中新的XML處理方法:E4X,直到現(xiàn)在,ECMA腳本語言規(guī)范(ECMA-262)--AscriptScript 3.0的核心基礎(chǔ),并沒有提供任何的XML數(shù)據(jù)處理類或方法。AcriontScript之前的版本(從Flash 5中的ActionScript開始)有一些處理XML數(shù)據(jù)的類和方法,但是它們并不是基于ECMAScript標(biāo)準(zhǔn)的。

新的ECMA腳本語言規(guī)范第4版草稿中定義了一系列新的處理XML數(shù)據(jù)的類和方法。這些類和方法的集合并命名為E4X("ECMAScript for XML"),ActionScript 3.0包括如下新的E4X類: XMLXMLListQNameNamespace

E4X類的方法、屬性和操作的開法基于以下的目標(biāo):

  • 簡單--E4X盡可能的使得處理XML數(shù)據(jù)的代碼容易編寫并且易于理解。
  • 一致性--E4X的方法于Actionscript的其它部分協(xié)調(diào)一致。
  • 友好--實(shí)用非常好理解的操作符處理XML數(shù)據(jù),如點(diǎn)號(.)。

注意:為避免與E4X中的新的XML類沖突,原來ActionScript 2.0中的XML類在ActionScript 3.0被重命名為XMLDocument,為了向前兼容,在ActionScript 3.0中遺留下來的類--XML、XMLNode、XMLParser和XMLTag--被包含進(jìn)了flash.xml包中。新的E4X類是核心類--使用它們不需要import任何包。

初始化XML對象

XML對象可以代表一個(gè)XML元素、屬性、注釋、處理指令或文本元素。在ActionScript 3.0中我們可以直接將XML數(shù)據(jù)賦值給變量:

var myXML:XML =
   <order>
       <item id='1'>
           <menuName>burger</menuName>
           <price>3.95</price>
       </item>
       <item id='2'>
           <menuName>fries</menuName>
           <price>1.45</price>
       </item>
   </order>

你也可以使用new 構(gòu)造器來從XML數(shù)據(jù)文本創(chuàng)建一個(gè)XML對象實(shí)例:

var myXML:XML = new XML("<order><item id='1'><menuName>burger</menuName><price>3.95</price></item></order>")

如果XML數(shù)據(jù)不是格式完好的(如少了結(jié)束標(biāo)簽),那么將會(huì)出現(xiàn)運(yùn)行時(shí)錯(cuò)誤。

注意,你也可以將變量實(shí)例傳入XML數(shù)據(jù)中:

var tagname:String = "item";
var attributename:String = "id";
var attributevalue:String = 5;
var content:String = "Chicken";
var x:XML = <{tagname} {attributename}={attributevalue}>{content}</{tagname}>;
trace (x.toXMLString())
   // Output: <item id="5">Chicken</item>

通常,我們的應(yīng)用是從外部源導(dǎo)入XML數(shù)據(jù),如web service或RSS feed,以下是一個(gè)從遠(yuǎn)程URL導(dǎo)入XML數(shù)據(jù)的例子:

var myXML:XML = new XML();
var XML_URL:String = "http://www.example.com/Sample3.xml";
//創(chuàng)建URLRequest。
var myXMLURL:URLRequest = new URLRequest(XML_URL);
//使用URLLoader導(dǎo)入數(shù)據(jù)。
var myLoader:URLLoader = new URLLoader(myXMLURL);
//添加事件監(jiān)聽器,以在XML數(shù)據(jù)導(dǎo)入完成后處理XML數(shù)據(jù)。
myLoader.addEventListener("complete", xmlLoaded);
//導(dǎo)入完成后,創(chuàng)建使用導(dǎo)入的數(shù)據(jù)創(chuàng)建XML對象
function xmlLoaded(evtObj:Event) {
       var myXML:XML = XML(myLoader.data);
       trace("Data loaded.");

}

為了演示代碼的清晰性,本文中的大部份示例都第1種直接使用文本的方法創(chuàng)建XML對象。

E4X包含一些直觀的方法XML數(shù)據(jù)的操作符(如.和@:用于訪問屬性):

//獲取第1個(gè)item的menuName值
trace(myXML.item[0].menuName); // Output: burger
//獲取第1個(gè)item的id屬性值
trace(myXML.item[0].@id);//Output:1
//獲取id屬性為2的item的menuName值
trace(myXML.item.(@id==2).menuName); // Output: fries
//獲取menuName為burger的item的price值
trace(myXML.item.(menuName=="burger").price); // Output: 3.95

你也可以使用appendChild()方法給XML添加新的子節(jié)點(diǎn):

var newItem:XML =
   <item id="3">
       <menuName>medium cola</menuName>
       <price>1.25</price>
   </item>

myXML.appendChild(newItem);

當(dāng)然你也可以使用@和.操作符來更新數(shù)據(jù):

myXML.item[0].menuName="regular burger";
myXML.item[1].menuName="small fries";
myXML.item[2].menuName="medium cola";

myXML.item.(menuName=="regular burger").@quantity = "2";
myXML.item.(menuName=="small fries").@quantity = "2";
myXML.item.(menuName=="medium cola").@quantity = "2";

訪問XML數(shù)據(jù)

你可以使用.(點(diǎn)號)和..操作符訪問XML對象的子節(jié)點(diǎn),使用@操作符訪問某一節(jié)點(diǎn)的屬性。考慮以下XML對象:

 
var x:XML =
      <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>

對象x.book表示一個(gè)包含所有名稱為book的子節(jié)點(diǎn)的XMLList對象,該XMLList包含兩個(gè)XML對象(兩個(gè)book節(jié)點(diǎn))。

對象x..lastName表示一個(gè)包含XML樹結(jié)構(gòu)下部所有的lastName屬性的XMLList對象,該XMList包含兩個(gè)XML對象(兩個(gè)LastName屬性)。

對象x.book.editor.lastName表示一個(gè)包含所有x對象的所有名稱為book的子節(jié)點(diǎn)的所有名稱為editor的子節(jié)點(diǎn)的所有l(wèi)astName節(jié)點(diǎn)的XMLList對象,該XMLList只包含一個(gè)XML對象(值為"Case"的lastName屬性)。

訪問父節(jié)點(diǎn)和子節(jié)點(diǎn)

parent()方法返回XML對象的父節(jié)點(diǎn)。

你可以使用子節(jié)點(diǎn)列表的順序索引值來訪問特定的子節(jié)點(diǎn),例如,假定某一XML對象x有兩個(gè)名稱為book的子節(jié)點(diǎn),你可以如下訪問它們:

//第1個(gè)book節(jié)點(diǎn)
x.book[0]
//第2個(gè)book節(jié)點(diǎn)
x.book[1]

要訪問孫子節(jié)點(diǎn),我們可以如下直接使用兒子和孫子節(jié)點(diǎn)的索引值來訪問:

x.book[0].title[0]

不過如果x.book[0]只有一個(gè)名稱為title的子節(jié)點(diǎn)的話,那么可以省略索引:

x.book[0].title

類似的,如果x對象只有一個(gè)book子節(jié)點(diǎn),并且該book節(jié)點(diǎn)的子節(jié)點(diǎn)對象只有一個(gè)title對象,那么兩個(gè)索引值都可以省略:

x.book.title

注意,你也可以使用child()方法,直接使用名稱訪問特定的子節(jié)點(diǎn):

var x.XML =
       <order>
           <book>
               <title>Dictionary</title>
           </book>
       </order>

var childName:String = "book";
trace (x.child(childName).title) // Output: Dictionary

訪問屬性

我們使用使用@操作符訪問XMLNode屬性:

var myXML:XML =
   <order>
       <item id='1'>
           <menuName>burger</menuName>
           <price>3.95</price>
       </item>
       <item id='2'>
           <menuName>fries</menuName>
           <price>1.45</price>
       </item>
   </order>

//獲取第1個(gè)item的id屬性值
trace(myXML.item[0].@id);//Output:1

使用屬性或元素值過濾XML數(shù)據(jù)

我們可以使用特定的元素名稱和屬性值來定位到特定的元素考慮以下XML對象:

var x:XML =
   <employeeList>
       <employee id="347">
           <lastName>Zmed</lastName>
           <firstName>Sue</firstName>
           <position>Data analyst</position>
       </employee>
       <employee id="348">
           <lastName>McGee</lastName>
           <firstName>Chuck</firstName>
           <position>Jr. data analyst</position>
       </employee>
   </employeeList>

以下是正確的訪問方法:

//lastName為“McGee”的employee對象,第1個(gè)employee節(jié)點(diǎn)
x.employee.(lastName == "McGee") // The first employee node
//lastName為“McGee”的employee對象的firstName節(jié)點(diǎn),第1個(gè)employee節(jié)點(diǎn)的節(jié)點(diǎn)
x.employee.(lastName == "McGee").firstName // The firstName property of that node
//lastName為“McGee”的id屬性
x.employee.(lastName == "McGee").@id // The value of the id attribute
//所有id屬性值為347的employee對象列表
x.employee.(@id == 347)
//id屬性值為347的employee對象的lastName子節(jié)點(diǎn)
x.employee.(@id == 347).lastName
//所有id屬性值大于347的employee對象列表
x.employee.(@id > 300) // An XML list with both employee properties
//所有position子節(jié)點(diǎn)值包含“analyst”的employee對象列表
x.employee.(position.toString().search("analyst") > -1)

使用for ... in和for each ... in 語句

ActionScript 3.0 包括了一個(gè)新用來遍歷XMLList對象的的for ... in語句和for each ... in語句。例如,考慮以下XML對象,myXML和myXML..item XMLList對象(包含兩個(gè)item XML對象節(jié)點(diǎn)的XML列表):

var myXML:XML =
   <order>
       <item id='1' quantity='2'>
           <menuName>burger</menuName>
           <price>3.95</price>
       </item>
       <item id='2' quantity='2'>
           <menuName>fries</menuName>
           <price>1.45</price>
       </item>
   </order>

for ... in語句可以讓我們遍歷XMLList的所有屬性名稱,實(shí)際上就是個(gè)節(jié)點(diǎn)的索引值:

var total:Number = 0;
for (var pname:String in myXML..item)
{
    total += Number(myXML.item.@quantity[pname]) * Number(myXML.item.price[pname]);
}

for each ... in語句遍歷XMLList的所有節(jié)點(diǎn):

var total2:Number = 0;
for each (var item:XML in myXML..item)
{
  total2 += Number(item@quantity) * Number(item.price);
}

使用with語句

我們可以使用with語句,來指明后續(xù)的屬性和節(jié)點(diǎn)值都是基于某一XML對象,前面的for each ... in示例代碼,使用with語句的代碼如下:

var total:Number = 0;
for each (var item in myXML..item)
{
   with (item)
    {
         //{內(nèi)的屬性和節(jié)點(diǎn)對象都是基于item XML對象的,所有不需要使用item.來訪問。
         total += Number(@quantity) * Number(price);
     }

}
trace(total);

修改XML對象

我們可以使用prependChild()方法或者appendChild()方法在XML對象的子節(jié)點(diǎn)列表的前面或者最后面添加節(jié)點(diǎn):

var x1:XML = <p>Paragraph 1</p>
var x2:XML = <p>Paragraph 2</p>
var x:XML = <body></body>
x = x.appendChild(x1);
x = x.appendChild(x2);
x = x.prependChild(<p>Paragraph 0</p>);

// x == <body><p>Paragraph 0</p><p>Paragraph 1</p><p>Paragraph 2</p></body>

使用insertChildBefore()方法或者insertChildAfter()方法在特定的節(jié)點(diǎn)前面活著回后面添加節(jié)點(diǎn):

var x:XML =
   <body>
       <p>Paragraph 1</p> 
       <p>Paragraph 2</p>
   </body>

var newNode:XML = <p>Paragraph 1.5</p>
x = x.insertChildAfter(x.p[0], newNode)
x = x.insertChildBefore(x.p[2], <p>Paragraph 1.75</p>)

注意,我們也可以在構(gòu)造XML對象的時(shí)候使用大括號({和})來引用變量:

var ids:Array = [121, 122, 123];
var names:Array = [["Murphy","Pat"],["Thibaut","Jean"], ["Smith","Vijay"]]
var x:XML = new XML("<employeeList></employeeList>");

for (var i:int = 0; i < 3; i++) {
          var newnode:XML = new XML(); 
           newnode =
               <employee id={ids[i]}>
                   <last>{names[i][0]}</last>
                   <first>{names[i][1]}</first>
               </employee>

           x = x.appendChild(newnode)

}

我們也可以使用=操作符來給XML對象節(jié)點(diǎn)賦值:

var x:XML =
   <employee>
       <lastname>Smith</lastname>
   </employee>

x.firstname = "Jean";
x.@id = "239";

以上代碼將把XML對象X設(shè)置成如下:

<employee id="239">
   <lastname>Smith</lastname>
   <firstname>Jean</firstname>
   </employee>

我們也可以使用+和+=操作符來連結(jié)XMLList:

var x1:XML = <a>test1</a>
var x2:XML = <b>test2</b>
var xList:XMLList = x1 + x2;
xList += <c>test3</c>

刪除XML對象

E4X規(guī)范中定義有delete和deleteByIndex方法用來刪除特定的XML節(jié)點(diǎn),但是在當(dāng)前版本的ActionScript 3.0實(shí)現(xiàn)中,并沒有實(shí)現(xiàn)這兩個(gè)方法,所有我們不能直接使用這兩個(gè)方法,不過我們可以使用遍歷XML樹,剔除特定的節(jié)點(diǎn),重新構(gòu)造新的XML對象的方法來刪除某一特定節(jié)點(diǎn),以下我們將刪除employee的lastname子節(jié)點(diǎn):

private function deleteByIndex(xmlNode:XML,index:Number):XML{
    var newStr:String='';
    newStr+='<'+xmlNode.localName();
    for each (var att:XML in xmlNode.attributes()){
        newStr+=' '+att.localName()+'="'+att.toString()+'"';
    }
    newStr+='>';
    var i:Number=0;
    for each(var node:XML in xmlNode.children()){
        if(i!=index)
            newStr+=node.toXMLString();
        i++;
    }
    newStr+='</'+xmlNode.localName()+'/>';
    return new XML(newStr);
}

var myXML:XML=
   <employee id="239">
           <lastname>Smith</lastname>
           <firstname>Jean</firstname>
           <address>
           	<city>shangrao</city>
           	<load>daihu</load>
           	<no>5</no>
           </address>
       </employee>

myXML=deleteByIndex(myXML,0);

以上的deleteByIndex函數(shù)有兩個(gè)參數(shù),第1的參數(shù)是被刪除節(jié)點(diǎn)的父節(jié)點(diǎn),第2的參數(shù)是被刪除節(jié)點(diǎn)的在父節(jié)點(diǎn)的子節(jié)點(diǎn)列表中索引值。先遍歷父節(jié)點(diǎn)的索引屬性,然后遍歷其所有的子節(jié)點(diǎn),跳過我們要?jiǎng)h除的節(jié)點(diǎn),然后將它們組合成新的XML對象返回。

如果XML對象非常復(fù)雜,數(shù)據(jù)量必較大的話,以上刪除節(jié)點(diǎn)的實(shí)現(xiàn)方法效率是非常好低,所有正確的選擇還是使用E4X定義的刪除方法,不過這個(gè)功能要等到ActionScript 3.0的下一個(gè)測試版本才能夠?qū)崿F(xiàn)。

XML類型轉(zhuǎn)換

我們可以將XML對象和XMLList對象轉(zhuǎn)換為字符串,同樣的,我們也可以將字符串轉(zhuǎn)換為XML對象和XMLList對象。順便,請記住所有的XML屬性值、名稱和文本值都是字符串。

轉(zhuǎn)換XML和XMLList對象為字符串

XML對象和XMLList對象都有有兩個(gè)成員方法:toString()和toXMLString()方法。toXMLString()方法返回包含所有標(biāo)簽、屬性、命名空間聲明和XML對象內(nèi)容的字符串,對與復(fù)雜的XML對象(包含有子元素),toString()方法的效果和toXMLString()方法的效果一樣,但是對與簡單的XML對象(只包含一個(gè)文本元素),toString()方法只返回元素的文本內(nèi)容:

var myXML:XML =
  <order>
       <item id='1' quantity='2'>
           <menuName>burger</menuName>
           <price>3.95</price>
       </item>
   <order>

trace(myXML.item0.menuName.toXMLString())
    // Output: <menuName>burger</menuName>
trace(myXML.item0.menuName.toString())
    // Output: burger

將文本字符串轉(zhuǎn)換為XML對象

我們可以使用new構(gòu)造方法從字符串創(chuàng)建一個(gè)XML對象

var x:XML = new XML('<a>test<b>');

但是如果我們試圖將一個(gè)非XML或者結(jié)構(gòu)不完整的字符串轉(zhuǎn)換為XML對象,那么將會(huì)報(bào)運(yùn)行時(shí)錯(cuò)誤:

var x:XML = new XML('<a>test'); // Throws an error

從Internet讀取Rss Fead數(shù)據(jù)

以下代碼將讀取本站的rss fead數(shù)據(jù):

<?xml version="1.0" encoding="utf-8"?>
  <mx:Application xmlns:mx="http://www.macromedia.com/2005/mxml" xmlns="*" creationComplete="doInit()">
  <mx:Script>
  <![CDATA[
    private function doInit():Void{
      getRssData("http://blog.eshangrao.com/rss.php",ta_view);
    }

    public function getRssData(url:String, outTxt:TextArea):Void
    {
      private var myXMLURL:URLRequest = new URLRequest(url);
      private var myLoader:URLLoader = new URLLoader(myXMLURL);
      myLoader.addEventListener("complete", xmlLoaded);
    }
    private function xmlLoaded(event:Event):Void{
      ta_view.text='load ok';
      var myLoader:URLLoader = URLLoader(event.target);
      XML.ignoreProcessingInstructions=false;
      var myXML:XML =new XML(myLoader.data);
      private var outStr:String = "";
      for each (var item:XML in myXML.children()) {
        if(item.localName()=='item'){
          outStr += "<p><b>" + item.children()0.toString() + ":</b></p><p>";
          outStr += item.children()6.toString() ;
          outStr += " <br/><a href='" + item.children()1.toString();
          outStr += "'><font color='#008000'>More...</font></a></p>";
        }
      }
      //ta_view.text=myXML.toString();
      ta_view.htmlText = outStr;
    }
  ]]>
  </mx:Script>
  <mx:Canvas width="100%" height="100%">
    <mx:TextArea id="ta_view">
      <mx:layoutConstraints>
        <mx:EdgeAnchor bottom="10" top="10" left="10" right="10"/>
      </mx:layoutConstraints>
    </mx:TextArea>
  </mx:Canvas>
</mx:Application>

運(yùn)行示例(FlashPlayer8.5 required)

注意,我們沒有直接使用節(jié)點(diǎn)名稱訪問節(jié)點(diǎn)(不知道為什么,如果使用item.title訪問title節(jié)點(diǎn)的話,返回是空的,可能跟我的RSS XML的中rdf指令有關(guān),如果有朋友知道解決的辦法,請告訴我),而是使用了children()方法,該方法返回某一XML對象的所有子節(jié)點(diǎn)對象,

獲取更多AS3中E4X類的使用信息,請查看Adobe在線文檔,詳細(xì)API參考:Adobe Flex 2 API文檔