Загрузка XML-файла

Автор: Aport Вторник, Январь 27th, 2015 Нет комментариев

Рубрика: Разное

Гораздо легче работать с данными в формате XML — они гораздо более удобочитаемые, а еще с их помощью можно хранить древообразные структуры в их естественном виде. Недостатком XML в сравнении с масивами в JavaScript, это его размер. При представлении большой структуры данных его «вес» иногда превосходит размеры простого масива в разы, сильно увеличивая время загрузки страницы.

Однако, если вам необходимо поместить большое количество хорошо структурированной информации не испытывая терпение посетителей, то лучшее решение всегда было разбиение информации на части и ее загрузка по мере необходимости. Но если для старых браузеров нужно просто сделать n-количество страниц, то с MSIE5 и Mozilla можно себе позволить загружать XML-файлы и использовать их не перегружая страницы.

Стандарты?

К сожалению стандартные способы загрузки внешных файлов появятся только в DOM level 3 (на момент написания статьи — WD).

Mozilla, которая практически во всем пытается действовать согласно стандартам, несмотря на это уже реализовала метод загрузки внешнего документа — уже известно (из WD), что это будет метод load(uri):

var externalDoc = document.implementation.createDocument("", "", null);
externalDoc.load('file.xml');

Из примера видно, что для загрузки нового документа в начале нужно создать новый документ с помощью метода createDocument(), а после этого воспользоваться методом load(), который будет у пустого документа.

Ребята, делающие Microsoft Internet Explorer однако не спешат реализовывать стандарты, которые еще не достигли зрелого возраста. Они предлагают пользоваться их фирменным методам — использовать один из компонентов ActiveX чья задача осуществлять HTTP-запросы — XMLHTTPRequest, чтобы создать объект XMLHTTP:

var externalDoc = new ActiveXObject("Msxml2.XMLHTTP");
externalDoc.open('GET', 'file.xml');
externalDoc.send();

Как видите, разница есть. И не только насчет того, как создать объект документа и не только каким образом запросить его, но самое главное — как узнать загружен ли документ, чтобы начать работать с ним.

В связи с этим с появлением версии 0.9.7 в Mozilla существует объект XMLHttpRequest, который (почти) точно повторяет поведение своего брата, растущего в Microsoft. Основные различия, которые касаются простых смертных — в создании самого объекта (нет поддержки ActiveX), в нехватки некоторых свойств, например — ошибки и в том, что параметр для метода send() обязателен (поэтому передаем null), все остальные методы, свойства и события в точности повторяют MS-вариант. Из кода все видно:

if (mozilla || ie5) {
   if (mozilla) var externalDoc = new XMLHttpRequest();
   if (ie5) var externalDoc = new ActiveXObject("Msxml2.XMLHTTP");
   externalDoc.open('GET', 'file.xml');
   externalDoc.send(null);
}

Разница в одну строку — нам больше и не надо.

Собственно запрос

Чтобы сделать запрос с помощью объекта XMLHTTP нужно вызвать два его метода — open() и send().

Первый из них инициализирует заголовки запроса — у него пять параметра, но мы рассмотрим только первых трех — последние два — это имя и пароль для авторизации:

XMLHTTP.open(method, url[, async]);

method — это именно метод запроса, он может иметь значения GET, POST, PUT и т. п. Так как в этой статье не будем рассматривать подробно формирование других полей запроса, то используем исключительно GET.
url — это соответственно URL файла, который нам нужен.
async — булевое значение, указывающее запрос будет выполняться синхронно или асинхронно, по умолчанию равен true, если не нужно устанавливатьfalse, то можно пропустить. Подробнее об этом чуть дальше.

После инициализации заголовков нужно сделать и сам запрос. Это делается с помощью метода send(). У него есть один параметр body — это тело документа, которого хотим отправить на сервер, если хотим. Так как для простого запроса документа не нужно отправлять ничего, то пишем просто null.

Синхронность и результаты

Вернемся к третьему параметру метода open(). Он напрямую связан с тем, каким образом мы будем обрабатывать результаты. Синхронная загрузка документа можно объяснить простыми словами так: после отправления запроса, выполнение скрипта останавливается и функция ждет пока документ не загружен полностью или если не выдана ошибка. С помощью синхронной загрузки можно сразу после запроса приступать к извлечению данных. Это очень удобно, если вам не нужно пока загружается документ делать что-либо еще. Для маленьких подгружаемых документов — это идеальный вариант:

...
externalDoc.open('GET', 'file.xml', false);
externalDoc.send(null);
externalDoc.responseXML.getElementsBytagName('property');
...

В случае если документ который подгружаете большой или его размер неизвестен (не зависит от вас), то лучше воспользоваться асинхронной обработки документа. Т. е. после выполнения send() следующая комманда выполняется без ожидания даже ответа сервера, тем более полной загрузки документа. В этом случае можно узнать загружен ли документ выясняя значение переменной readyState. Оно имеет пять возможные значения:

0 — запрос не инициализирован (не вызван метод open())
1 — запрос не осуществлен (не вызван метод send())
2 — ожидание ответа — запрос сделан, но все еще данные не поступают
3 — данные поступают, но все еще ответ не завершен
4 — ответ поступил полностью

Значения переменной readyState нужно отслеживать с помощью события onreadystatechange. Нужно сделать функцию, которая будет запускаться при наступлении этого события и проверяет значение, если оно равно 4, то именно эта функция будет запускать обработку данных:

