我在解析 Flash 中的各种类型的 XML(特别是 FeedBurner RSS 文件和 YouTube 数据 API 响应)时遇到了一些问题。我正在使用 URLLoader 加载 XML 文件,并在 Event.COMPLETE 创建一个新的 XML 对象。 75% 的时间都可以正常工作,而且我时不时会遇到这种类型的异常:
1
| TypeError: Error #1085: The element type"link" must be terminated by the matching end-tag"/link". |
我们认为问题在于 XML 很大,并且可能在实际从 URLLoader 下载 XML 之前触发了 Event.COMPLETE 事件。我们想出的唯一解决方案是在事件上设置一个计时器,并在开始解析数据之前基本上"等待几秒钟"。这肯定不是最好的方法。
有什么方法可以在 Flash 中解析 XML 吗?
2008 年 9 月 2 日更新 我们得出以下结论,此时代码中的 excption 被触发:
1 2 3 4 5 6
| 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
只是一个旁注,这个声明没有效果:
因为 ignoreWhitespace 是一个属性。您必须像这样将其设置为 true:
1
| XML.ingoreWhitespace = true; |
我担心的是它可能在完成加载之前触发 Event.COMPLETE,这让我想知道加载是否超时。
问题多久发生一次?您能否在同一时间获得成功,然后在下一刻以相同的提要失败?
出于测试目的,请尝试跟踪 Event.COMPLETE 处理程序方法顶部的 URLLoader.bytesLoaded 和 URLLoader.bytesTotal。如果它们不匹配,您就知道该事件过早触发。如果是这种情况,您可以监听 URLLoader 的进度事件。检查处理程序中的 bytesLoaded 与 bytesTotal 并仅在加载真正完成后才解析 XML。当然,这很可能类似于 URLLoader 在触发 Event.COMPLETE 之前所做的事情,但如果它被破坏了,您可以尝试自己滚动。
请告诉我们您的发现。如果可以,请粘贴一些源代码。我们也许能够发现一些值得注意的东西。
您是否尝试过检查加载的字节数是否与总字节数相同?
1
| URLLoader.bytesLoaded == URLLoader.bytesTotal |
这应该告诉你文件是否已经完成加载,它不会帮助完成事件提前触发,但它应该告诉你它是否是读取 xml 的问题。
我不确定它是否适用于域,因为我的 xml 总是在同一个站点上。
有时 RSS 服务器页面可能无法输出正确且有效的 XML 数据,尤其是在您不断点击它的情况下,因此这可能不是您的错。您是否尝试过在 Web 浏览器中点击页面(最好使用 xml 验证器插件)来检查服务器响应是否始终有效?
我在这里能看到的唯一其他东西是这行:
1 2 3 4
| 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 中添加缓存破坏器?
只是一些建议...
您可以在 XML 文档的最后设置一个唯一的元素名称空间,其中一个属性"value"等于"true";
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| //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 确实不完整,我们会强制它再次加载。
我建议您在 https://bugs.adobe.com/flashplayer/ 提交错误报告,因为在加载所有字节之前确实不应该触发该事件。与此同时,我想你必须忍受计时器。您也许可以通过监听进度事件来做同样的事情,这也许可以让您不必自己处理计时器。
除非加载器完全加载,否则真的不应该调用 Event.COMPLETE 处理程序,这是没有意义的。您是否确认它实际上没有完全加载(通过查看您跟踪的 bytesLoaded 与 bytesTotal 值)?如果 Event.COMPLETE 事件在 bytesLoaded == bytesTotal 之前调度,这是一个错误。
很好,你已经让它与计时器一起工作,但你需要它很奇怪。
@Brian Warshaw:这个问题只有大约 10-20% 的时间发生。有时它会打嗝,只需重新加载应用程序就可以正常工作,有时我会花半个小时一遍又一遍地重新加载应用程序无济于事。
这是原始代码(当我问这个问题时):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 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 的原始回复编写的代码(类似于提到的一些建议):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| 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 是否没有问题的代码 - 也就是说,这个错误很难重现,所以据我所知,我没有修复任何东西,而是添加了无用的代码。
如果您可以发布更多代码,我们或许能够找到问题所在。
要测试的另一件事(除了跟踪 bytesTotal)是在 Event.COMPLETE 处理程序中跟踪加载程序的 data 属性,只是为了查看 XML 数据是否实际加载正确,例如检查是否存在/link 那里。
正如您在问题中提到的那样,问题很可能是您的程序在实际完全下载之前正在查看 XML,我不知道是否有"解析"XML 的可靠方法,因为解析您的部分代码很可能没问题,这只是它是否实际下载的问题。
您可以尝试使用 ProgressEvent.PROGRESS 事件在下载 XML 时持续监控它,然后按照 Re0sless 的建议,检查 bytesLoaded 与 bytesTotal 并在两个数字相等时开始 XML 解析,而不是使用 Event .COMPLETE 事件。
无论域如何,您都应该能够很好地获得 bytesLoaded 和 bytesTotal 数字,如果您可以访问该文件,则可以访问其字节信息。