2008-08-20 75 views
1

我一直在解析Flash中的各種XML(特別是FeedBurner RSS文件和YouTube數據API響應)時遇到了一些麻煩。我正在使用URLLoader加載XML文件,並使用Event.COMPLETE創建新的XML對象。的75%的時間這項工作很好,而現在每一次我得到這種類型的異常:AS3 XML解析的最佳實踐

TypeError: Error #1085: The element type "link" must be terminated by the matching end-tag "</link>". 

我們思考的問題是,XML是大,也許之前的XML是Event.COMPLETE事件被觸發實際上是從URLLoader下載的。我們提出的唯一解決方案是在事件發生時啓動計時器,並在開始解析數據之前基本「等待幾秒鐘」。當然,這不是做這件事的最好方法。

在Flash中解析XML有沒有什麼可靠的方法?

更新2008年9月2日我們總結以下,excption在代碼在這一點上觸發:

data = new XML(mainXMLLoader.data); 

// calculate the total number of entries. 
for each (var i in data.channel.item){ 
    _totalEntries++; 
} 

我已經放在一個try/catch語句解決這個部分,我目前顯示屏幕上出現錯誤信息。我的問題是如果一個不完整的文件如果bytesLoaded == bytesTotal


我已經用狀態報告更新了原始問題;我想另一個問題可能是有沒有一種方法來確定是否在訪問數據之前對對象進行了正確解析(如果錯誤是我的循環計數對象的數量在XML被實際解析到對象之前開始)?


