提问人:Jeff Winkworth 提问时间:8/20/2008 最后编辑:Adam LearJeff Winkworth 更新时间:3/9/2016 访问量:18190
AS3 XML 分析的最佳实践
Best Practices for AS3 XML Parsing
问:
我在 flash 中解析各种类型的 XML(特别是 FeedBurner RSS 文件和 YouTube 数据 API 响应)时遇到了一些麻烦。我正在使用 a 加载 XML 文件,并在创建新的 XML 对象时。75% 的时间这工作正常,我时不时地会遇到这种类型的异常:URLLoader
Event.COMPLETE
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实际解析到对象中之前就开始了计算对象数量)?XML
@Theo:感谢您的 ignoreWhitespace 提示。此外,我们已确定该事件在准备就绪之前被调用(我们做了一些测试跟踪mainXMLLoader.bytesLoaded + "/" + mainXMLLoader.bytesLoaded
答:
您是否尝试过检查加载的字节数是否与总字节数相同?
URLLoader.bytesLoaded == URLLoader.bytesTotal
这应该告诉您文件是否已完成加载,它无助于提前触发 compleate 事件,但它应该告诉您是否是读取的 xml 有问题。
我不确定它是否适用于域,因为我的 xml 始终位于同一站点上。
评论
正如您在问题中提到的,问题很可能是您的程序在实际完全下载之前正在查看 XML,我不知道是否有一种万无一失的方法来“解析”XML,因为代码的解析部分很可能很好,这只是它是否实际下载的问题。
您可以尝试使用 ProgressEvent.PROGRESS 事件在下载 XML 时持续监视它,然后按照 Re0sless 的建议,检查 bytesLoaded 与 bytesTotal,并在两个数字相等时开始 XML 分析,而不是使用 Event.COMPLETE 事件。
无论域如何,您都应该能够获得 bytesLoaded 和 bytesTotal 数字,如果您可以访问该文件,则可以访问其字节信息。
令我担心的是,它可能会在完成加载之前触发 Event.COMPLETE,这让我想知道加载是否超时。
问题多久发生一次?你能不能在前一刻成功,下一刻就失败了?
出于测试目的,请尝试跟踪处理程序方法顶部的 和 。如果它们不匹配,则表示您知道事件过早触发。如果是这种情况,您可以侦听 URLLoader 的进度事件。在处理程序中检查 ,只有在加载真正完成后才解析 XML。当然,这很可能类似于 URLLoader 在触发之前正在做的事情,但如果它被破坏,您可以尝试滚动自己的。URLLoader.bytesLoaded
URLLoader.bytesTotal
Event.COMPLETE
bytesLoaded
bytesTotal
Event.COMPLETE
请告诉我们您的发现。如果可以的话,请粘贴一些源代码。我们也许能够发现一些值得注意的东西。
如果您可以发布更多代码,我们也许能够找到问题所在。
要测试的另一件事(除了跟踪)是在处理程序中跟踪加载程序的属性,只是为了查看XML数据是否实际正确加载,例如检查是否存在。bytesTotal
data
Event.COMPLETE
</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;
除非加载程序已满载,否则确实不应该调用处理程序,这毫无意义。您是否确认它实际上没有完全加载(通过查看 vs. 您跟踪的值)?如果在此之前调度事件,则是一个错误。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].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);
}
}
我希望这对您有所帮助,但真正的希望是让足够多的人知道这个问题。不能依靠事件是一件可悲的事情。不过,我必须说,从我所听说的 XML 来看,它在大规模上并不是很理想,并且相信这是当您需要像 AMFPHP 这样的东西来序列化数据时。
希望这有帮助!请记住,这里的想法是,我们知道 XML 中的最后一个子元素是什么,因为我们设置了它!我们没有理由不能访问最后一个子元素/元素,但如果我们不能,我们必须假设 XML 确实不完整,然后我们强制它再次加载。
有时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 标头和/或向 url 添加缓存破坏程序?
只是一些建议...
上一个:cURL 为发布内容添加空格?
评论