...
externalDoc.onreadystatechange = checkReadyState;
externalDoc.send();
...
function checkReadyState() {
   if (externalDoc.readyState == 4) {
      alert('Документ загружен!');
   }
}

Вместо alert’а, нужно вызвать функцию, которая будет парсить документов или обрабатывать документ тут-же. К сожалению я так и не нашел ни в одной документации полного описания события onreadystatechange, поэтому не знаю можно ли каким нибудь образом добраться до объекта вызвавшего его, чтобы не использовать его имя напрямую (externalDoc). Однако есть идея — воспользоваться необъявленной функцией, которая будет запускатьcheckReadyState() с параметром — тот самый объект, который нам нужен:

...
externalDoc.onreadystatechange = function() { checkReadyState(externalDoc) };
externalDoc.send(null);
...
function checkReadyState(extDoc) {
   if (extDoc.readyState == 4) {
      alert('Документ загружен!');
   }
}

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

Ошибки

Для того чтобы выяснить пришел ли документ и можно ли с ним работать, нужно в начале проверить не произошла ли ошибка во время пересылки. Самое простое, что можно проверить, это свойство satus. Его значение — статус HTTP-транзакции. Если он равен 200, то все в порядке. Если нет, то нужно делать вывод на основе его значения, например 404 — файл не найден и т. п. Свойство статус можно проверить таким образом:

if (externalDoc.status == 200) {
   alert('Файл пришел в целости и сохранности!');
}

Нужно однако помнить, что не каждый документ можно третировать как XML. XML должен быть, как говорят, well-formed. Т. е. должен быть заголовок, все тэги должны быть закрыты, все ентити должны быть определены и т. д. Даже если транзакция прошла успешно, но XML не well-formed, то пользоваться его данными нельзя, потому что документ не воспринимается XML-парсером. Вот тут браузеры снова начинают себя вести по-разному.

В MS-реализации XMLHTTP у объекта есть под-объект parseError со свойствами errorCode, reason и др. в которых указывается есть ли ошибка, какая она и где находится в коде. Проверку на ошибки документа для MSIE будет выглядит так:

if (externalDoc.status == 200 && externalDoc.parseError.errorCode == 0) {
   alert('Файл пришел в целости и сохранности!');
}

Т. е. когда нет ошибок errorCode равен нулю. В случае ошибки он принимает меньшее (отрицательное) значение.

К сожалению в Mozilla нет под-объекта parseError. Она в отличие от MSIE в случае ошибки XML-парсера в качестве ответа в responseXML выдает XML описывающий ошибки. Начальный тэг (элемент документа) у него — <parsererror>. Поэтому проверку можно делать на основе его имени:

if (
   externalDoc.status == 200
   && externalDoc.responseXML.documentElement.nodeName != 'parsererror'
) {
   alert('Файл пришел в целости и сохранности!');
}

Для тех, кто увидел незнакомую конструкцию напомню, что начальный тэг документа или элемент документа можно узнать из свойстваdocumentElement, которое есть у всех документов. А в свойстве объекта XMLHTTP.responseXML записывается сам XML-документ, который мы запросили.

Да, с этими отличиями получается нехорошо. Нехорошо, потому что для того, чтобы проверить небыло ли ошибок нужно проверять что это за браузер. К счастью оказывается, что в отличие от Mozilla, в случае ошибки MSIE оставляет свойство responseXML пустым. Т. е. у него нет дажеdocumentElement. Поэтому можно написать одинаковую проверку на ошибки для обоих браузеров:

if (
   externalDoc.status == 200
   && externalDoc.responseXML.documentElement
   && externalDoc.responseXML.documentElement.nodeName != 'parsererror'
) {
   alert('Файл пришел в целости и сохранности!');
}

Вот это другое дело. У нас теперь есть примерно одинаковые способы сделать новый объект типа XMLHTTP, запросить документ, узнать когда он загрузиться, проверить его на наличие ошибок и осталось только его обработать.

Обработка

Информация находится не в самом HTML-документе, а в XMLHTTP-объекте, точнее в его поле responseXML. Поэтому нужно запускать методgetElementsByTagName() не для document, а для externalDoc.responseXML или начинать распарсивание с его documentElement.

А вот и полный пример кода, который рассматривали на этот раз:

var externalDoc = null;

function checkReadyState() {
   if (externalDoc.readyState == 4) {
      if (
         externalDoc.status == 200
         && externalDoc.responseXML.documentElement
         && externalDoc.responseXML.documentElement.nodeName != 'parsererror'
      ) {
         alert('Файл пришел в целости и сохранности!');
      }
   }
}

function loadAsyncXML(fileName) {
   try {
      externalDoc = new XMLHttpRequest();
   } catch (error) {
      try {
         externalDoc = new ActiveXObject("Msxml2.XMLHTTP");
      } catch (error) {
         return;
      }
   }
   externalDoc.onreadystatechange = checkReadyState;
   externalDoc.open('GET', fileName);
   externalDoc.send(null);
}

Вот такой маленький скрипт. Вам нужно только вызвать функцию loadAsyncXML() и поменять alert() на обработку данных. Изменения по отношению к приведенных ранее кусках только в том, что при создании объекта я воспользовался способом перехватки ошибок try — catch, чтобы не делать проверку что за браузер пришел. Еще, это пример асинхронной загрузки. Но я уверен, что вы уже знаете как сделать функцию для синхронного запроса.

 

Источник: yapro.ru

Оставить комментарий

Чтобы оставлять комментарии Вы должны быть авторизованы.

Похожие посты