XMLMirror и XMLAccessor

Недавно один хороший человек предложил сделать сайт на Flash и именно на ActionScript 2. Проект мне понравился тем, что уже практически всё было готово к моей работе, т.е. предоставили FLA файл с дизайном и PDF с описанием как должно всё выглядеть после завершения задания. И, как всегда, оказалось, что работу нужно делать на вчера.
Сайт разбит на разделы отдельными файлами, каждый из них загружается после нажатия кнопки меню. Сайт должен получать все данные с помощью XML сгенерированным со всеми динамическими данными. Вот тут я начал вспоминать DOM XML модель ActionScript 2, которой мне предстоит пользоваться – постоянные поиски нужных узлов, использование ID'шников и XMLNode.firstChild/lastChild/childNodes. В общем, эйфория с E4X закончилась. Тогда я решил написать более удобные интерфейсы для доступа к XML данным. Это не E4X, но намного удобнее (как мне показалось) средств класса XMLNode.
Пример XML документа, который будет использован далее:

<?xml version="1.0" encoding="utf-8"?>
<root test="true">
	<child length="5">
		<node value="one"/>
		<node value="two"/>
		<node value="three"/>
		<node value="four"/>
		<node value="five"/>
		<![CDATA[child 1 data]]>
	</child>
	<child attr1="value 1"><![CDATA[child 2 data]]></child>
	<child attr2="value 2"><![CDATA[child 3 data]]></child>
	<another1/>
	<another2/>
</root>

XMLMirror

Класс получает доступ к XMLNode с данными и строит на его основе структуру из объектов XMLMirror. В итоге получается структура объектов идентичная структуре XML документа. Этот класс специально создавался для данного сайта и предназначался для обработки небольших XML документов. После обработки XML документа, его можно удалить – все данные сохранены в структуре объектов XMLMirror.
Использовать его достаточно просто, сами смотрите пример:

  1. import aw.template.xml.XMLMirror;
  2. var mrr:XMLMirror = new XMLMirror(XML);
  3. trace(mrr.test); // true
  4. trace(mrr.children.child.getValue()); // child 1 data
  5. trace(mrr.children.child.children.node.value); // one

По примеру не трудно догадаться, что атрибуты узлов становятся свойствами объектов, а дочерние узлы становятся свойствами объекта children. На самом деле всё обстоит немного сложнее. В обоих случаях – XMLMirror и XMLAccessor, я использовал "волшебный" метод Object.__resolve.
В XMLMirror, атрибуты сохраняются в объекте XMLMirror.attributes, а метод XMLMirror.__resolve перенаправляет запрос на объект attributes.
Дочерние узлы сортируются по именам в объектеXMLMirror.children. Для одноимённых узлов создаются объекты MirrorList, в них и сохраняются узлы с одинаковыми именами. MirrorList сам не является массивом, но хранит внутри себя массив элементов и использует метод MirrorList.__resolve. Он возвращает элемент по номеру, а если номер не задан, то перенаправляет запрос на первый элемент массива, с индексом "0".
Пример:

  1. import aw.template.xml.XMLMirror;
  2. var mrr:XMLMirror = new XMLMirror(XML);
  3. // разные способы доступа к атрибуту дают один результат
  4. trace(mrr.test); // true
  5. trace(mrr.attributes.test); // true
  6. // доступ к атрибуту length первого узла child
  7. trace(mrr.children.child.length); // 5
  8. // доступ к массиву узлов child
  9. trace(mrr.children.child.getList()); // child 1 data,child 2 data,child 3 data
  10. // доступ к массиву длине массива узлов child
  11. trace(mrr.children.child.getLength()); // 3
  12. // разные способы доступа к узлу дают один результат
  13. trace(mrr.children.child.children.node.value); // one
  14. trace(mrr.children.child.children.node[0].value); // one
  15. // получение количества всех одноимённых вложеных узлов
  16. trace(mrr.getDescendants('node').getLength()); // 5

Свойства объекта XMLMirror:

  • attributes:Object – объект содержащий атрибуты узла
  • children:Object – объект содержащий списки дочерних узлов

Методы объекта XMLMirror:

  • getChildList():MirrorList – возвращает массив дочерних узлов
  • getChildNames():Array – возвращает массив имён дочерних узлов
  • getDescendants(name:String):MirrorList – возвращает все одноимённые вхождения
  • getName():String – имя XML узла
  • getParent():XMLMirror – указывает на родительский объект XMLMirror
  • getValue():String – содержит строковое значение из XML узла
  • toString():String – возвращает строковое значение хранящееся из XML узла
  • create(xml:XMLNode, save:Boolean):XMLMirror – статический метод создающий XMLMirror

Конструктор XMLMirror принимает два параметра:

  • XMLNode – Узел, который следует преобразовать.
  • Boolean – указывает на необходимость сохранение порядка элементов. Если установить в true, то метод getChildList будет возвращать список дочерних узлов сохраняя их последовательность, предопределённую в XML документе. Иначе, узлы будут каталогизированы по именам в любом порядке. По умолчанию, имеет значение "false".

