HTML и CSSXSLTJavaScriptИзображенияСофтEtc
Иван Дембицкий

Flash и XML 21 ноября 2006


 
Задача. Опубликовать недописанную книгу.

Содержание

  • Приемы практической работы с XML
    • Cвязь с внешними источниками данных
      • Создание XML документов, простейшая проверка на корректность
      • Кодировка XML документов
        • Логика предзагрузки внешних данных
        • Простейший прелодер
        • Прелодер для класса XML
        • Многопоточный прелодер
        • Прогнозирование времени загрузки
        • Использование возможностей XML прелодеров для расширения функциональности других объектов.
          • TextField.dataURL
      • Первичный парсинг, подготовка XML объекта к использованию в среде Flash
        • XML.onData(), XML.onLoad()
        • Встроеный парсер, XML.parseXML()
        • Возможность создания пользовательского парсера.
      • Навигация
        • Понятие навигации
        • Расширение свойств навигации
          • XMLNode.nextNode
          • XMLNode.previousNode
          • навигация в индексированных документах
            • Суть индексации
            • Индексация при первичном парсинге
            • Способы индексации на стороне клиента
          • Обращение к узлам как к многомерному массиву
            • Без сохранения результата
            • С сохранением результата
            • Возможный конфликт с индексированным XML
      • Вторичный парсинг
        • Введение
        • Простейшие приемы парсинга
        • Копирование атрибутов
        • Передача массива атрибутов в качестве объекта инициализации
        • Передача дочерних узлов для раздельной обработки
      • Принципы создания проектов с динамическим наполнением
        • Установка связи классов с узлами XML
        • Организация интерфейса общения
        • Организация вещания событий
          • Понятие события, создание вещателя, добавление слушателей
          • События класса, вещатель класса
          • Иерархия вещателей
        • XML зеркало
          • Общее описание
          • Установка связей
          • Использование пользовательских свойств
          • Повторение структуры XML
        • Составной XML
          • Пример меню
      • Подклассы XML
      • Подклассы XMLNode
        • Эмуляция
        • Пользовательский первичный парсинг: XMLNitro, ASK (BrandenHall)
  • Недокументированные и неочевидные возможности объектов XML и XMLNode
    • Содержимое прототипа XMLNode
    • Инициализация XML в плеере
    • Переменная текстового поля
  • Материалы для изучения

Предисловие 

Почему XML 

Формат XML благодаря своей гибкости и универсальности завоевывает все большую популярность в качестве формата обмена данными. В Macromedia Flash объекты XML, XMLNode с пятой версии Flash практически не претерпели никаких изменений. Однако способы их использования благодаря динамично развивающемуся окружению Flash существенно эволюционировали.

Из-за особенностей Flash эффективное использование XML-объектов вполне возможно, однако при неумелом применении их употребление может нанести существенный вред проекту — как производительности, так и удобству обслуживания. Чтобы эффективно использовать XML-данные во Flash, нужно знать приемы оптимального использования, нюансы поведения и устройство объекта XML.

Аудитория 

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

Знакомство с XML-форматом и объектами XML и XMLNode во Flash. Формат XML 

Введение 

Формат XML является одним из стандартизированных языков разметки документов; он рекомендован к использованию консорциумом W3C. Спецификация XML опубликована по адресу www.w3.org/XML.

Переводы спецификации на другие языки найти по адресу www.w3.org/XML/Core/Translations. Формат XML предназначен для описания данных, имеющих логическую структуру.

Сравнение с HTML 

Подавляющее большинство тех, кто связан с веб-технологиями, представляет, что такое HTML-формат. XML- и HTML-форматы очень похожи. Для того чтобы быстро понять, что же такое XML, рассмотрим XML-документ в сравнении с HTML.

Любой XML-документ, так же как и HTML-документ, содержит символы разметки и данные. В XML-формате используются те же самые символы разметки, что и в HTML-документе.

Эта часть HTML-документа

01 
02 
03 
04 
05 
<head>
	<title>
		Any text...
	</title>
</head>
				

также является и корректным XML-документом.

Это легко проверить.

Создаем новый FLA, в первом кадре, в _root, пишем:

_root.my_xml = new XML("<head><title>any text&hellip;</title></head>");
				

Хозяйке на заметку

Обратите внимание на правильное именование ссылок на XML-объекты: оно состоит из двух частей, где первая часть является произвольной и характеризует XML-объект, a вторая часть всегда одинакова: _xml. Такая нотация называется венгерской и принята в ActionScript1.

Использование этой нотации — признак хорошего тона в программировании на ActionScript1. Помимо улучшения читабельности кода, среда разработки Flash при использовании такой нотации помогает вводить имена встроенных свойств методов. При написании в ActionScript2 используется типизация данных.


Тестируем (CTRL+ENTER) и заглядываем в листинг переменных: Debug — List Variables (или CTRL+ALT+V):

В открывшемся окне Output видим:

Здесь нужно обратить внимание на строку

