ECMAScript для XML и с чем его "едят"

Итак, кто не в курсе, E4X — это расширение стандарта ECMAScript (на котором основан JavaScript, ActionScript, итд.), которое предоставляет простой и удобный интерфейс для работы с XML. Несмотря на то, что первая версия была опубликована в 2004, E4X не обрел широкого распространения, а жаль. Таким образом, его поддержка ограничивается в следующих JS движках: Gecko (Firefox), SpiderMonkey (основан на Gecko) и Rhino (также движок от Mozilla, только написанный на Java). Так что, если вы динозавр ретроград даже используете такой древний Firefox, как 1.5, то там уже реализована поддержка E4X!

В качестве затравки приведу несколько фишек в E4X

  • XML-структуру можно писать прямо в JS-коде (это не вызовет синтаксической ошибки)
  • E4X можно использовать как простой шаблонизатор (код, обрамленный в фигурные скобки, парсится JS-движком)
  • Возможность расширять прототип объекта XMLList (что позволит еще удобнее работать с XML)
  • Удобная выборка XML-элементов из дерева, подобная XPath (но всё-же уступающая ему)

Ближе к делу, или "примеры всему голова"

Создадим тренировочный объект E4X, который будет использоваться в дальнейших примерах: <script type="text/javascript"> var sales = <sales vendor="John"> <item type="peas" price="4" quantity="6"> <special price="9.99">$</special> </item> <item type="carrot" price="3" quantity="10">zzz</item> <item type="chips" price="5" quantity="3" marked="yes"/> </sales>; </script>

И это всё! Неожидали такого от JavaScript? :) Чтобы убедиться, что всё работает, мы можем вызвать метод sales.toXMLString() или sales.toString(), и получить текстовое представление нашего XML. Как я уже говорил ранее, внутри XML-структуры мы можем использовать placeholder-ы, что в итоге нам даст некое подобие шаблонизатора. Также отмечу, что для создания объекта E4X, можно воспользоваться конструктором XML, например: var xmldoc = XML('<root/>'); при этом ключевое слово new не обязательно.

Пример использования в качестве шаблонизатора: var node = 'abc'; var xmldoc = <{node}>Hello world</{node}> console.log( xmldoc.toXMLString() ); // <abc>Hello world</abc>

Выборка элементов XML-дерева

Итак, правило первое, наш созданный объект sales уже ссылается на корневой элемент, а не на корень (/), как некоторые могли подумать по аналогии с XPath. Таким образом, чтобы получить его аттрибут vendor, мы можем выполнить следующую строку кода: sales.@vendor. Как вы уже заметили, E4X делает синтаксис не совсем привычным, приведу сразу основные сокращения для выборки элементов:

Аттрибуты элементов:
sales.@vendor (или sales.attribute('vendor')), для поиска всех аттрибутов можно использовать звездочку (*), например выполнение строки sales.item[0].@*.length() вернет цифру 3.

Элементы (непосредственные "дети" текущей ноды):
sales.item (или sales.elements('item') или sales['item']), если вам нужно выбрать все элементы, включая текстовые ноды, то можно воспользоваться звездочкой (*), т.е sales.*

Потомки (все элементы, идущие "вглубь" текущей ноды):
sales..item (или sales.descendants('item'))

Для более гибкой выборки элементов, можно указывать условия в круглых скобках: <script type="text/javascript"> sales.item.( function::text() == 'zzz' ).@price.toString() // "3" sales..*.( function::parent().@price == 4 ).text() // "$" sales.item.( function::attribute('marked') == 'yes' ).@type // "chips" sales.item.( @type.match(/^c/i) ).@quantity // список аттрибутов у элементов item, у которых аттрибут type начинается на букву "c" </script> Примечание: внутри предикатов (в круглых скобках), чтобы обратиться к нужному контексту найденных элементов, следует использовать пространство имен "function::". Таким образом, чтобы в условии обратиться к самому себе (в XPath это было бы ./@attr), то в E4X это менее удобно, приходиться писать function::valueOf().@attr (или же сразу function::attribute('attr')). E4X также расширяет цикл for(property in object), позволяя "пробежаться" не по свойствам объекта, а их значениям: <script type="text/javascript"> for each(i in sales.item){ alert([ i.childIndex(), i.@type ].join(' => ')) } </script>