Методы объекта MirrorList:

  • clone():MirrorList – клонирует список узлов
  • getLength():Number – возвращает количество элементов
  • getList():Array – возвращает массив элементов списка
  • pop():Object – вызывает одноимённый метод в массиве содержащем элементы списка
  • push(o:Object, …):Number – вызывает одноимённый метод в массиве содержащем элементы списка
  • shift():Object – вызывает одноимённый метод в массиве содержащем элементы списка
  • unshift(o:Object, …):Number – вызывает одноимённый метод в массиве содержащем элементы списка

Загрузить все классы XMLMirror.

XMLAccessor

Предыдущий метод необходим, если единожды загруженные XML данные будут использоваться на протяжении всей работы программы – идеальный вариант для небольших сайтов или случаев передачи, с помощью XML, статических данных. Но я решил развить тему и вчера написал ещё один "облегчённый" способ доступа к элементам XML документа не требующий дополнительных преобразований. Объект класса XMLAccessor работают с XML документом "наживую".
В данном случае я решил избавиться от дополнительных объектов-прокладок, как в предыдущем методе. Этого избежал данный способ, после ввода символа "$" предшествующего имени атрибута (как и в E4X, только вместо "@" используется "$").
Пример:

  1. import aw.template.xml.XMLAccessor;
  2. var acc :XMLAccessor = new XMLAccessor(XML);
  3. // доступ к атрибуту
  4. trace(acc.$test); // true
  5. trace(acc.child.node.$value); // one
  6. trace(acc.child.node[1].$value); // two
  7. // доступ к дочернему узлу, без индекса узла - возвращается XMLAccessorList
  8. trace(acc.another1); // <another1 />
  9. // доступ к атрибуту узла без указания его ноиера
  10. trace(acc.child.$length); // 5
  11. // количество дочерних узлов "node"
  12. trace(acc.child.node.getLength()); // 5
  13. // количество всех узлов. здесь используется XMLAccessor только для получения узла child,
  14. // а дальше обращение происходит к свойству childNodes объекта XMLNode.
  15. trace(acc.child.childNodes.length); // 6
  16. // аналогично предыдущему - XMLAccessor использовался только для получения втрого узла child.
  17. trace(acc.child[1].firstChild.nodeValue); // child 2 data

При попытке получения узла без указания индекса узла, вы всегда будете получать объект типа XMLAccessorList.

Для всех дочерних одноимённых узлов создаются списки, использующие метод "__resolve". Такие списки создаются во время выполнения запроса, по имени узла. Если номер узла не задан, то будет использован первый элемент из списка. Списком является объект типа XMLAccessorList, содержащий в себе массив.
Каждый раз, используя данный стиль доступа, нужно создавать объект XMLAccessor, привязывая его к XML узлу (достаточно передать в конструктор). И именно этот, созданный объект, нужно использовать как корневой элемент для цепочек доступа. Так же, следует учитывать, что нельзя прерывать цепочку доступа переходя на нативные способы DOM XML и после этого пытаться опять воспользоватся свойствами XMLAccessor, т.е. он действует, только если вести непрерывную цепочку доступа от объекта XMLAccessor к цели.
Пример:

  1. import aw.template.xml.XMLAccessor;
  2. var acc :XMLAccessor = new XMLAccessor(XML);
  3. // правильно
  4. trace(acc.child); // получаем XMLAccessorList c одним узлом: <child length="5">...
  5. trace(acc.child.node); // получаем XMLAccessorList: <node value="one" />,<node...
  6. trace(acc.child.node[4]); // <node value="five" />
  7. trace(acc.another1); // получаем XMLAccessorList c одним узлом: <another1 />
  8. // неправильно
  9. var child:XMLNode = acc.child[0];
  10. trace(child.node[3]);
  11. // неправильно
  12. trace(acc.childNodes[0].node[3])

Никаких методов и свойств объект XMLAccessor не определяет.
Конструктор XMLAccessor принимает один параметр:

  • XML/XMLNode – Узел, который будет использоватся

Методы объекта XMLAccessorList:

  • toString():String – строковое представление массива элементов списка
  • valueOf():Array – массив содержащий элементы списка
  • getList():Array – массив содержащий элементы списка
  • getLength():Number – количество элементов списке
Загрузить все классы XMLAccessor (45)

Общим недостатком обоих методов можно считать отсутствие поддержки пространств имён – вы не сможете воспользоваться ими в данном случае.

Метки: , , , ,

Комментарии (2) на «XMLMirror и XMLAccessor»

  1. shaman4d:

    Привет. КОнечно все так красиво разбито. Но как то привык пользоваться одной функцией которая перегоняет все XML древо просто в объект с подобъектами типа XML2Object. Если хочешь могу выслать.

  2. admin:

    А как твоя функция ведёт себя если одноимённых дочерних узлов много? Если складывает в массив, то не вижу, чем она лучше XMLMirror? XMLMirror можно расширять добавляя спецефическую функциональность.
    Удобнее, ИМХО.

Добавить комментарий

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