代码之家  ›  专栏  ›  技术社区  ›  Jeff Winkworth

AS3XML解析的最佳实践

  •  1
  • Jeff Winkworth  · 技术社区  · 18 年前

    我在flash中解析各种类型的XML(特别是FeedBurner RSS文件和YouTube数据API响应)时遇到了一些问题。我用的是 URLLoader 加载XML文件,然后 Event.COMPLETE 创建新的XML对象。75%的时间都很好,而且我时不时会遇到这种情况:

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

    我们认为问题是XML太大,而且 事件在实际从 URL加载程序 . 我们提出的唯一解决方案是在事件发生时设置一个计时器,并在开始解析数据之前“等待几秒钟”。当然这不是最好的办法。

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

    2008年9月2日更新 我们得出以下结论,此时在代码中触发了exclption:

    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真正解析到对象之前,我计算对象数量的循环开始了)?


    @西奥:谢谢你的忽略空白提示。另外,我们已经确定在事件准备好之前调用它(我们做了一些测试跟踪 mainXMLLoader.bytesLoaded + "/" + mainXMLLoader.bytesLoaded

    10 回复  |  直到 10 年前
        1
  •  1
  •   Re0sless    18 年前

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

    URLLoader.bytesLoaded == URLLoader.bytesTotal
    

    这应该会告诉您,如果文件已完成加载,它将不会有助于提前触发完成事件,但它应该告诉您是否是xml读取的问题。

        2
  •  1
  •   sergtk    18 年前

    我担心的是它可能会发射事件完成在加载完成之前,这让我想知道加载是否超时。

    出于测试目的,请尝试跟踪 URLLoader.bytesLoaded 以及 URLLoader.bytesTotal 在你的顶部 Event.COMPLETE 处理程序方法。如果它们不匹配,则说明事件过早触发。如果是这种情况,您可以监听URLLoader的progress事件。检查 bytesLoaded 反对 bytesTotal 事件完成 ,但如果坏了,你可以试试自己的。

    请告诉我们你发现了什么。如果可以,请粘贴一些源代码。我们也许能发现一些值得注意的东西。

        3
  •  1
  •   Theo    18 年前

    XML.ignoreWhitespace;
    

    因为 ignoreWhitespace 是一种财产。你必须设置为 true 这样地:

    XML.ingoreWhitespace = true;
    
        4
  •  0
  •   vanhornRF    18 年前

    你可以试着用事件进展事件在下载XML时持续监视它,然后按照Re0sless的建议,检查bytesloated与bytesTotal,并在两个数字相等时开始XML解析,而不是使用事件完成事件。

    您应该能够获得bytesloated和bytesTotal数字,而不考虑域,如果您可以访问该文件,则可以访问其字节信息。

        5
  •  0
  •   Theo    18 年前

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

        6
  •  0
  •   Jeff Winkworth    18 年前

    这是原始代码(当我问这个问题时):

    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很难重现,所以据我所知,我没有修复任何东西,而是添加了一些无用的代码。

        7
  •  0
  •   Theo    18 年前

    Event.COMPLETE 除非加载程序已完全加载,否则不应调用处理程序,这是没有意义的。您是否确认它实际上没有完全加载(通过查看 bytesLoaded 对比。 bytesTotal 您跟踪的值)?如果 事件完成 事件在之前调度 bytesLoaded == bytesTotal

    很好,你可以用计时器工作,但很奇怪你需要它。

        8
  •  0
  •   Theo    18 年前

    我建议你在 https://bugs.adobe.com/flashplayer/ ,因为在加载所有字节之前不应触发事件。同时,我想你必须和计时器一起生活。你也许可以通过监听progress事件来达到同样的效果,这也许可以让你省去自己处理计时器的麻烦。

        9
  •  0
  •   enzuguri    17 年前

    有时候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数据格式设置为URLLoaderDataFormat.TEXT,还添加了pramano cache的url头和/或向url添加了一个cache buster?

    只是一些建议。。。

        10
  •  0
  •   Will Brian Hodge    10 年前

    您可以在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确实不完整,并强制它再次加载。