Добавление, удаление элементов и аттрибутов

С этим еще проще, чем можно было себе представить. Для удаления элементов или аттрибутов используется стандартное ключевое слово delete: <script type="text/javascript"> // удалим аттрибут vendor у корневого элемента delete sales.@vendor // удалим все аттрибуты у элементов item delete sales.item.@type // удалим все возможные элементы special delete sales..special </script> Чтобы добавить новый аттрибут или изменить значение уже существующего, достаточно просто присвоить ему значение: sales..special.@price = '100 USD' Чтобы добавить новый элемент в XML-дерево, мы можем воспользоваться или оператором "+" или же готовыми методами из API E4X: <script type="text/javascript"> // добавим новый элемент item в НАЧАЛО списка sales.prependChild(<item type="prepend" price="0"/>); sales.item = <item type="prepend">another newone</item> + sales.item; // добавим новый элемент item в КОНЕЦ списка sales.appendChild(<item type="append" price="10"/>); sales.item += <item type="append">awesome</item>; // добавим новый элемент item ПОСЛЕ ПЕРВОГО элемента sales.insertChildAfter(sales.item[0], <item type="after-first"/>); sales.item[0] += <item type="after-first"/>; // добавим новый элемент item ПЕРЕД ВТОРЫМ элементом sales.insertChildBefore(sales.item[1], <item type="before-second"/>); sales.item[1] = <item type="before-second"/> + sales.item[1]; // ЗАМЕНИМ первый элемент item на что-то новое sales.item[0] = <message>Hello from E4X!</message>; </script>

Расширение прототипа XMLList

Класс XMLList доступен не только в режиме readonly, но его также можно расширять, но перед именем нового метода, при добавлении в прототип, необходимо опять же указывать пространство имен function. Для примера, напишем функцию wrapAll, которая будет оборачивать все элементы в результирующем списке. <script type="text/javascript"> XMLList.prototype.function::wrapAll = function( wrapper ) { for( var i in this ){ this[i] = <{wrapper}>{ this[i] }</{wrapper}> }; return this; }; // вернет нам список item, обернутых в <div> sales.item.wrapAll('div') </script>

Дополнительные фишки E4X

1) E4X позволяет создавать промежуточные элементы "на лету". Рассмотрим код:

<script type="text/javascript"> var node = <xml/>; node.data.message.@title = 'E4X is awesome!'; alert( node.toXMLString() ) // вернет развернутое представление XML в виде строки </script>

2) E4X можно использовать для красивого форматирования XML. Для этого всего лишь создаете E4X-объект через конструктор XML, о чем говорилось вначале статьи, а затем получаете красивый форматированный код через метод toXMLString().

Баги в ECMAScript for XML

При попытке выборки всех элементов sales.item.(@marked) произойдет ошибка. Происходит она потому, что не все элементы item имеют аттрибут marked.
Решение: sales.item.( function::attribute('marked').length() )

Применение

В связи со слабой поддержкой E4X браузерами, он редко где и кем используется в вебе. Но это не мешает вам использовать его в случае:
  1. При написании расширения для Firefox
  2. Если вы используете в работе серверный JavaScript, который работает на одном из приведенных выше движков, поддерживающих E4X
  3. Если вы пишите на AS3 ;)
Надеюсь статья вам понравилась, если есть дополнительные вопросы, задавайте их в комментариях. Более подробно об API E4X (на английском) можно почитать тут и тут.
27.09.2011 / javascript, ecmascript, xml
Понравилась статья?
Подпишись на рассылку через RSS или следуй за нами в Twitter!
Похожие статьи:
Оценка статьи: проголосовало - 0, средняя оценка - 0
Комментарии к статье (0): Добавить комментарий
АНТИСПАМ: Выберите улыбающийся смайл: yep! nope! nope!
Оформление заявки
Файл>>