Лучшие практики для разбора AS3 XML - PullRequest
1 голос
/ 20 августа 2008

У меня возникли проблемы с анализом различных типов 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 фактически анализируется объект)?


@ Тео: Спасибо за подсказку ignoreWhitespace. Также мы определили, что событие вызывается до его готовности (мы провели несколько тестов, отслеживающих mainXMLLoader.bytesLoaded + "/" + mainXMLLoader.bytesLoaded

Ответы [ 10 ]

1 голос
/ 22 августа 2008

Примечание: это утверждение не действует:

XML.ignoreWhitespace;

потому что ignoreWhitespace является собственностью. Вы должны установить его на true следующим образом:

XML.ingoreWhitespace = true;
1 голос
/ 20 августа 2008

Что меня беспокоит, так это то, что он может запускать Event.COMPLETE до того, как закончится загрузка, и это заставляет меня задуматься, истекает ли время загрузки.

Как часто возникает проблема? Можете ли вы добиться успеха в один момент, а затем потерпеть неудачу в следующий раз с тем же фидом?

В целях тестирования попробуйте отследить URLLoader.bytesLoaded и URLLoader.bytesTotal в верхней части метода обработки Event.COMPLETE. Если они не совпадают, вы знаете, что событие срабатывает преждевременно. Если это так, вы можете прослушивать событие прогресса URLLoader. Сравните bytesLoaded с bytesTotal в вашем обработчике и анализируйте XML только после завершения загрузки. Конечно, это очень похоже на то, что делает URLLoader до того, как он запустит Event.COMPLETE, но если он сломан, вы можете попробовать запустить свой собственный.

Пожалуйста, дайте нам знать, что вы узнали. И если вы могли бы, пожалуйста, вставьте в некоторый исходный код. Мы могли бы заметить что-то заметное.

1 голос
/ 20 августа 2008

Вы пробовали проверить, совпадают ли загруженные байты с общим количеством байтов?

URLLoader.bytesLoaded == URLLoader.bytesTotal

Это должно сказать вам, если файл закончил загрузку, это не поможет с завершением события, которое запускается на ранней стадии, но оно должно сказать вам, если проблема с xml была прочитана.

Я не уверен, будет ли он работать над доменами, поскольку мой xml всегда находится на одном сайте.

0 голосов
/ 17 августа 2009

иногда страница сервера 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);

Вы также пытались установить для dataFormat urlloader значение URLLoaderDataFormat.TEXT, а также добавить заголовки URL-адреса prama-no-cache и / или добавить кеш-буфер для URL-адреса?

Только некоторые предложения ...

0 голосов
/ 25 сентября 2008

Вы можете установить уникальное пространство имен элемента в самом конце вашего 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 действительно не был завершен, и мы заставляем его снова загружаться.

0 голосов
/ 24 августа 2008

Я предлагаю вам сообщить об ошибке на https://bugs.adobe.com/flashplayer/,, потому что событие действительно не должно запускаться до загрузки всех байтов В то же время, я думаю, вы должны жить с таймером. Возможно, вы сможете сделать то же самое, прослушивая событие прогресса, что, возможно, избавит вас от необходимости самостоятельно управлять таймером.

0 голосов
/ 22 августа 2008

Обработчик Event.COMPLETE действительно не должен вызываться, если загрузчик не был полностью загружен, это не имеет смысла. Подтвердили ли вы, что он на самом деле не полностью загружен (глядя на отслеживаемые значения bytesLoaded против bytesTotal)? Если событие Event.COMPLETE отправлено до bytesLoaded == bytesTotal, это ошибка.

Хорошо, что вы работаете с таймером, но очень странно, что вам это нужно.

0 голосов
/ 21 августа 2008

@ Брайан Уоршоу: эта проблема возникает только в 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 у меня не было проблемы - при этом этот дефект трудно воспроизвести, поэтому, насколько я знаю, я ничего не исправил, а вместо этого просто добавил бесполезный код.

0 голосов
/ 21 августа 2008

Если бы вы могли опубликовать еще немного кода, мы могли бы найти проблему.

Еще одна вещь, которую нужно проверить (помимо трассировки bytesTotal), - это проследить свойство data загрузчика в обработчике Event.COMPLETE, просто чтобы убедиться, что данные XML действительно были загружены правильно, например, проверить, есть ли </link> там.

0 голосов
/ 20 августа 2008

Как вы упомянули в своем вопросе, проблема, скорее всего, заключается в том, что ваша программа просматривает XML до того, как он действительно был полностью загружен, я не знаю, что существует надежный способ "проанализировать" XML, потому что синтаксический анализ часть вашего кода, скорее всего, в порядке, просто вопрос в том, действительно ли он загружен.

Вы можете попытаться использовать событие ProgressEvent.PROGRESS для постоянного мониторинга XML во время его загрузки, а затем, как рекомендует Re0sless, проверить bytesLoaded против bytesTotal и начать анализ XML, когда два числа равны вместо использования события .COMPLETE событие.

Вы должны иметь возможность получать номера bytesLoaded и bytesTotal очень хорошо независимо от доменов, если вы можете получить доступ к файлу, вы можете получить доступ к его байтовой информации.

...