Лучшие практики для синтаксического анализа AS3 XML

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

20.08.2008 13:15:57
10 ОТВЕТОВ

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

URLLoader.bytesLoaded == URLLoader.bytesTotal

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

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

1
20.08.2008 13:36:43
примечание: bytesLoaded - это то же самое, что и bytesTotal, до того, как он также начнет загружаться (они оба
matt lohkamp 25.09.2008 10:47:37

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

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

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

0
20.08.2008 16:36:44

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

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

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

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

1
20.08.2008 16:54:12

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

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

0
21.08.2008 14:13:37

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

0
21.08.2008 21:06:20

Просто примечание, это утверждение не имеет никакого эффекта:

XML.ignoreWhitespace;

потому что ignoreWhitespaceэто собственность. Вы должны установить это trueтак:

XML.ingoreWhitespace = true;
1
22.08.2008 07:27:47

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

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

0
22.08.2008 07:32:57

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

0
24.08.2008 14:00:38

Вы можете установить уникальное пространство имен элемента в самом конце вашего XML-документа, который имеет один атрибут «значение», равный «истине»;

//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
6.03.2016 22:31:37

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

Просто несколько предложений ...

0
17.08.2009 10:30:55