Variable _level0.my_xml = [object #1, class 'XMLNode']
				

Мы видим, что объект #1 принадлежит к классу XMLNode. Flash распознал этот текст как корректный XML — это можно легко проверить, если после ранее написанной строки дописать:

trace(_root.my_xml.status);
				

При тестировании в окне Output будет выведено значение статуса XML равное нулю, что говорит о корректности.

Если при вводе данных вкралась ошибка, то статус будет иметь значение от –2 до –10. Описание перечня ошибок приводится далее, в описании свойства status объекта XML.

Статус XML — очень полезное свойство. Благодаря ему можно проверить любой XML-объект на корректность и принять меры в случае, если входящие XML-данные имеют ошибку.

Номер ошибки поможет найти конкретное место в документе XML, где эта ошибка допущена.

Типы узлов 

Тип узлаНомер типа узла
ELEMENT_NODE1
ATTRIBUTE_NODE2
TEXT_NODE3
CDATA_SECTION_NODE4
ENTITY_REFERENCE_NODE5
ENTITY_NODE6
PROCESSING_INSTRUCTION_NODE7
COMMENT_NODE8
DOCUMENT_NODE9
DOCUMENT_TYPE_NODE10
DOCUMENT_FRAGMENT_NODE11
NOTATION_NODE12

При разборе XML-документа Flash либо приводит узлы к первому или третьему типу (как, например, это происходит с секцией CDATA), либо помещает в соответствующие объекты (как, например, атрибуты или объявление DOCTYPE). Далее по мере необходимости мы рассмотрим это подробнее.

Элементы, составляющие узел 

Определим, из каких основных элементов может состоять узел XML во Flash. Как сказано выше, узлы во Flash делятся на два типа: элемент XML и текстовый узел.

Текстовый узел представляет собой любой набор символьных данных, включая пробелы, табуляции, переносы строк и т. п. Ограничения: текстовый узел не может содержать символы разметки < и >, которые следует заменять символами подстановки или помещать в секцию CDATA.

Хозяйке на заметку

Как узнать, какие символы подстановки использовать? Flash автоматически конвертирует запрещенные символы в символы подстановки, если они помещены в секцию CDATA.

Отсюда простой способ — создание объекта XMLNode четвертого типа:

trace(new XMLNode(4, '<>'));

В данном примере будут выведены символы подстановки для символов < и >.


Элемент XML состоит из символов разметки, имени узла и атрибутов с их значениями.

Пример узла XML первого типа без вложенных элементов:

<node_name attribute1='any value' attributeN='any value' />
				

Элементы:

< и > — открывающий и закрывающий символы разметки;

node_name — имя узла, может быть любым; ограничения: имя узла не должно содержать пробелов, знаков пунктуации и символов разметки < и >;

attribute1 — имя атрибута, может быть любым; атрибутов в данном примере два, но их может быть неограниченное количество или не быть вовсе; ограничения: имя атрибута не должно содержать пробелов, знаков пунктуации и символов разметки < и >;

any value — значение атрибута, может быть любым, может содержать пробелы и знаки пунктуации; обязательно должно быть заключено в кавычки.

Хозяйке на заметку

Ограничения: значение атрибута не может содержать символов разметки < и > или кавычек, их следует заменять символами подстановки.


Более подробно о правилах создания узлов можно узнать на сайте консорциума W3C по адресу http://www.w3.org/XML/.

В некоторых случаях во Flash ограничения возможно обойти, но не стоит этого делать без крайней необходимости.

Тип 1: элемент XML (ELEMENT_NODE) 

Тело XML не имеет предопределенных имен тегов, для него нет разницы — <head> или <leg>, если в примере выше заменить head на leg (закрывающие теги тоже нужно заменять), то никакой ошибки не возникнет. В HTML же имена тегов строго предопределены.

XML, в отличие от HTML, не может содержать незакрытых тегов, как, например, широко используемый без знака закрытия тег <BR> в HTML.

В XML значения атрибутов обязательно должны быть заключены в кавычки. HTML допускает возможность использования атрибутов без кавычек.

Как и в HTML, возможны две формы записи узла:

<имя_узла имя_атрибута="значение атрибута" />
				

и

<имя_узла имя_атрибута="значение атрибута"></имя_узла>
				

Тип 3: текстовый узел (TEXT_NODE) 

Текстовый узел — это обычный текст, не содержащий символов разметки. Символы разметки < и > должны быть заменены соответствующими символами подстановки &lt; &gt; или &#60; &#62;.  Однако в отличие от HTML, возможно использование символов разметки внутри специальной секции CDATA. В таком случае Flash при разборе XML-документа самостоятельно сконвертирует символы разметки в символы подстановки. Во Flash следующие варианты записи текстовых узлов, находящихся внутри узла text_holder, дадут одинаковый результат:

вариант 1

01 
02 
03 
<text_holder>
	<![CDATA[<text node>]]>
</text_holder>
				

вариант 2

01 
02 
03 
<text_holder>
	&#60;text node&#62;
</text_holder>
				

вариант 3

01 
02 
03 
<text_holder>
	&lt;text node&gt;
</text_holder>

				

Это легко проверить, если написать следующий скрипт и протестировать:

<![CDATA[<text node>]>
trace(new XML("<text_holder></text_holder>"));
trace(new XML("<text_holder>&#60;text node&#62;</text_holder>"));
trace(new XML("<text_holder>&lt;text node&amp;gt;</text_holder>"));
				

Мы увидим в окне Output три одинаковые строки.

Это не все сходства и отличия XML и HTML, но для продолжения знакомства этого вполне достаточно.

Структурные отношения 

Иерархия XML 

XML-формат допускает вложенность элементов друг в друга. Если один или несколько элементов XML расположены между тегами открытия и закрытия другого узла, то такие элементы являются вложенными (nested).

В XML вложенные элементы, по отношению к элементу, их содержащему, называются дочерними (children). Соответственно, по отношению к дочерним элементам, элемент, их содержащий, называется родительским (parent). Элементы, расположенные в одном родителе на одном уровне, называются братьями (siblings).

Любой родитель с дочерними узлами или любой дочерний элемент называется узлом (node).

Дочерние узлы зависимы от родительских. Например, если родительский узел будет программно удален, то будут удалены и его дочерние узлы.

Всегда имеется единственный узел, который содержит в себе все остальные узлы, называемый корневым узлом XML (XML root node).

Узел, c которым в данный момент производятся какие-либо действия, называется текущим узлом (current node).

С помощью XML очень удобно описывать структурированные, т. е. взаимозависимые данные. Рассмотреть это можно на примере структуры меню.

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

01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
_root.xml_string = "<item name='item1'>";
_root.xml_string += "<item name='item2'>";
_root.xml_string += "<item name='item3' />";
_root.xml_string += "</item>";
_root.xml_string += "</item>";
_root.xml_string += "<item name='item4'>";
_root.xml_string += "<item name='item5'/>";
_root.xml_string += "</item>";
_root.my_xml = new XML(_root.xml_string);
delete _root.xml_string
if (_root.my_xml.status) {
trace("incorrect xml");
}
				

Хозяйке на заметку

Переменной _root.xml_string можно было присвоить сразу всю строку, но в подобном случае в дальнейшем будет неудобно просматривать или исправлять такую строку.


Если при тестировании на CTRL+ENTER в окне Output появилось сообщение incorrect xml, значит, статус XML не равен нулю, что говорит об ошибке. В этом случае внимательно проверьте соответствие вашей записи приведенной выше.

Если сообщение не появилось, то в листинге переменных XML должен отобразиться в таком виде:

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

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

Структурные типы XML 

По структурной организации XML-документы можно условно разделить на три типа: одномерные, многомерные и смешанные.

Многомерный XML-документ характеризуется тем, что его узлы могут содержать вложенные узлы того же вида и функциональности. Ярким примером такой структуры является XML-описание меню с произвольной глубиной вложенности элементов. В примере многомерной структуры все узлы описаны одинаковым образом, но у них разная и как правило непредсказуемая вложенность друг в друга.

01 
02 
03 
04 
05 
06 
07 
08 
<item name="item1">
	<item name="item2">
		<item name="item3" />
	</item>
</item>
<item name="item4">
	<item name="item5" />
</item>

				

Одномерная структурная организация XML-документа характеризуется тем, что узлы не имеют дочерних узлов своего вида и обычно имеют жестко установленную, иногда стандартизированную, иерархию.

01 
02 
03 
04 
05 
06 
07 
08 
<head>
	<title>
		<field name="my_txt" text="Hello!" />
	</title>
</head>
<controls>
	<checkBox name="check_mc" />
</controls>

				

Смешанная структурная организация XML-документа представляет собой комбинацию одномерной и многомерной структур.

01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
<head>
	<title>
		<field name="my_txt" text="Hello!" />
	</title>
</head>
<controls>
	<checkBox name="check_mc" property="colored" />
</controls>
<menu>
	<item name="item1">
		<item name="item2">
			<item name="item3" />
		</item>
	</item>
	<item name="item4">
		<item name="item5" />
	</item>
</menu>

				

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

Объекты XML и XMLNode во Flash 

Для того чтобы иметь полноценный программный доступ к данным в XML-формате, во Flash существуют два класса: XML и XMLNode. Класс XML является подклассом XMLNode, который в свою очередь является подклассом Object.

Важно!

Принадлежность объекта к классу определяет наличие той или иной функциональности у каждого экземпляра объектов этого класса. Во Flash существуют предопределенные классы, такие как MovieClip, Array, XML, XMLNode, и другие. По сути дела, экземпляры этих классов, например, Array и XMLNode, отличаются друг от друга лишь внутренней структурной организацией и набором функций и свойств, характерных для этих классов.

Структурная организация объекта, как правило, формируется конструктором класса, а функции и свойства наследуются экземплярами из прототипов класса и суперклассов.

В дальнейшем изучении XML во Flash часто потребуется понимание принципов объектно-ориентированного программирования (ООП).

Настоящая книга не ставит целью изучение принципов ООП, и мы лишь изредка будем останавливаться на некоторых особенностях их применения к объектам XML и XMLNode.

Если вам незнакомы понятия и принципы ООП или по мере изучения этой книги у вас возникнут вопросы, с этим связанные, то рекомендую изучить статьи Робина Дебреиля на сайте www.debreuil.com. Там имеются ссылки на переводы этих статей на разные языки, в том числе на русский.


new XML() 

Для создания экземпляра XML существует конструкция, синтаксис которой выглядит следующим образом:

new XML([source])
				

где source — строковые данные в формате XML, необязательный параметр. Пример: создание пустого объекта XML.

my_xml = new XML()
				

Конструктору можно передать в качестве параметра строковые данные в формате XML, которые конструктор класса превратит в объект XML:

my_xml = new XML("<example>example text</example>")
				

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

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

my_xml = XML
				

В листинге переменных результат будет следующим:

Отображается безымянный узел первого типа, не имеющий аргументов, и ссылка на объект prototype класса XMLNode.

new XMLNode() 

В 6-й и 7-й версиях Flash классу XMLNode «Макромедиа» отказывала в праве на документированную жизнь. Класс фактически существовал, но в документации не описывался. В 8-й версии Flash класс XMLNode задокументирован.

Синтаксис конструктора таков:

new XMLNode(type, source)
				

где type — это номер типа, который может принимать значения от 1 до 12.

1ELEMENT_NODE
2ATTRIBUTE_NODE
3TEXT_NODE
4CDATA_SECTION_NODE
5ENTITY_REFERENCE_NODE
6ENTITY_NODE
7PROCESSING_INSTRUCTION_NODE
8COMMENT_NODE
9DOCUMENT_NODE
10DOCUMENT_TYPE_NODE
11DOCUMENT_FRAGMENT_NODE
12NOTATION_NODE

Для создания экземпляров XMLNode существуют два специальных метода 

Для создания узлов первого типа:

XML.createElement(name) 
				

где name — обязательный параметр, который может быть и пустой строкой.

Для создания узлов текстового типа:

XML.createTextNode(text)
				

где text — обязательный параметр, который может быть и пустой строкой.

По сути, функции createElement и createTextNode в плеере заданы примерно так:

01 
02 
03 
04 
05 
06 
XML.prototype.createElement = function (name) {
 return name == undefined ? undefined : new XMLNode(1, name)
};
XML.prototype.createTextNode = function (text) {
 return text == undefined ? undefined : new XMLNode(3, text)
};

				

Однако эти способы не всегда удовлетворяют потребностям. Иногда требуется создать объект XMLNode из текста в формате XML. В этом случае используется конструктор класса XML с последующим получением первого потомка:

my_new_node = new XML("<node attribute='value'/>").firstChild
				

Объект prototype, некоторые принципы наследования 

При объявлении функции всегда создаются два объекта: тело функции и объект prototype, принадлежащий этой функции. Объект prototype используется как хранилище свойств и методов класса и имеет интересные и крайне важные особенности, связанные с организацией наследования.

Во Flash-плеере при обращении к методу, свойству, переменной и т. д. их поиск происходит по следующей схеме:

1.проверяется наличие в экземпляре класса; если нет, то выполняется п. 2;
2.проверяется в прототипе класса; если нет, то выполняется п. 3;
3.проверяется в прототипе вышестоящего класса; если нет, то выполняется п. 4;
4.
5.проверяется в прототипе класса Object, если нет, п. 6;
6.вызывается функция __resolve(), если она не определена, п. 7;
7.возвращается undefined.

Таким образом, если в прототип какого-либо класса поместить переменную, свойство или метод и если в экземпляре класса эта переменная, свойство или метод не определены, то значение будет найдено в прототипе и возвращено как значение экземпляра.

Применительно к объектам классов XML и XMLNode справедливо утверждение, что класс XML является подклассом XMLNode, который, в свою очередь, является подклассом Object. Соответственно, по этой причине любой XML-объект помимо собственных свойств и методов будет иметь доступ к свойствам и методам класса XMLNode и класса Object, а любой объект XMLNode помимо собственных свойств и методов будет иметь доступ к свойствам и методам класса Object, но не будет иметь доступа к свойствам и методам класса XML.

Методы свойства и события объектов XML и XMLNode 

Реализация свойств во Flash 

Прежде чем перейти к непосредственному изучению свойств XML и XMLNode, следует знать, как они реализованы во Flash. Все свойства XML имеют getter- и setter-методы. Это необходимо для предотвращения нежелательного изменения свойств объектов XML-дерева.

Например, попытка присвоить неверный тип узла не приведет к присвоению неправильного типа:

01 
02 
03 
04 
my_xml = new XML("<node />")
trace(my_xml.firstChild.nodeType)
my_xml.firstChild.nodeType = 3
trace(my_xml.firstChild.nodeType)

				

В результате в окне Output будет дважды выведено значение 1; это говорит о том, что попытка назначить неверное значение свойства не удалась.

Но это вовсе не значит, что никакие свойства объектов XML изменять нельзя. Нельзя изменять или удалять только такие свойства, изменение или удаление которых может привести к нарушению иерархических взаимоотношений в дереве XML.

Важно!

Любой объект во Flash может иметь собственные и унаследованные методы и свойства. Любой объект может быть добавлен в список слушателей любых событий, и ему могут быть назначены обработчики этих событий.

Вышеперечисленное в итоге определяет реакцию объекта на те или иные вызовы его методов на присвоение ему новых значений свойств или на возникновение событий. Реакции определяет поведение объекта.

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


Для эффективной работы с данными в формате XML поведение объектов дерева XML организовано исходя из тех задач, которые необходимо решать:

— связь с внешними источниками данных;

— разбор текстовых данных и создание XML-объекта;

— навигация по дереву XML;

— управление данными;

— управление структурой данных;

— расширение функциональности объектов XML.

Далее именно в этом порядке будут рассмотрены свойства, методы и события объектов XML-дерева.

Связь с внешними источниками данных 

Многие XML-данные получаются Flash-роликом извне. Для организации связи с внешними источниками данных предусмотрены следующие методы, свойства и события:

XML.load()
XML.getBytesLoaded()
XML._bytesLoaded
XML.getBytesTotal()
XML._bytesTotal
XML.onData()
XML.onLoad()
XML.loaded
XML.sendAndLoad()
XML.send()
XML.addRequestHeader()
XML.contentType
XML.docTypeDecl

				

Обратите внимание, что все они заданы для класса XML и по умолчанию недоступны для узлов XMLNode, но, разумеется, при желании их можно сделать доступными многими способами.

XML.load() 

Метод. Предназначен для инициализации загрузки внешних текстовых данных в формате XML.

Синтаксис:

my_xml.load(url)
				

Помимо инициализации загрузки данных устанавливает свойство loaded-экземпляра XML равным false.

В качестве url может быть использован адрес любого файла или программы, которая возвращает текстовые данные в XML-формате. Возможна как относительная, так и абсолютная адресация, возможна адресация от корневой папки источника данных или от адреса, указанного в атрибуте BASE.

Абсолютная адресация 

Абсолютная адресация, как правило, используется в тех случаях, когда, необходимо получать данные из иного домена, чем тот, в котором расположена HTML-страница, содержащая swf-файл. Пример:

my_xml.load("http://www.anydomain.com/xmlfiles/data.xml")
				

Важно!

Политика безопасности Macromedia Flash до 7-й версии плеера запрещала загрузку каких-либо данных из других доменов. Однако это ограничение всегда было довольно просто обойти: на текущем домене располагался простой скрипт, который брал на себя загрузку данных из другого домена и возвращал их ролику.

Начиная с 7-й версии плеера вступила в силу новая политика безопасности: стало возможным на сервере-отправителе данных определить круг доменов, Flash-ролики из которых имеют право получать эти данные.

Подробнее с политикой безопасности можно ознакомиться на сайте производителя: www.macromedia.com/support/flash/ts/documents/security_sandbox.htm.

При этом способ с применением серверного скрипта, описанный выше, не потерял своей актуальности.


Адресация от корня сайта 

Если данные находятся в том же домене, где расположен Flash-ролик, то имя домена можно заменить на символ /.

Пример

Если ролик расположен в домене www.anydomain.com, то абсолютную адресацию

my_xml.load("http://www.anydomain.com/xmlfiles/data.xml")
				

можно заменить на

my_xml.load("/xmlfiles/data.xml")
				

Данные будут загружены из того же домена, что и HTML-страница, содержащая Flash-ролик, из папки xmlfiles.

Относительная адресация 

Часто бывает удобно указать адрес данных, используя относительную адресацию. Правила этой адресации аналогичны используемым в HTML, символы ../ указывают плееру на необходимость подняться на уровень выше. Можно подниматься на один, два и более уровней, используя такое обращение нужное количество раз.

Если HTML-страница и swf-файл находятся в различных папках, то адресация указывается от адреса HTML-страницы.

Адресация относительно base 

При встраивании Flash-ролика в HTML-страницу в атрибутах тегов OBJECT и EMBED можно указать адрес, который будет являться базовым для относительных путей. Делается это следующим образом:

01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
<OBJECT>
[другие параметры]
<PARAM NAME="BASE" VALUE="/any_folder/any_folder/">
[другие параметры]
<EMBED 
[другие параметры]
BASE="/any_folder/any_folder/" 
[другие параметры] >
</EMBED>
<OBJECT>

				

В нашем примере, если указать в качестве BASE адрес /xmlfiles, обращение к данным будет выглядеть следующим образом:

my_xml.load("data.xml")
				

Плеер достроит адрес, используя BASE, и получит данные из папки xmlfiles, находящейся в корне сайта.

Получение данных от серверных программ 

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

Например:

my_xml.load("/counter/show.php?name=printer.swf")
				

В этом примере серверной программе, подсчитывающей количество просмотров Flash-роликов, передается имя просматриваемого ролика.

Разумеется, данные, передаваемые серверной программе, практически не бывают статическими, как в примере выше. Строку запроса обычно составляют, используя переменные ролика. Например:

01 
02 
03 
var str = "name="+_root.user_name
str+="&scores"+_root.total_scores
my_xml.load("/counter/show.php?"+str)

				

Так, если переменная _root.user_name содержит строку John а переменная _root.total_scores содержит цифру 56, итоговая строка запроса будет аналогична такой:

my_xml.load("/counter/show.php?name=John&scores=56")
				

Запрет кеширования запросов 

Если использовать способы обращения, рассмотренные в предыдущем примере, то существует вероятность того, что запрос не будет направлен серверу, а ответ будет взят из кеша браузера. Чтобы исключить это, следует использовать дополнительную переменную, которая будет отличать один запрос от другого. Практика показала, что лучше всего для этого использовать функцию getTime объекта Date следующим образом:

01 
02 
03 
04 
var str = "name="+_root.user_name
str+="&scores="+_root.total_scores
str+="&random="+new Date().getTime()
my_xml.load("/counter/show.php?"+str)

				

Этот способ полностью гарантирует, что запрос будет отличаться от предыдущих и ответ не будет взят из кеша браузера.

Возможно, что использование запрета на кеширование со стороны Flash-ролика несколько избыточно. При правильных настройках серверных программ ролику будут возвращены данные, полученные от сервера, а не из кеша браузера. Однако наличие различных браузеров с часто неожиданным поведением, а также простая невнимательность серверных программистов могут сыграть злую шутку.

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

Использование алгоритмов сжатия 

Для уменьшения объема передаваемых данных иногда применяется предварительное сжатие данных на стороне сервера, например, с использованием GZIP.

Это возможно и по отношению к XML-документам, которые запрашивает Flash-ролик.

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

Таким образом, использование алгоритмов сжатия данных ложится полностью на браузер пользователя, а не на Flash-проигрыватель и может быть реализовано без каких-либо изменений во Flash-ролике. С другой стороны, придумано достаточно много различных способов осуществлять обмен сжатыми данными независимо от возможностей пользовательских браузеров.

Существуют различные реализации использования алгоритма GZIP с распаковкой данных внутри Flash-ролика. Но думаю, что одним из наиболее оригинальных решений в этом направлении является идея Марио Клингеманна, который предложил упаковывать XML-данные в сжатый swf: www.quasimondo.com/archives/000213.php. Это избавляет от необходимости распаковки данных с помощью ActionScript, так как при загрузке swf-файл с данными распаковывается Flash-плеером.

Защищенный режим 

В сети передача данных в защищенном режиме возможна с использованием протокола HTTPS. Flash как, и в случае с использованием алгоритмов сжатия данных не требует особых мероприятий для организации передачи данных в защищенном режиме. Вся работа по обслуживанию HTTPS ложится на браузер. Таким образом, любой Flash-ролик, созданный для работы по протоколу HTTP, не потребует дополнительных доработок для протокола HTTPS. Требования безопасности налагают определенные ограничения на взаимодействие Flash-ролика на безопасной странице с данными; подробнее об этом можно прочесть на сайте производителя: www.macromedia.com/support/flash/ts/documents/security_sandbox.htm.

XML.sendAndLoad() 

Метод. В отличие от XML.load(), конвертирует объект XML в строку и посылает эту строку серверной программе методом POST. Также требуется указать XML-объект — получатель ответа серверной программы. Этим объектом может быть и текущий XML-объект.

Синтаксис:

my_xml.sendAndLoad(url, target_xml)
				

После вызова этого метода события onData и onLoad произойдут в target_xml, а не в XML-объекте — инициаторе загрузки.

XML.send() 

Метод. Его отличие от предыдущего состоит в том, что ответ серверной программы возвращается не XML-объекту Flash-ролика, а окну браузера. Соответственно, требуется указывать, в какое окно браузера загрузить ответ сервера. По умолчанию указано _self.

Синтаксис:

my_xml.send(url [, window])
				

По сути дела, этот метод аналогичен команде getURL с передачей на сервер строки XML методом POST.

Хозяйке на заметку

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

Любые наборы переменных и их значений по-прежнему можно передавать в строке запроса после вопросительного знака, разделяя пары переменных-значений символом &, а переменные и их значения — символом =.


XML.getBytesLoaded(), XML._bytesLoaded, XML.getBytesTotal(), XML._bytesTotal 

Для отслеживания состояния процесса загрузки данных в объекте XML существуют две функции: getBytesLoaded и getBytesTotal. Они возвращают объем загруженных данных в байтах и объем всего файла в байтах.

В плеере они реализованы примерно следующим образом:

01 
02 
03 
04 
05 
06 
XML.prototype.getBytesLoaded = function () {
return this._bytesLoaded
}
XML.prototype.getBytesTotal = function () {
return this._bytesTotal
}
				

Свойства _bytesLoaded и _bytesTotal, которые имеются у объекта XML, не документированы. Это значит, что их использование влечет за собой риск того, что в последующих версиях Flash-плеера они будут недоступны, хотя это маловероятно.

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

На практике функции обслуживания предзагрузки используются для получения процента загрузки с помощью простой конструкции:

01 
02 
03 
var total = my_xml.getBytesTotal()
var loaded = my_xml.getBytesLoaded()
var percents = Math.floor(loaded/total*100)

				

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

XML.onData() 

Это первое событие, которое происходит по окончании загрузки XML-данных. Функция onData определена в прототипе класса XML и выглядит примерно следующим образом:

01 
02 
03 
04 
05 
06 
07 
08 
09 
XML.prototype.onData = function (source) {
 	if (source == null) {
 	 	this.onLoad(false);
 	} else {
 	 	this.parseXML(source);
 	 	this.loaded = true;
 	 	this.onLoad(true);
 	}
}

				

В качестве аргумента source функция получает все пришедшие данные.

Если никаких данных не получено, то вызывается событие onLoad с передачей false в качестве аргумента.

Если пришли данные, то вызывается процедура разбора пришедших данных, затем свойство loaded устанавливается равным true и вызывается событие onLoad с передачей ему true в качестве аргумента.

Функцию onData при желании можно переназначить как классу XML, так и любому конкретному экземпляру XML.

XML.onLoad() 

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

К моменту вызова события onLoad полученные данные уже разобраны методом parseXML и помещены в тело экземпляра XML. Используется событие onLoad обычно следующим образом:

01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
my_xml = new XML()
my_xml.load("/xmlfiles/data.xml")
my_xml.onLoad=function(success) {
 	if(!success || this.status){
 	 	return this.customErrorHandler(this.status, success)
 	}
 	this.customParsingProcedure()
}
my_xml.customParsingProcedure = function (){
// custom actions here
}
my_xml.customErrorHandler = function (status, success){
trace("loaded: " + success +" valid: " + !this.status)
// custom actions here
}

				

В первую очередь проверяется успешность загрузки XML-данных (аргумент success) и отсутствие ошибок при разборе XML-документа (свойство status экземпляра XML). Если хотя бы одно из вышеперечисленных условий не возвращает true, то дальнейшая обработка, как правило не имеет смысла и выполнение onLoad прерывается выходом в пользовательский обработчик ошибок, в данном случае это пользовательский метод customErrorHandler. Если документ успешно загружен и он соответствует требованиям XML формата, то можно осуществить дальнейшую его обработку пользовательским методом customParsingProcedure.

XML.loaded 

Свойство. Может иметь следующие значения:

undefined — если не предпринималось попыток загрузить внешние данные.

false — если предпринята попытка загрузить внешние данные, но эти данные еще не загружены, либо если произошла ошибка загрузки данных. Это значение устанавливается при вызове метода XML.load.

true — если попытка получить внешние данные оказалась успешной. Это значение устанавливается при вызове метода XML.onData.

XML.addRequestHeader() 

Начиная с 7-й версии плеера появилась возможность добавлять собственные теги в заголовок запроса.

Синтаксис:

my_xml.addRequestHeader(header_name, header_value)
				

либо с использованием массива:

01 
02 
headers_array =[header_1name, header_1value, header_n_name, header_n_value]
my_xml.addRequestHeader(headers_array)

				

На момент написания этой книги использование заголовков HTTP-запроса считается довольно экзотическим способом передачей дополнительных данных для серверных программ и с успехом заменяется передачей данных через строку запроса или добавление специальных узлов в дерево XML.

XML.contentType 

Свойство. Представляет MIME тип данных, передаваемых через заголовок документа.

По умолчанию имеет значение application/x-www-form-urlencoded.

За всю мою практику я ни разу не столкнулся с необходимостью изменять это свойство.

XML.docTypeDecl, XML.xmlDecl 

Flash никак не интерпретирует узлы XML, содержащие объявления типа документа и анализаторы достоверности. Такие узлы в текстовом виде помещаются в соответствующие свойства XML-объекта и никак не влияют на разбор или декодировку данных.

Пример узлов объявления типа документа:

01 
02 
<?xml version="1.0" encoding="windows-1251"?>
<!DOCTYPE Imprimatur SYSTEM "/entities.dtd">

				

В данном примере указание кодировки «windows-1251» никак не повлияет на разбор этого XML-документа во Flash. Детальнее практика применения различных кодировок будет рассмотрена далее в этой книге.

Анализ текстовых данных и создание XML-объекта 

Текстовые данные в формате XML не становятся сами по себе объектом XML. Чтобы превратить текстовые данные в XML-объект, существует специальная процедура анализа текстовых данных, называемая парсингом или первичным парсингом.

Для организации процедуры парсинга класс XML имеет следующие свойства и методы:

XML.parseXML()
XML.status
XML.ignoreWhite

				

Первичный парсинг XML-документа — это анализ текстовых данных в формате XML и помещение этих данных в соответствующие объекты экземпляра XML, а также другие действия, в конечном итоге формирующие экземпляр XML-объекта.

Например, XML-документ следующего содержания:

<item name="text">Hello world!</item>
				

после парсинга будет представлять собой четыре объекта:

  • объект XML (класс XML)
  • узел XML с именем «item» (класс XMLNode, первый тип)
  • объект attributes узла item (класс Object)
  • текстовый узел (класс XMLNode, третий тип)

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

В процессе парсинга не создаются никакие внешние ссылки на эти объекты. Доступ к ним можно получить только через ссылку на экземпляр объекта XML.

Процедура парсинга осуществляется методом XML.parseXML.

XML.parseXML() 

Метод. Осуществляет парсинг текстовых данных в формате XML.

Синтаксис:

my_xml.parseXML(source)
				

Ничего не возвращает, а изменяет содержимое текущего XML-объекта.

Ошибочно пытаться сделать таким образом:

my_xml = my_xml.parseXML("<item />")
				

В этом случае, поскольку метод parseXML ничего не возвращает, my_xml примет значение undefined.

Правильное использование:

my_xml.parseXML("<item />")
				

Во встроенных функциях метод parseXML используется:

  • в конструкторе класса XML, если в качестве параметра передана соответствующая строка;
  • в обработчике события onData.

Метод parseXML в процессе разбора устанавливает значение свойству status экземпляра XML.

Если в процессе разбора обнаруживается ошибка, то процедура парсинга прерывается. Созданные процедурой парсинга новые узлы не удаляются.

XML.status 

Свойство. Устанавливается процедурой парсинга. Принимает значение 0 при отсутствии ошибок. В случае ошибки парсинга принимает значение от −2 до −10, каждое из которых указывает на тип ошибки.

Вот перечень значений, которые может принимать status:

ОписаниеСтатус
Нет ошибок: синтаксический анализ закончен успешно0
Раздел CDATA не был закончен должным образом−2
Объявление XML не было закончено должным образом−3
Объявление DOCTYPE не было закончено должным образом−4
Комментарий не был закончен должным образом−5
XML-элемент сформирован неправильно−6
Не хватает памяти−7
Значение атрибута не было закончено должным образом−8
Тег начала не был согласован с конечным тегом−9
Конечный тег указан без соответствующего начального тега−10

Навигация 

Для организации навигации по дереву XML класс XMLNode имеет следующие свойства:

XMLNode.firstChild 
XMLNode.lastChild
XMLNode.childNodes
XMLNode.nextSibling
XMLNode.previousSibling
XMLNode.parentNode
XMLNode.attributes.id

				

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

Важно!

Переход с одного узла на другой — это получение ссылки на узел XML с использованием навигационных свойств другого узла XML или объекта XML.

Подняться на шаг в иерархии узлов означает перейти на родительский уровень. Родитель у узла может быть только один.

Опуститься на шаг в иерархии узлов означает перейти на уровень потомков текущего узла.

Переход возможен:

  • на первого потомка (свойство firstChild);
  • на последнего потомка (свойство lastChild);
  • на потомка по его порядковому номеру в массиве узлов-потомков (childNodes[3] — это переход на четвертого потомка, потому, что нумерация массивов начинается с нуля)

Глубина узла — степень вложенности узла в другие узлы: фактически это количество предков данного узла в иерархии XML.


Дочерние, братские, родительские узлы 

Каждый узел XML-объекта имеет структурные связи с другими узлами. Однако каждый узел имеет ссылки не на все остальные узлы, а только на близлежащие в иерархии: на родительские, дочерние и братские узлы.

Соответственно, у каждого узла имеются ссылки на:

  • на дочерние узлы
    1.node_xml.firstChild — первый потомок в узле node_xml
    2.node_xml.lastChild — последний потомок в узле node_xml
    3.node_xml.childNodes — массив всех потомков узла node_xml
  • на братские узлы
    1.node_xml.nextSibling — следующий брат узла node_xml
    2.node_xml.previousSibling — предыдущий брат узла node_xml
  • на родительский узлы
    1.node_xml.parentNode — родительский узел узла node_xml

Пример навигационных переходов 

Чтобы проиллюстрировать использование навигационных свойств для переходов по узлам, создадим объект XML и затем, зная структуру XML-данных, последовательно получим ссылку на каждый из узлов этого XML и выведем в окно Output значение атрибута name каждого узла:

01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
// создаем объект XML:
_root.xml_string = "<item name='item1'>";
_root.xml_string += "<item name='item2'>";
_root.xml_string += "<item name='item3' />";
_root.xml_string += "</item>";
_root.xml_string += "</item>";
_root.xml_string += "<item name='item4'>";
_root.xml_string += "<item name='item5'/>";
_root.xml_string += "</item>";
_root.my_xml = new XML(_root.xml_string);
delete _root.xml_string
if (_root.my_xml.status) {
trace("incorrect xml");
}

// НАВИГАЦИЯ
// переходим на объект XML √ это самый верхний узел в иерархии узлов XML
_root.link_to_node = _root.my_xml
// опускаемся на шаг, на первого потомка объекта XML:
_root.link_to_node = _root.link_to_node.firstChild
trace(_root.link_to_node.attributes.name)
// опускаемся на шаг, на первого потомка
_root.link_to_node = _root.link_to_node.firstChild
trace(_root.link_to_node.attributes.name)
// опускаемся на шаг, на первого потомка
_root.link_to_node = _root.link_to_node.firstChild
trace(_root.link_to_node.attributes.name)
// поднимаемся на шаг
_root.link_to_node = _root.link_to_node.parentNode
trace(_root.link_to_node.attributes.name)
// поднимаемся на шаг
_root.link_to_node = _root.link_to_node.parentNode
trace(_root.link_to_node.attributes.name)
// переходим на следующего брата
_root.link_to_node = _root.link_to_node.nextSibling
trace(_root.link_to_node.attributes.name)
// опускаемся на шаг, на первого потомка
_root.link_to_node = _root.link_to_node.firstChild
trace(_root.link_to_node.attributes.name)
delete _root.link_to_node

				

Затем тестируем.

В окне Output должен появиться результат:

Вначале мы пошагово спустились до третьего узла, затем пошагово вернулись наверх, к первому узлу, затем с первого узла перешли на следующего брата — на четвертый узел — и спустились к пятому узлу. Мы видим, что переменная _root.link_to_node поочередно содержала ссылку на эти узлы XML и нам были доступны их атрибуты.

По окончании обхода мы удалили эту переменную командой delete _root.link_to_node.

Важно!

Стоит заметить, что командой delete _root.link_to_node мы удалили только ссылку на узел, но не сам узел.

Во Flash объекты и ссылки на них — это разные вещи. Удаляя ссылку на объект, мы удаляем сам объект только в том случае, если на этот объект больше не остается никаких ссылок. Это легко проиллюстрировать примером:

_root.my_object = new Object()
_root.my_link_to_object = _root.my_object
delete _root.my_object
trace(_root.my_link_to_object)

В окне Output будет выведена информация о том, что такой объект существует. Объект также будет виден в листинге переменных.

Почему так происходит?

Дело в том, что строка

_root.my_object = new Object()

не создает объект my_object. На самом деле создается безымянный объект, а в переменную my_object только помещается ссылка на него.

Строка

_root.my_link_to_object = _root.my_object

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

Во Flash у программиста нет прямого способа удаления объекта. Любой объект удаляется только сборщиком мусора, и только в случае, если на него не осталось ссылок.

Однако это поведение относится исключительно к наследникам класса Object.

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

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

Понимание этого механизма может сэкономить массу времени при программировании на ActionScript.


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

Атрибут id 

Среди навигационных свойств id — идентификатор узла — стоит особняком. Это связано с существенными отличиями его реализации и использования от остальных навигационных свойств.

id — специальный атрибут узла. Может быть и цифровым и строковым. Должен быть уникальным для каждого узла объекта XML. Если в узлах XML-объекта указаны атрибуты id, то при парсинге они будут распознаны, и будут созданы одноименные переменные в объекте XML, а также в эти переменные будут помещены ссылки на узлы, содержащие эти идентификаторы.

Если же атрибуты id не будут уникальны, то при парсинге соответствующая переменная будет перезаписана столько раз, сколько встретится один и тот же id и в итоге эта переменная будет иметь значение последнего узла с таким id.

Фактически наличие id у узла позволяет обращаться напрямую к любому узлу XML, используя этот идентификатор:

01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
xml_string = "<item id='1' name='item1'>";
xml_string += "<item id='2' name='item2'>";
xml_string += "<item id='3' name='item3' />";
xml_string += "</item>";
xml_string += "</item>";
xml_string += "<item id='4' name='item4'>";
xml_string += "<item id='5' name='item5'/>";
xml_string += "</item>";
_root.my_xml = new XML(xml_string);
delete xml_string;
if (_root.my_xml.status) {
	trace("incorrect xml");
}
for (i=1; i<6; i++) {
	trace(i+" - "+_root.my_xml[i].attributes.name);
}

				

При тестировании этого примера в окне Output должно появиться следующее:

Значение такой возможности трудно переоценить. В некоторых случаях использование id повышает во много раз производительность программ и значительно упрощает процесс программирования.

В дальнейшем мы подробно рассмотрим приемы работы с этим идентификатором.

Как уже было сказано выше, атрибут id распознается во время процедуры парсинга, однако это не единственный путь. Природа id проста — это находящиеся в корне XML-объекта обычные ссылки на узлы этого XML-объекта. Поэтому подобные ссылки можно в любой момент создавать, изменять или удалять. В дальнейших примерах это будет неоднократно использовано.

В 8-й версии Flash для работы с id появилось новое свойство XML: объект idMap и, в отличие от 7-й версии, все id-ссылки теперь располагаются в нем.

Код, приведенный выше, в 8-й версии Flash должен быть таким:

01 
02 
03 
04 
var map = _root.my_xml.idMap;
for (i=1; i<6; i++) {
	trace(i+" - "+map[i].attributes.name);
}

				

Причем следует заметить, что, видимо, по ошибке свойство idMap не было добавлено в intrinsic-класс, поэтому если переменная, ссылающаяся на объект XML, типизирована, то обращение к свойству idMap вызовет ошибку компилятора:

01 
02 
var my_xml:XML = new XML()
var map:Object = my_xml.idMap;

				

Сообщение об ошибке:

**Error** Scene=Scene 1, layer=Layer 1, frame=1:Line 2: There is no property with the name 'idMap'.
var map:Object = my_xml.idMap;

				

Эту проблему легко обойти:

01 
02 
var my_xml:XML = new XML()
var map:Object = my_xml["idMap"];

				

Другой, более корректный способ — указание публичной переменной в подклассе XML, если вы такой делаете.

Ни в коем случае не рекомендую изменять intrinsic-класс. Это хоть и избавит вас от проблемы, но может доставить много хлопот другим разработчикам, которым придется работать с вашим кодом.

Доступ к данным 

Для полноценного доступа к информации, находящейся в объекте XML, класс XMLNode содержит следующие объекты, свойства и методы:

XMLNode.nodeName
XMLNode.nodeType
XMLNode.attributes
XMLNode.nodeValue
XMLNode.hasChildNodes
XMLNode.toString()

				

XMLNode.nodeName 

Свойство. Строковый тип, представляющий имя узла XML.

Например, узел

<item name="name" />
				

будет иметь значение свойства nodeName, равное item.

После стандартного парсинга nodeName будет только у узлов первого типа. Текстовые узлы имеют значение этого свойства, равное null.

Данное свойство может программно изменяться.

Flash-плеер не ограничивает возможность задания аналогичного свойства текстовым узлам:

01 
02 
03 
my_xml = new XML("hello")
my_xml.firstChild.nodeName = "item"
trace(my_xml.firstChild.nodeName);

				

При тестировании в окне Output будет выведена строка item — говорит о том, что присвоение прошло успешно.

Таким образом, нет гарантии того, что узел, имеющий непустое значение свойства nodeName, является узлом первого типа.

Поскольку корневой узел XML, являясь узлом первого типа, имеет значение этого свойства равным null, то нельзя однозначно утверждать, что узел, имеющий значение nodeName, равное null, является текстовым узлом:

01 
02 
my_xml = new XML("hello")
trace(my_xml.nodeName)

				

Для корректного определения типа узла используется свойство nodeType.

XMLNode.nodeType
				

Свойство. Может иметь значение 1 или 3. Характеризует тип узла, где первый тип — это узел XML, второй тип — текстовый узел.

Использование:

type_of_current_node = node_xml.nodeType
				

Изменить это свойство невозможно. Только для чтения.

XMLNode.attributes 

Свойство. Ссылка на объект, содержащий атрибуты узла первого типа. Атрибут может быть один, их может быть несколько или не быть вовсе. Количество атрибутов и объем данных, которые будут содержать атрибуты, никак не ограничен. Однако на практике рекомендуется не перегружать этот объект данными.

Любые данные в атрибутах узла непосредственно после парсинга имеют строковый тип.

Объект attributes узла

<item name="name" text="any text" num="545" />
				

будет представлять собой объект вида

{name:"name", text:"any text", num:"545"}
				

Хозяйке на заметку

Кажущаяся похожесть attributes на обычный объект класса Object обманчива.

Это весьма странный объект, поскольку он не является потомком класса Object (!) и не наследует методов этого класса. Именно поэтому, например, создать пользовательское свойство в объекте attributes напрямую нельзя, поскольку метода addProperty у объекта attributes не существует.

Для понимания того, что собой представляет attributes, определим его как объект аналогичный объектам класса Object, но лишенный конструктора класса и ссылки на прототип класса.

Чтобы наглядно увидеть разницу между attributes и обычным объектом, можно провести следующий эксперимент:

my_xml = new XML("<item1 text='text1' /><item2 />")
my_xml.firstChild.nextSibling.attributes = {text:"text2"}
trace(my_xml);

В окне Output будет выведено сообщение:

<item1 text="text1" /><item2 text="text2" __proto__="[object Object]" constructor="[type Function]" />

Такая реализация attributes, с моей точки зрения, направлена на снижение потребления машинных ресурсов объектами XML.


Однако атрибутам можно присваивать данные любого типа, как и любой другой переменной

01 
02 
03 
04 
my_xml = new XML("<item age='55'>")
trace(typeof my_xml.firstChild.attributes.age)
my_xml.firstChild.attributes.age = parseInt(my_xml.firstChild.attributes.age)
trace(typeof my_xml.firstChild.attributes.age)

				

При тестировании получим в окне Output:

string
number

				

Начальный строковый тип данных атрибута age успешно преобразован в цифровой тип.

XMLNode.nodeValue 

Свойство текстовых узлов. Возвращает декодированное значение узла.

На практике довольно часто, например, при необходимости задать некому текстовому полю текст из текстового узла, присваивают непосредственно узел:

my_txt.text = my_xml.firstChild.firstChild
				

Плеер в таком случае производит преобразование типов: узел XML преобразуется в строку командой toString. Но это далеко не всегда верно, что можно увидеть на примере:

01 
02 
03 
my_xml = new XML("<item>&lt;Hello world&gt;</item>")
trace(my_xml.firstChild.firstChild.toString())
trace(my_xml.firstChild.firstChild.nodeValue)

				

При тестировании в окне Output будет выведено сообщение:

&lt;Hello world&gt;
<Hello world> 

				

nodeValue и toString не всегда возвращают одинаковое значение. Преобразование, используемое в nodeValue снимает необходимость декодирования символов подстановки. Более того, за исключением пользовательских методов, это единственный путь декодирования символов подстановки в текстах, и его можно применять для обслуживания других объектов.

В файлах справки указывается, что nodeValue — это свойство «только для чтения» и что задать это свойство узлам первого типа нельзя:

"If the XML object is an XML element (nodeType is 1), nodeValue is null and read-only."
				

Но хотя Flash-плеер и не создает узлам первого типа свойство nodeValue, имеющее собственные getter- и setter-функции, не существует запрета на создание одноименной переменной в этом узле:

01 
02 
03 
my_xml = new XML("<hello />");
my_xml.firstChild.nodeValue = "&lt;500&gt;";
trace(my_xml.firstChild.nodeValue); 

				

В Output будет выведена строка <500>.

Присвоенные таким образом узлам первого типа значения nodeValue не отображаются в листинге переменных или при преобразовании toString.

В результате возможна ситуация, которая начинающего может поставить в тупик:

01 
02 
03 
my_xml = new XML(" <item />Hello world!")
my_xml.firstChild.nextSibling.nodeValue = "Goodbye world!"
trace(my_xml.firstChild.nextSibling.nodeValue)

				

Команда trace будет выдавать новое значение nodeValue:

Но в листинге переменных и при отправке данных на сервер будет сохраняться старое значение:

Все это происходит по причине одного незамеченного пробела перед символом < и возможности создания переменной nodeValue в узлах первого типа.

XMLNode.hasChildNodes 

Свойство. Boolean. Только для чтения. В плеере реализовано примерно следующим образом:

01 
02 
03 
XMLNode.prototype.addProperty("hasChildNodes", function (){
return this.firstChild != undefined
}, null)

				

Поскольку свойство firstChild нельзя изменить пользовательскими методами, то такая конструкция вполне корректна.

XMLNode.toString() 

Метод. Возвращает текстовое представление XML-объекта.

Любые операции с XML-объектами, где требуется преобразование их типа в строковый, используют этот метод.

Во Flash преобразование типов автоматическое. Даже нововведения в ActionScript 2 незначительно изменили эту ситуацию:

01 
02 
03 
my_xml = new XML("<item>")
my_str="text"+my_xml 
trace(my_str);

				

В итоге, методы преобразования типов оказываются в числе самых часто вызываемых методов во Flash.

Важно!

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

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

Замена метода преобразования типов может привести к существенному снижению производительности ролика, при том, что никаких видимых причин этого не будет.

Стоит прислушаться к рекомендациям не заменять встроенные методы преобразования типов на пользовательские и вообще подходить к замене встроенных методов очень аккуратно и только в крайних случаях: это нерекомендуемая практика.


Разделение именных пространств 

// TODO
				

XMLNode.namespaceURI 

Свойство. Возвращает URI (Uniform Resource Identifiers) узла XML. Появилось в 7-й версии Flash. Недокументировано (задокументировано в 8-й версии).

XMLNode.prefix, XMLNode.localName 

В 7-й версии Flash появилась недокументированная возможность разделения имени узла nodeName на префикс и локальное имя с помощью разделителя двоеточие.

В 8-й версии эти свойства уже задокументированы.

Свойство prefix. Возвращает префикс имени узла XML, если имя узла содержит разделитель двоеточие.

Пример:

01 
02 
my_xml = new XML("<prefix_value:local_name_value />");
trace(my_xml.firstChild.prefix); // prefix_value

				

Если имя узла не содержит разделителя двоеточие, возвращается пустая строка.

Свойство localName возвращает локальное имя узла XML, если имя узла содержит разделитель двоеточие.

Пример:

01 
02 
my_xml = new XML("<prefix_value:local_name_value />");
trace(my_xml.firstChild.localName); // local_name_value

				

Если имя узла не содержит разделитель Разделение именных пространств, возвращается имя узла.

XMLNode.getPrefixForNamespace(), XMLNode.getNamespaceForPrefix() 

// TODO
				
01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
str= "<document>"
str += "<bk:book xmlns:bk='urn:samples'>";
str += "<bk:ISBN>1-861001-57-5</bk:ISBN>";
str += "<title>Pride And Prejudice</title>";
str += "</bk:book>";
str+= "</document>"
foo = new XML(str);
delete str;
var node = foo.firstChild.firstChild
trace(node.prefix);
trace(node.localName);
trace(node.namespaceURI);
trace(node.getPrefixForNamespace("urn:samples"));
trace(node.getNamespaceForPrefix("bk"));

				

Изменение структуры данных 

Для изменения структуры дерева XML используются следующие методы:

XMLNode.appendChild()
XMLNode.insertBefore()
XMLNode.cloneNode()
XMLNode.removeNode()

				

Важно!

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

Методы appendChild, insertBefore, removeNode ничего не возвращают, что крайне неудобно.

Метод appendChild позволяет добавлять дочерние узлы текстовым узлам, что противоречит не только спецификации XML, но и здравому смыслу. Убедитесь сами:

my_xml = new XML("Hello world!"); node = my_xml.firstChild
node.appendChild(new XMLNode(3, "Goodbye world!"))
trace(node.lastChild)
node=node.lastChild
node.appendChild(new XMLNode(3, "Hello again!"))
trace(node.lastChild)
delete node
trace("---");
trace(my_xml);

В этом примере дочерние узлы успешно создаются, но метод toString их не отображает, и в листинге переменных вы их не увидите, что также добавляет пикантности ситуации.

Метод insertBefore требует избыточных данных: второй аргумент этого метода явно лишний, и указание родительского объекта в качестве this не отвечает сущности этого метода.

Если мне приходится работать с изменением структуры XML-дерева, я обязательно создаю собственные методы и пользуюсь ими.

В дальнейшем мы рассмотрим варианты реализации пользовательских методов, предназначенных для изменения структуры XML-дерева.


XMLNode.appendChild() 

Метод. Перемещает узел XML из объекта, в котором он находится, в узел XML, который вызвал этот метод. Перемещенный узел помещается последним в массив дочерних узлов, т.е.  вставленный узел будет всегда узлом lastChild текущего узла. Этот метод ничего не возвращает.

Синтаксис:

my_xml.appendChild(node_xml)
				

где, my_xml  — любой XML узел (не рекомендуется применять к текстовым узлам)

node_xml  — вставляемый узел, объект класса XMLNode.

XMLNode.insertBefore() 

Метод. Перемещает узел XML из объекта, в котором он находится, в узел XML, который вызвал этот метод. Перемещенный узел помещается перед узлом, указанном во втором аргументе. Этот метод ничего не возвращает.

Синтаксис:

my_xml.insertBefore(node_xml, before_node_xml)
				

где: my_xml — любой XML узел, содержащий дочерние узлы;

node_xml — вставляемый узел, объект класса XMLNode;

before_node_xml — узел, перед которым следует вставить node_xml.

XMLNode.cloneNode() 

Метод. Создает и возвращает копию текущего узла. Однако следует учесть, что это не точная копия: новый узел не имеет ссылок на родительские и братские узлы, а также может не иметь дочерних узлов, если не передать true в качестве аргумента.

Синтаксис:

new_node_xml = my_xml.cloneNode(deep)
				

где new_node_xml — переменная, в которую будет помещена ссылка на новый узел;

my_xml — любой узел XML;

deep — аргумент, указывающий на необходимость дублирования дочерних узлов.

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

XMLNode.removeNode() 

Метод. Удаляет узел и его дочерние узлы из дерева XML. Не возвращает ничего.

Синтаксис:

my_xml.removeNode()
				

где my_xml — любой узел XML в дереве, за исключением корневого.

Унаследованные свойства и методы 

Как и другие встроенные классы, классы XML и XMLNode можно расширять, добавляя собственную функциональность. Для этих целей используются свойства и методы, унаследованные от класса Object.

Рассмотрим детальнее, что собой представляет конструктор класса Object, его prototype и какое влияние это оказывает на объекты — наследники этого класса,  в первую очередь, на объекты классов XML и XMLNode.

Класс Object 

Для того чтобы посмотреть содержимое конструктора класса Object и его prototype, будем использовать недокументированную функцию ASSetPropFlags.

Хозяйке на заметку

Если вы раньше никогда не сталкивались с недокументированной функцией ASSetPropFlags, то предлагаю провести небольшой эксперимент.

Поместите этот скрипт в первом кадре нового FLA и в режиме тестирования загляните в листинг переменных. Вы будете удивлены.

ASSetPropFlags(_global, null, 6, 1); for (i in _global) {
  ASSetPropFlags(_global[i], null, 6, 1);
  for (j in _global[i]) {
    ASSetPropFlags(_global[i][j], null, 6, 1);
    for (k in _global[i][j]) {
      ASSetPropFlags(_global[i][j][k], null, 6, 1);
    }
  }
}

Ранее скрытые объекты и их содержимое станут видимы.

ASSetPropFlags управляет не только видимостью объектов, но также возможностью их удаления или перезаписи.

Подробности можно узнать из статей:


Если обойти сам Object и его экземпляр циклом for..in.., предварительно открыв для такого обхода Object и Object.prototype с помощью ASSetPropFlags:

01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
ASSetPropFlags(_global, "Object", 6, 1);
ASSetPropFlags(Object, null, 6, 1);
trace("\tObject:");
for (i in Object) {
	trace(typeof Object[i]+":\t"+i);
}
trace("\n\tObject.prototype:");
ASSetPropFlags(Object.prototype, null, 6, 1);
for (i in Object.prototype) {
	trace(typeof Object.prototype[i]+":\t"+i);
}
delete i;

				

В окне Output при тестировании получим следующий результат:

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

Object.registerClass — статический метод функции Object.

Ссылка на registerClass просто помещена в функцию Object, конструкторы классов XML и XMLNode не имеют аналогичной функции.

Важно!

Может ли функция помимо собственно тела содержать в себе еще что-нибудь?

Это, безусловно, возможно и широко используется. Более того, каждая функция всегда имеет ссылки на другие объекты.

Любая функция — это объект класса Function, в свою очередь являющегося наследником класса Object. Таким образом, можно сказать, что любая функция — это всегда объект класса Object, содержание и поведение которого расширено классом Function.


Object.prototype — поскольку Object является функцией, то, как и любая функция, он имеет ссылку на собственный объект prototype. Конструкторы классов XML и XMLNode также являются функциями и имеют аналогичные объекты.

Object.constructor — ссылка на конструктор объекта. Поскольку Object является функцией, его конструктором является Function.

Проверим в новом FLA:

trace(Object.constructor === Function) // true
				

Object.__proto__ — ссылка на объект prototype конструктора класса. Поскольку у Object конструктор класса Function, то его __proto__ ссылается на Function.prototype.

Проверим в новом FLA:

trace(Object.__proto__ === Function.prototype) // true
				

Вторая часть  — объекты содержащиеся в объекте prototype класса Object.

Если заглянуть в листинг переменных после тестирования скрипта обхода циклом for..in.. прототипа Object, то обнаружим:

В листинге класс Object стал видимым, но внутри Object отобразился только объект prototype. Метод registerClass и объекты __proto__ и constructor не отобразились по причине того, что процедура, которая выводит данные в окно Output, их игнорирует. Обратите внимание на то, что constructor, находящийся непосредственно в Object, и constructor, находящийся внутри Object.prototype, — это разные вещи.

То, что находится вне объекта prototype, наследоваться дочерними классами не будет. Все находящееся внутри prototype может быть унаследовано другими классами, в том числе и классами XML и XMLNode.

Важно!

Следует учесть, что наследуется не все. Например, функция toString у объектов классов XML и XMLNode другая, нежели у Object.

Ранее, в разделе этой книги «Объект prototype, некоторые принципы наследования» рассказывалось, как и почему это происходит.


Наследуемые свойства и методы класса Object 

Практически все объекты во Flash являются наследниками класса Object. Классы XML и XMLNode не исключение. Поэтому любой объект класса XML или XMLNode являясь наследником Object имеет доступ ко всем свойствам и методам, заданным в Object.prototype, при условии, что эти свойства или методы не перекрыты их собственными одноименными свойствами или методами.

Используя простой обход Object.prototype, можно увидеть все унаследованные свойства и узнать, действительно ли эти свойства являются унаследованными.

01 
02 
03 
04 
ASSetPropFlags(Object.prototype, null, 6, 1);
for (i in Object.prototype) {
	trace((Object.prototype[i] == XMLNode.prototype[i])+"\t"+i);
}

				

Результат:

Сравнивая одноименные ссылки в прототипе Object и XMLNode, видим, что только toString и конструктор класса constructor разные, все остальное успешно наследуется.

Object.prototype.toLocaleString() 

Метод. Возвращает результат, аналогичный toString. В настоящий момент реализован примерно следующим образом:

01 
02 
03 
Object.prototype.toLocaleString = function () {
return this.toString()
}

				

Метод не документирован, я думаю, он включен «на будущее» для таких случаев, как, например, преобразование объекта Date в строку, использующую названия месяцев и дней недели на языке пользователя.

Object.prototype.isPropertyEnumerable() 

Метод. Возвращает булево значение, характеризующее видимость свойства при обходе объекта циклом for..in.. .

Пример:

01 
02 
03 
trace(Object.prototype.isPropertyEnumerable("toString")) // false
ASSetPropFlags(Object.prototype, "toString", 6, 1)
trace(Object.prototype.isPropertyEnumerable("toString")) // true

				

Метод не документирован.

Object.prototype.isPrototypeOf() 

Метод. Возвращает булево значение, которое показывает, присутствует ли объект prototype какого-либо класса в иерархии классов объекта.

Пример:

01 
02 
03 
04 
05 
06 
07 
my_xml = new XML("<node />")
trace(Object.prototype.isPrototypeOf(my_xml)) // true
trace(Object.prototype.isPrototypeOf(my_xml.firstChild)) // true
trace(XMLNode.prototype.isPrototypeOf(my_xml)) // true
trace(XMLNode.prototype.isPrototypeOf(my_xml.firstChild)) // true
trace(XML.prototype.isPrototypeOf(my_xml)) // true
trace(XML.prototype.isPrototypeOf(my_xml.firstChild)) // false

				

Из примера видно, что в цепочке наследования my_xml встречаются прототипы классов XML, XMLNode и Object, в то время как my_xml.firstChild не имеет в этой цепочке прототипа класса XML.

Метод не документирован.

Object.prototype.hasOwnProperty() 

Метод. Возвращает булево значение, которое показывает, принадлежит ли свойство непосредственно объекту или оно унаследовано от родительских классов.

Пример:

01 
02 
03 
04 
05 
06 
XML.prototype.readyForUse = true;
first_xml = new XML();
first_xml.readyForUse = true 
trace(first_xml.hasOwnProperty("readyForUse")); // true
second_xml = new XML();
trace(second_xml.hasOwnProperty("readyForUse")); // false

				

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

Метод не документирован.

Object.prototype.toString() 

Метод. Классы XML и XMLNode его не наследуют, поскольку имеют собственный одноименный метод, описанный выше.

Object.prototype.valueOf() 

Метод. Возвращает базовое значение объекта: если такового нет, то сам объект.

01 
02 
03 
04 
05 
bla = new String("hello world");
trace(bla == bla.valueOf()); // true
trace(bla === bla.valueOf()); // false
trace(typeof bla); // object
trace(typeof bla.valueOf()); // string

				

Базовое значение — это всегда значение простого типа: string или number. Все объекты во Flash можно разделить на имеющие и не имеющие базовое значение. Объекты, имеющие базовое значение, используют его для расчетов всех своих остальных свойств.

Например, метод getDay в этом выражении

trace(new Date().getDay())
				

возвращает не сохраненное ранее значение дня недели, а вычисляет его, основываясь на базовом значении, которое у объекта Date можно получить методом getTime.

Экземпляры классов XML и XMLNode не имеют базового значения, и при вызове ими метода valueOf возвращаться будут вызывающие экземпляры.

Object.prototype.addProperty 

Метод. Создает пользовательское вычисляемое свойство. Синтаксис:

my_object.addProperty("property_name", get_function, set_function)
				

где property_name — строка, имя создаваемого свойства;

get_function — функция, возвращающая значение свойства;

set_function — функция, изменяющая значение свойства.

Этот метод появился в 6й версии Flash и существенно облегчил программирование на ActionScript. В дальнейшем мы будем широко применять создание пользовательских свойств и более подробно изучим их особенности.

Object.prototype.watch, Object.prototype.unwatch 

Методы для организации слежения за изменением значения переменных. Object.watch устанавливает слежение, Object.unwatch снимает слежение. В подавляющем большинстве случаев предпочтительнее создавать пользовательские свойства вместо наблюдения за значением переменной.

Object.prototype.constructor 

Ссылка на конструктор класса. Классы XML и XMLNode его не наследуют, поскольку имеют собственные. Обратите внимание, что ссылка на constructor выглядит как

test_xml = new XML("<node/>")
trace(test_xml.constructor === test_xml.firstChild.constructor);

				

Надеюсь, что продолжение следует.

Автор с удовольствием ответит на вопросы, связанные с этой книгой, в рассылке ruFlash.