У меня возникли проблемы с синтаксическим анализом различных типов XML во флэш-памяти (в частности, файлов RSS FeedBurner и ответов API данных YouTube). Я использую 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. Единственное решение, которое мы придумали, - это включить таймер для события и, по сути, «подождать несколько секунд», прежде чем начать анализ данных. Конечно, это не лучший способ сделать это.
Есть ли надежный способ разобрать XML во Flash?
Обновление 2 сентября 2008 г. Мы пришли к следующему выводу, в этот момент в коде запускается исключение:
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
Это должно сказать вам, закончила ли загрузка файл, это не поможет с ранним запуском события завершения, но должно сказать вам, была ли прочитана проблема с xml.
Я не уверен, будет ли он работать с доменами, поскольку мой xml всегда находится на одном сайте.
Как вы упомянули в своем вопросе, проблема, скорее всего, заключается в том, что ваша программа просматривает XML до того, как он был фактически полностью загружен, я не знаю, есть ли надежный способ «проанализировать» XML, потому что часть синтаксического анализа вашего код, скорее всего, в порядке, это просто вопрос того, действительно ли он загружен.
Вы можете попробовать использовать событие ProgressEvent.PROGRESS для постоянного мониторинга XML по мере его загрузки, а затем, как предлагает Re0sless, проверить bytesLoaded против bytesTotal и начать синтаксический анализ XML, когда два числа равны, вместо использования события Event.COMPLETE .
Вы должны иметь возможность получать числа bytesLoaded и bytesTotal нормально независимо от доменов, если вы можете получить доступ к файлу, вы можете получить доступ к его байтовой информации.
Меня беспокоит то, что он может запускать Event.COMPLETE до того, как завершит загрузку, и это заставляет меня задаться вопросом, истекает ли время загрузки.
Как часто возникает проблема? Можете ли вы добиться успеха в один момент, а затем потерпеть неудачу в следующий с той же подачей?
В целях тестирования попробуйте отследить URLLoader.bytesLoaded и URLLoader.bytesTotal в верхней части метода обработчика Event.COMPLETE. Если они не совпадают, вы знаете, что событие запускается преждевременно. В этом случае вы можете прослушивать событие progress 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/, потому что событие действительно не должно срабатывать до того, как будут загружены все байты. А пока, думаю, тебе придется жить с таймером. Вы могли бы сделать то же самое, вместо этого прослушивая событие progress, что, возможно, могло бы избавить вас от необходимости самостоятельно обрабатывать таймер.
Вы можете установить уникальное пространство имен элемента в самом конце вашего 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 действительно не был полным, и мы принудительно загружаем его снова.
иногда страница сервера RSS может не выдавать правильные и действительные данные XML, особенно если вы постоянно обращаетесь к ней, поэтому это может не быть вашей ошибкой. Вы пытались открыть страницу в веб-браузере (желательно с помощью плагина 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, а также добавить заголовки URL-адресов prama-no-cache и / или добавить блокировку кеша к URL-адресу?
Просто несколько предложений ...
примечание: bytesLoaded совпадает с bytesTotal до начала загрузки (они оба равны 0), поэтому я бы проверил условие onComplete, где: URLLoader.bytesLoaded == URLLoader.bytesTotal && bytesTotal> 0