AS3 XML解析的最佳实践

时间:2008-08-20 13:15:57

标签: xml flash actionscript-3 rss fileparse

我在flash中解析各种类型的XML(特别是FeedBurner RSS文件和YouTube Data API响应)时遇到了一些麻烦。我正在使用URLLoader加载XML文件,并在Event.COMPLETE创建新的XML对象。 75%的时间这个工作正常,并且我不时地得到这种类型的例外:

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

我们认为问题在于XML很大,并且在从Event.COMPLETE实际下载XML之前可能会触发URLLoader事件。我们提出的唯一解决方案是在事件上启动计时器,并在开始解析数据之前基本上“等待几秒钟”。当然,这不是最好的方法。

在Flash中解析XML有什么可靠的方法吗?

2008年9月2日更新我们已经得出以下结论,此时在代码中触发了这个问题:

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对象(如果错误是我的循环计算对象数量在实际XML之前开始)解析成对象)?


@Theo:感谢ignoreWhitespace提示。此外,我们已确定事件在准备好之前被调用(我们做了一些跟踪mainXMLLoader.bytesLoaded + "/" + mainXMLLoader.bytesLoaded

的测试

10 个答案:

答案 0 :(得分:1)

您是否尝试检查加载的字节是否与总字节数相同?

URLLoader.bytesLoaded == URLLoader.bytesTotal

那应该告诉你文件是否已经完成加载,它不会帮助解决早期的补充事件,但它应该告诉你它是否已经读取了xml的问题。

我不确定它是否适用于域名,因为我的xml始终位于同一网站上。

答案 1 :(得分:1)

对我来说,有关的事情是它可能在它加载完成之前触发Event.COMPLETE,这让我想知道负载是否超时。

问题多久发生一次?你可以在一个时刻取得成功,然后用相同的饲料失败吗?

出于测试目的,请尝试跟踪URLLoader.bytesLoaded处理程序方法顶部的URLLoader.bytesTotalEvent.COMPLETE。如果它们不匹配,您就会知道该事件过早发生。如果是这种情况,您可以侦听URLLoader的progress事件。检查处理程序中bytesLoaded的{​​{1}},并在加载完全正确后解析XML。当然,这很可能类似于URLLoader在触发bytesTotal之前所做的事情,但是如果它被破坏了,你可以尝试自己滚动。

请告诉我们您的结果。如果可以,请粘贴一些源代码。我们或许可以发现一些值得注意的事情。

答案 2 :(得分:1)

请注意,此声明无效:

XML.ignoreWhitespace;

因为ignoreWhitespace是一个属性。您必须将其设置为true,如下所示:

XML.ingoreWhitespace = true;

答案 3 :(得分:0)

正如您在问题中提到的,问题很可能是您的程序在实际完全下载之前查看了XML,我不知道有一种“解析”XML的绝对方法,因为解析你的代码部分很可能很好,这只是它是否实际下载的问题。

您可以尝试使用ProgressEvent.PROGRESS事件在下载时继续监视XML,然后按照Re0sless建议,检查bytesLoaded与bytesTotal,并在两个数字相等而不是使用Event时开始XML解析.COMPLETE事件。

无论域名如何,您都应该能够获得bytesLoaded和bytesTotal数字,如果您可以访问该文件,则可以访问其字节信息。

答案 4 :(得分:0)

如果您可以发布更多代码,我们可能会发现问题。

要测试的另一件事(除了跟踪bytesTotal)是跟踪data处理程序中加载程序的Event.COMPLETE属性,只是为了查看XML数据是否实际正确加载,例如,检查那里有</link>

答案 5 :(得分: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我是否还没有问题 - 这就是说,这个bug很难重现,所以我知道我没有修复任何东西,而是只是添加了无用的代码。

答案 6 :(得分:0)

除非加载器已完全加载,否则不应该调用Event.COMPLETE处理程序,这没有任何意义。您是否确认它实际上未满载(通过查看您追踪的bytesLoadedbytesTotal值)?如果在Event.COMPLETE之前调度bytesLoaded == bytesTotal事件是一个错误。

很好,你已经使用了计时器,但你需要它很奇怪。

答案 7 :(得分:0)

我建议您在https://bugs.adobe.com/flashplayer/提交错误报告,因为在加载所有字节之前,事件确实不应该触发。与此同时,我猜你必须忍受计时器。你也许可以通过听取progress事件来做同样的事情,这可能会让你不必自己处理定时器。

答案 8 :(得分: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].documentEnd.@value == "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);
   }
}

我希望这对你现在有所帮助,但真正的希望是有足够的人让adobe知道这个问题。不能依赖事件是一件令人伤心的事情。我必须说,从我所听到的关于XML的内容来看,它并不是非常优化的,并且相信这就是当你需要像AMFPHP这样的东西来序列化数据时。

希望这有帮助!请记住,这里的想法是我们知道XML中最后一个子元素是什么,因为我们设置它!我们没有理由不能访问最后一个子元素,但如果我们不能,我们必须假设XML确实不完整,我们强制它再次加载。

答案 9 :(得分:0)

有时RSS服务器页面可能无法吐出正确有效的XML数据,尤其是如果您经常点击它,那么这可能不是您的错。您是否尝试在Web浏览器中访问该页面(最好使用xml验证器插件)来检查服务器响应是否始终有效?

我能在这里看到的另一件事就是这一行:

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-no-cache的url标头和/或添加了一个缓存存储器?

只是一些建议......