AS3 XML解析的最佳实践
我一直在解析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
您是否尝试过检查装载的字节是一样的总字节数?
URLLoader.bytesLoaded == URLLoader.bytesTotal
这应该告诉如果文件已经完成加载,它不会帮助完成事件发射到早期,但它应该告诉你它是否读取了xml的问题。
我不确定它是否会在域上工作,因为我的xml始终在同一网站上。
正如你在你的问题中提到的那样,这个问题很可能是你的程序在实际完全下载之前查看了XML,我不知道有一种确定的方法来“解析”XML,因为解析部分你的代码很可能没有问题,这只是它是否实际下载的问题。
您可以尝试使用ProgressEvent.PROGRESS事件来持续监视下载的XML,然后按照Re0sless的建议,检查bytesLoaded与bytesTotal,并在两个数字相等时开始XML解析,而不是使用Event.COMPLETE事件。
如果您可以访问文件,您可以访问其字节信息,您应该能够获得bytesLoaded和bytesTotal数字,无论域如何。
对我而言,有关的事情是,它可能在完成加载之前触发Event.COMPLETE,这让我怀疑加载是否超时。
问题发生的频率如何?你能否在一瞬间取得成功,然后用相同的饲料失败呢?
出于测试目的,请尝试在Event.COMPLETE
处理程序方法的顶部跟踪URLLoader.bytesLoaded
和URLLoader.bytesTotal
。如果它们不匹配,就会知道该事件正在提前发射。如果是这种情况,您可以监听URLLoader的进度事件。检查bytesLoaded
与您的处理程序中的bytesTotal
,并且只在加载真正完成时才解析XML。诚然,这很可能类似于URLLoader在触发Event.COMPLETE
之前正在做的事情,但如果这种情况被破坏,您可以尝试滚动自己的。
请让我们知道您发现了什么。如果可以的话,请粘贴一些源代码。我们可能会发现一些注意事项。
如果你可以发布更多的代码,我们可能会发现问题。
另一件事测试(除了追踪bytesTotal
)是跟踪装载机的data
财产在Event.COMPLETE
处理,只是为了看看XML数据实际上是正确加载,例如检查是有</link>
那里。
@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
我没有问题 - 说,这个错误很难重现,所以我知道我没有修复任何东西,而是添加无用的代码。
只是一个侧面说明,这种说法没有任何效果:
XML.ignoreWhitespace;
因为ignoreWhitespace
是一个属性。你必须将其设置为true
这样的:
XML.ingoreWhitespace = true;
的Event.COMPLETE
处理真的不应该被称为除非装载机满载,它是没有意义的。您是否确认它实际上并未完全加载(通过查看您跟踪的bytesLoaded
与bytesTotal
值)?如果Event.COMPLETE
事件在bytesLoaded == bytesTotal
之前发送,那是一个错误。
好,你已经得到它与定时器,但它是非常奇怪的,你需要它。
我建议你在https://bugs.adobe.com/flashplayer/上提交一个错误报告,因为在加载所有字节之前事件确实不应该触发。在此期间,我想你必须忍受这个计时器。您也许可以通过监听进度事件来做同样的事情,这可能会让您不必亲自处理计时器。
您可以在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确实没有完成,我们强制它再次加载。
有时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头和/或添加一个缓存克星他的网址?
只是一些建议...
边注:的bytesLoaded是一样的BYTESTOTAL它开始加载,以及(他们均为0)之前,所以我会检查的条件的onComplete其中: 的URLLoader。 bytesLoaded == URLLoader.bytesTotal && bytesTotal> 0 – 2008-09-25 10:47:37