@Theo:Thanks for the ignoreWhitespace tip。此外,我們已經確定,該事件之前的準備叫(我們做了一些測試,跟蹤mainXMLLoader.bytesLoaded + "/" + mainXMLLoader.bytesLoaded

回答

1

您是否嘗試過檢查裝載的字節是一樣的總字節數?

URLLoader.bytesLoaded == URLLoader.bytesTotal 

這應該告訴如果文件已經完成加載,它不會幫助完成事件發射到早期,但它應該告訴你它是否讀取了xml的問題。

我不確定它是否會在域上工作,因爲我的xml始終在同一網站上。

+1

邊注:的bytesLoaded是一樣的BYTESTOTAL它開始加載,以及(他們均爲0)之前,所以我會檢查的條件的onComplete其中: 的URLLoader。 bytesLoaded == URLLoader.bytesTotal && bytesTotal> 0 – 2008-09-25 10:47:37

0

正如你在你的問題中提到的那樣,這個問題很可能是你的程序在實際完全下載之前查看了XML,我不知道有一種確定的方法來「解析」XML,因爲解析部分你的代碼很可能沒有問題,這只是它是否實際下載的問題。

您可以嘗試使用ProgressEvent.PROGRESS事件來持續監視下載的XML,然後按照Re0sless的建議,檢查bytesLoaded與bytesTotal,並在兩個數字相等時開始XML解析,而不是使用Event.COMPLETE事件。

如果您可以訪問文件,您可以訪問其字節信息,您應該能夠獲得bytesLoaded和bytesTotal數字,無論域如何。

1

對我而言,有關的事情是,它可能在完成加載之前觸發Event.COMPLETE,這讓我懷疑加載是否超時。

問題發生的頻率如何?你能否在一瞬間取得成功,然後用相同的飼料失敗呢?

出於測試目的,請嘗試在Event.COMPLETE處理程序方法的頂部跟蹤URLLoader.bytesLoadedURLLoader.bytesTotal。如果它們不匹配,就會知道該事件正在提前發射。如果是這種情況,您可以監聽URLLoader的進度事件。檢查bytesLoaded與您的處理程序中的bytesTotal,並且只在加載真正完成時才解析XML。誠然,這很可能類似於URLLoader在觸發Event.COMPLETE之前正在做的事情,但如果這種情況被破壞,您可以嘗試滾動自己的。

請讓我們知道您發現了什麼。如果可以的話,請粘貼一些源代碼。我們可能會發現一些注意事項。

0

如果你可以發佈更多的代碼,我們可能會發現問題。

另一件事測試(除了追蹤bytesTotal)是跟蹤裝載機的data財產在Event.COMPLETE處理,只是爲了看看XML數據實際上是正確加載,例如檢查是有</link>那裏。

0

@Brian Warshaw:這個問題只發生在大約10-20%的時間。有時它打嗝,只是重新加載應用程序將工作正常,其他時間我會花半個小時重新加載應用程序一遍又一遍,無濟於事。

這是原來的代碼(當我問的問題):

public class BlogReader extends MovieClip { 
    public static const DOWNLOAD_ERROR:String = "Download_Error"; 
    public static const FEED_PARSED:String = "Feed_Parsed"; 

    private var mainXMLLoader:URLLoader = new URLLoader(); 
    public var data:XML; 
    private var _totalEntries:Number = 0; 

    public function BlogReader(url:String){ 
     mainXMLLoader.addEventListener(Event.COMPLETE, LoadList); 
     mainXMLLoader.addEventListener(IOErrorEvent.IO_ERROR, errorCatch); 
     mainXMLLoader.load(new URLRequest(url)); 
     XML.ignoreWhitespace; 
    } 
    private function errorCatch(e:IOErrorEvent){ 
     trace("Oh noes! Yous gots no internets!"); 
     dispatchEvent(new Event(DOWNLOAD_ERROR)); 
    } 
    private function LoadList(e:Event):void { 
     data = new XML(e.target.data); 

     // calculate the total number of entries. 
     for each (var i in data.channel.item){ 
      _totalEntries++; 
     } 

     dispatchEvent(new Event(FEED_PARSED)); 
    } 
} 

這是我寫的基礎上Re0sless'原答覆代碼(類似於提到的一些建議):

public class BlogReader extends MovieClip { 
    public static const DOWNLOAD_ERROR:String = "Download_Error"; 
    public static const FEED_PARSED:String = "Feed_Parsed"; 

    private var mainXMLLoader:URLLoader = new URLLoader(); 
    public var data:XML; 
    protected var _totalEntries:Number = 0; 

    public function BlogReader(url:String){ 
     mainXMLLoader.addEventListener(Event.COMPLETE, LoadList); 
     mainXMLLoader.addEventListener(IOErrorEvent.IO_ERROR, errorCatch); 
     mainXMLLoader.load(new URLRequest(url)); 
     XML.ignoreWhitespace; 
    } 
    private function errorCatch(e:IOErrorEvent){ 
     trace("Oh noes! Yous gots no internets!"); 
     dispatchEvent(e); 
    } 
    private function LoadList(e:Event):void { 
     isDownloadComplete();   
    } 
    private function isDownloadComplete() { 
     trace (mainXMLLoader.bytesLoaded + "/" + mainXMLLoader.bytesLoaded); 
     if (mainXMLLoader.bytesLoaded == mainXMLLoader.bytesLoaded){ 
      trace ("xml fully loaded"); 

      data = new XML(mainXMLLoader.data); 

      // calculate the total number of entries. 
      for each (var i in data.channel.item){ 
       _totalEntries++; 
      } 

      dispatchEvent(new Event(FEED_PARSED)); 
     } else { 
      trace ("xml not fully loaded, starting timer"); 
      var t:Timer = new Timer(300, 1); 
      t.addEventListener(TimerEvent.TIMER_COMPLETE, loaded); 
      t.start(); 
     } 
    } 
    private function loaded(e:TimerEvent){ 
     trace ("timer finished, trying again"); 
     e.target.removeEventListener(TimerEvent.TIMER_COMPLETE, loaded); 
     e.target.stop(); 

     isDownloadComplete(); 
    } 
} 

我會指出,因爲添加代碼確定如果mainXMLLoader.bytesLoaded == mainXMLLoader.bytesLoaded我沒有問題 - 說,這個錯誤很難重現,所以我知道我沒有修復任何東西,而是添加無用的代碼。

1

只是一個側面說明,這種說法沒有任何效果:

XML.ignoreWhitespace; 

因爲ignoreWhitespace是一個屬性。你必須將其設置爲true這樣的:

XML.ingoreWhitespace = true; 
0

Event.COMPLETE處理真的不應該被稱爲除非裝載機滿載,它是沒有意義的。您是否確認它實際上並未完全加載(通過查看您跟蹤的bytesLoadedbytesTotal值)?如果Event.COMPLETE事件在bytesLoaded == bytesTotal之前發送,那是一個錯誤。

好,你已經得到它與定時器,但它是非常奇怪的,你需要它。

0

我建議你在https://bugs.adobe.com/flashplayer/上提交一個錯誤報告,因爲在加載所有字節之前事件確實不應該觸發。在此期間,我想你必須忍受這個計時器。您也許可以通過監聽進度事件來做同樣的事情,這可能會讓您不必親自處理計時器。

0

您可以在XML文檔的最後一個元素名稱空間中設置一個屬性「value」等於「true」的元素名稱空間;

//The XML 
//Flash ignores the line that specifies the XML version and encoding so I have here as well. 

<parent> 
    <child name="child1" /> 
    <child name="child2" /> 
    <child name="child3" /> 
    <child name="child4" /> 
    <documentEnd value="true" /> 
</parent> 

//Sorry about the spacing, but it is difficult to get XML to show. 

//Flash 
var loader:URLLoader = new URLLoader(); 
var request:URLRequest = new URLRequest('pathToXML/xmlFileName.xml'); 

var xml:XML; 

//Event Listener with weak reference set to true (5th parameter); 
//The above comment does not define a required practice, this is to aid with garbage collection. 

loader.addEventListener(Event.COMPLETE, onXMLLoadComplete, false, 0, true); 
loader.load(request); 
function onXMLLoadComplete(e:Event):void 
{ 
    xml = new XML(e.target.data); 

    //Now we check the last element (child) to see if it is documentEnd. 
    if(xml[xml.length()-1][email protected] == "true") 
    { 
     trace("Woot, it seems your xml made it!"); 
    } 
    else 
    { 
     //Attempt the load again because it seems it failed when it was unable to find documentEnd in the XML Object. 
     loader.load(request); 
    } 
} 

我希望這可以幫助你現在,但真正的希望是,足夠多的人,讓土坯瞭解這個問題。不能依靠事件是件可悲的事情。但是我必須說,從我聽說過的XML來看,它在大規模上並不是最優的,並且相信這是在您需要像AMFPHP這樣的序列化數據的時候。

希望這會有所幫助!記住這裏的想法是,我們知道XML中最後一個子元素是什麼,因爲我們設置了它!我們沒有理由不能訪問最後一個子元素,但如果我們不能,我們必須假設XML確實沒有完成,我們強制它再次加載。

0

有時RSS服務器頁面可能無法吐出正確和有效的XML數據,特別是如果您不斷點擊它,所以它可能不是你的錯。您是否嘗試過在網頁瀏覽器中打開頁面(最好使用xml validator插件)以檢查服務器響應是否始終有效?

,我可以在這裏看到的唯一的另一件事是行:

xml = new XML(event.target.data); 

//the data should already be XML, so only casting is necessary 
xml = XML(event.target.data); 

您是否也嘗試過的URLLoader DATAFORMAT設置爲URLLoaderDataFormat.TEXT,則也加入prama,無緩存的URL頭和/或添加一個緩存剋星他的網址?

只是一些建議...