[ предыдущая страница ] [ следующая страница ] [ содержание ]

8. Интеграция с XML

8.1. Немного о структуре ХМL-документов

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

При этом особое внимание было уделено обработке данных формата XML. Очевидно, это связано с тем, что в последние несколько лет язык XML (eXtensible Markup Language - расширяемый язык разметки) претерпел очень бурное развитие и довольно быстро приобрел популярность в Web-приложениях. Многие предсказывают языку XML великолепное будущее вплоть до полного или частичного вытеснения старого доброго HTML.

Правда, на момент написания этих строк концепция XML еще не до конца сформировалась. Рабочие группы WWW-консорциума продолжают работу над спецификациями. Существует еще множество нерешенных проблем. Поддержка отображения XML-документов уже введена в браузеры Internet Explorer 5 и Netscape 6, однако она пребывает пока в весьма "сыром" состоянии. Например, XML-документах пока что не отображаются русские буквы, а при отсутствии связи с таблицей стилей Internet Explorer вообще не находит ничего лучшего, чем отобразить красиво подсвеченный исходный код документа.

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

8.1.1. Основные понятия XML

Прежде чем мы рассмотрим способы манипулирования этими объектами, давайте кратко вспомним, как строятся XML-документы. Правильно оформленный XML-документ должен состоять из одного и только одного корневого элемента, в который могут быть вложены другие элементы. Правила вложения должны быть описаны в соответствующем "определении типа документа" (Document Type Definition - DTD). Определение DTD может находиться во внешнем файле. Кроме того, в документе необходимо объявить версию языка XML, а если документ предназначен для непосредственного отображения на экране (в браузере), то еще связать его с таблицей стилей. Вот пример простейшего XML-документа:

<?xml version="1.0" ?>
<!DOCTYPE test [
   <!ELEMENT test (#PCDATA|kind)*>
   <!ELEMENT kind (#PCDATA)>
   <!ATTLIST test id ID #REQUIRED>
]>
<?xml-stylesheet href="testxml.css" type="text/css"?>

<test id="rootobj">
This is XML document...
<kind>this is inner element...</kind>
...
<kind>this is one more inner element...</kind>
</test>

При наличии соответствующей таблицы стилей, в которой определены стили отображения элементов <test> и <kind>, браузер сможет отобразить этот документ, например так, как показано на рис. 8.1.


Рис. 8.1. Пример простейшего XML-документа

Различают так называемые "правильные" (valid) документы XML и "хорошо оформленные" (well-formed). Эти последние не обязаны иметь определение типа документа (тег <!DOCTYPE>), а также объявление версии XML. Поэтому с точки зрения структуры XML-документа значение имеет только корневой элемент (в приведенном примере это <test>) и вложенные в него элементы. (Элементом мы здесь называем фрагмент, ограниченный парой из открывающего и закрывающего тегов. Напомним, что закрывающие тети в XML обязательны.)

Каждый элемент XML-документа представляет собой узел (node) структурного дерева этого документа. Узел, вложенный в какой-либо элемент, является дочерним по отношению к этому элементу. Например, в приведенном примере элемент <kind> - дочерний узел по отношению к элементу <test>. Весь документ также является узлом (вершиной иерархии), имеющим единственный дочерний узел - корневой элемент.

Текст, содержащийся внутри элемента (ограниченный парой тегов), также является узлом иерархической структуры XML-документа. Узлы, являющиеся дочерними по отношению к одному и тому же элементу, называют братскими или родственными (sibling).

Поскольку эта книга не посвящена XML, мы здесь не будем подробно рассматривать принципы построения XML-документов. Если вы желаете подробнее познакомиться с языком XML, можем посоветовать книгу Дж. Бумфрея "XML: новые перспективы WWW" (ДМК, 2000). Нам же сейчас важно обратить внимание на то, что:

В приведенном выше примере корневой элемент <test> является дочерним узлом по отношению к документу и родительским узлом по отношению к двум текстовым узлам и двум узлам-элементам. Схематично это можно изобразить так, как показано на рис. 8.2.


Рис. 8.2. Структура связей элементов XML-документа

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

<smalldoc>
</smalldoc>

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

<auth>
<user>Ivanov</user>
<password>qT67$&89SiityqPPd</password>
</auth>

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

8.2. Представление элементов XML во Flash

Теперь давайте поговорим о том, в каком виде элементы XML представлены в программе Flash. Для такого представления существует специальный предопределенный объект, который так и называется: XML. Создается этот объект так же, как и большинство других, с помощью конструктора new:

a = new XML();

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

а = new XML("<mydoc><director>Fockin</director><file>wir.sdw
   </file><file>asdf.sdw</file></mydoc>");

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

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

а = new XML();
a.load ("http://www.myserver.com/mydoc1.xml");

В документе Flash объект XML состоит из отдельных узлов, для каждого из которых определены такие свойства, как: firstChild (первый дочерний узел), lastChild (последний дочерний узел), nextSibling (следующий родственный элемент), и peviousSibling (предыдущий родственный элемент), а также parentNode (родительский узел). С помощью этих свойств можно получить доступ к любому узлу структуры XML-документа.

Кроме того, каждый узел имеет свойства nodeType и nodeValue. Значение первого из них равно XML-номеру типа соответствующего узла. Во Flash используются только два типа узлов: тип 1 - XML-элемент и тип 3 - текстовый узел. Если значение nodeType равно 3, то значением nodeValue является строка, содержащая текст данного узла, в противном же случае это значение равно null. Существует также свойство nodeName, которое возвращает имя элемента (если значение nodeType равно 1).

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

a = new XML("<mydoc>ABCDE<emptytag /></mydoc>");
b = a.toString();
aa = a.firstChild;
c = aa.nodeName;
d = aa.firstChild.nodeValue;
x = aa.firstChild;
e = x.nextSibling.nodeName;

(Для наглядности можно поставить в соответствие переменным b, c, d и e динамические текстовые поля.)

В первой строке мы создаем XML-объект, корневой элемент которого (<mydoc>) имеет два дочерних узла: текстовый (со значением ABCDE) и элемент <emptytag>. Напомним, что согласно синтаксису XML запись <emptytag /> эквивалентна записи <emptytag></emptytag>.

Следующие строки демонстрируют обращение к различным узлам XML-объекта. Переменной aa присваивается значение корневого элемента (единственного дочернего узла самого объекта). Его свойство nodeName имеет значение mydoc, которое и присваивается переменной c. Далее переменной d присваивается значение nodeValue первого дочернего узла элемента mydoc. Поскольку этот узел текстовый, то его свойство nodeValue возвращает текстовую строку. Сам этот узел мы ставим в соответствие переменной x. Поэтому при вычислении ее свойства nextSibling происходит обращение к следующему родственному узлу, то есть к следующему дочернему узлу элемента mydoc. Это элемент <emptytag>. Итак, после запуска ролика с приведенным выше сценарием, переменные получат следующие значения:

Variable _level0.b = "<mydoc>ABCDE<emptytag /></mydoc>"
Variable _level0.aa = [object #2]
Variable _level0.c = "mydoc"
Variable _level0.d = "ABCDE"
Variable _level0.x = [object #3]
Variable _level0.e = "emptytag"

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

x.nextSibling.attributes.id = 658;
f = a.toString();

то на выходе получим следующее:

Variable _level0.f = "<mydoc>ABCDE<emptytag id=\"658\"/></mydoc>"

Так мы программно установили атрибут id элемента <emptytag>! Обратите внимание, что сценарий работает корректно, несмотря на то что первоначально тег <emptytag> не имел соответствующего атрибута. Подобным же образом можно управлять свойствами xmlDecl и docTypeDecl, которые означают, соответственно, объявление версии XML и определение типа документа. Например, если в приведенный выше сценарий добавить в какое-нибудь место строку

a.xmlDecl = &quuot;<?xml version=\"1.0\"?>";

то XML-код на выходе будет таким:

<?xml version="1.0"?><mydoc>ABCDE<emptytag id="658" /></mydoc>

Необходимо отметить, что свойства xmtDecl и docTypeDecl просто вводят и выводят информацию, совершенно не заботясь о ее синтаксисе. Например, если бы мы написали какую-нибудь ерунду вроде

a.xmlDecl = 2;

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

Для того чтобы лучше понять строение XML-объекта внутри сценария ActionScript, полезно просто проанализировать листинг одного из таких объектов, несмотря на то что на первый взгляд он кажется пугающе громоздким. Вот как, например, выглядит листинг объекта из только что приведенного примера (обратите внимание на то, что в него уже включены добавленные детали: атрибут id у элемента <emptylag> и объявление версии XML):

Variable _level0.a = [object #1] {
   nodeType:1,
   nextSibling:null,
previousSibling:null,
parentNode:null,
firstChild:[obgect #2] {
   nodeType:1,
   nextSibling:null,
   previousSibling:null,
   parentNode:[object #1],
   firstChild:[object #3] {
      nodeType:3,
      nextSibling:[object #4] {
         nodeType:1,
         nextSibling:null,
         previousSibling:[object #3],
         parentNode:[object #2],
         firstChild:null,
         lastChild:null,
         childNodes:[object #5] [],
         attributes:[object #6] {
            id:658
         },
         nodeName:"emptytag",
         nodeValue:null
      },
      previousSibling:null,
      parentNode:[object #2],
      firstChild:null,
      lastChild:null,
      childNodes:[object #7] [],
      attributes:[object #8] {},
         nodeName:null,
         nodeValue:"ABCDE"
      },
      lastChild:[object #4],
      childNodes:[object #9] [
         0:[object #3],
         1:[object #4]
      ],
      attributes:[object #10] {},
      nodeName:"mydoc",
      nodeValue:null
   },
   lastChild:[object #2],
   childNodes:[object #11] [
      0:[object #2]
   ],
   attributes:[object #12] {},
   nodeName:null,
   nodeValue:null,
   xmlDecl:"<?xml version=\"l.0\"?>",
   docTypeDecl:undefined,
   status:0
}

В этом листинге почти все элементы нам уже знакомы, за исключением элементов chidNodes и status. Первый из них просто представляет собой список (массив) дочерних узлов элемента. А вот о втором следует сказать особо.

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

Например, если бы мы в исходном тексте вставили бы закрывающий тег </asdf> без соответствующего открывающего тега, то он бы вообще не был вставлен в объект. При этом свойству status было бы присвоено значение -10, что означает: "Обнаружен закрывающий тег без открывающего". Если же мы вставили бы тег и не закрыли его, программа тоже исправила бы эту ошибку, например заменила бы тег <asdf> на тег <asdf />. В нашем же примере исходный текст был написан корректно, поэтому значение свойства status равно 0. Для свойства status определены следующие значения.

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

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

8.3. Манипуляции с объектами XML

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

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

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

obj.createTextNode();

создаст пустой текстовый узел, а строки

txt = "let's create new text node";
obj = createTextNode(txt);

создают текстовый узел с текстом "let's create new text node" (при условии, что переменная obj ссылается на XML-объект). Правда, пока что этот текстовый узел "висит в воздухе" - он не имеет своего места в иерархической структуре XML-объекта. Для того чтобы он стал полноправным элементом структуры, используется метод appendChild. Он позволяет сделать новый узел дочерним по отношению к любому элементу XML-структуры. Например, дополним пример, приводившийся ранее:

а = new XML("<mydoc>ABCDE<emptytag /></mydoc>");
b = a.toString();
aa = a.firstChild;
c = aa.nodeName;
d = aa.firstChild.nodeValue;
x = aa.firstChild;
e = x.nextSibling.nodeName;
x.nextSibling.attributes.id = 658;
a.xmlDecl = "<?xml version=\"l.0\"?>";
newnode = a.createTextNode("new node");
aa.appendChild (newnode);
f = a.toString();

Вот какой код XML получим при этом на выходе:

<?xml version="1.0"?><mydoc>ABCDE<emptytag id="658" />new node</mydoc>

Как видите, чтобы применить метод appendChild, мы ввели ссылку на вновь созданный узел переменной newnode. Затем с помощью данного метода мы сделали этот узел дочерним узлом элемента <mydoc>, ссылка на который хранится в переменной aa.

Точно так же можно создавать не только текстовые узлы, но и XML-элементы. Для этого надо использовать метод createElement. Он действует так же, только в качестве его аргумента следует указывать название элемента. Например, если к сценарию предыдущего примера дописать еще такие строки:

morenode = a.createElement("director");
aa.appendChild (morenode);

то в выходной код будет добавлен еще один элемент <director>, являющийся дочерним узлом корневого элемента <mydoc>:

<?xml version="1.0"?><mydoc>ABCDE<emptytag id="658" />new node<director /></mydoc>

Можно поместить этот элемент и в другое место объекта. Например, его можно сделать дочерним узлом элемента <emptytag>. Для этого нужно только переписать последнюю строку:

x.nextSibling.appendChild (morenode);

Тогда на выходе XML-код будет следующим:

<?xml version="1.0"?><mydoc>ABCDE<emptytag id="658"><director/></emptytag>new node</mydoc>

Как видите, все довольно просто. Если по ошибке (или же с умыслом) попробовать несколько раз применить метод appendChild, имеющий в качестве аргумента ссылку на один и тот же узел, то он будет помещен в последнее из указанных мест. Например, в последовательности

x.nextSibling.appendChild (morenode);
aa.appendChild (morenode);

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

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

Для этого существует метод cloneNode. В качестве аргумента он принимает логическое значение (true или false). Если аргумент имеет значение true, то узел дублируется вместе со всеми дочерними узлами, а если аргумент равен false, то без них. Например, модифицируем еще раз наш сценарий, чтобы продублировать в нем элемент <emptytag>:

а = new XML("<mydoc>ABCDE<emptytag /></mydoc>");
b = a.toString();
aa = a.firstChild;
c = aa.nodeName;
d = aa.firstChild.nodeValue;
x = aa.firstChild;
e = x.nextSibling.nodeName;
x.nextSibling.attributes.id = 658;
a.xmlDecl = "<?xml version=\"1.0\"?>";
newnode = a.createTextNode("new node");
morenode = a.createElement("director");
aa.appendChild (newnode);
x.nextSibling.appendChild (morenode);
clone = x.nextSibling.cloneNode(false);
aa.appendChild (clone);
f = a.toString();

На выходе получим следующий XML-код:

<?xml version="1.0"?><mydoc>ABCDE<emptytag id="658"><director /></emptytag>new node<emptytag id="658" /></mydoc>

Как видите, мы продублировали элемент <emptytag>; и сделали дубликат еще одним дочерним узлом корневого элемента <mydoc>. При этом дочерний узел <director> не был продублирован, поскольку метод cloneNode получил аргумент false. Обратите внимание на то, что атрибут элемента продублировался, как ни в чем не бывало - аргумент true / false влияет только на дублирование дочерних узлов, но не атрибутов. Если же продублировать этот элемент с аргументом true

clone = x.nextSibling.cloneNode(true);

а все остальные строки сценария оставить без изменения, то дочерний узел <director> будет продублирован вместе со своим "родителем":

<?xml version="1.0"?><mydoc>ABCDE<emptytag id="658"><director /></empty tag>new node<emptytag id="658"><director /></emptytag></mydoc>

Во всех приведенных выше примерах, в которых мы использовали метод appendChild, новый узел добавлялся в конец списка дочерних узлов родительского элемента. А что делать, если мы хотим вставить новый узел не в конец, а в середину или в начало этого списка? Возвращаясь к нашему примеру - так продублировать элемент <emptytag>, чтобы его копия была помещена перед текстовым узлом new node, а не после него.

<?xml version="1.0"?><mydoc>ABCDE<emptytag id="658"><director /></empty tag><emptytag id="658"><director /></emptytag>new node</mydoc>

Для подобных случаев вместо метода appendChild следует использовать метод insertBefore. Этот метод вставляет новый узел в список дочерних узлов родительского элемента перед указанным узлом. Например, приведенный выше код XML-объекта также был сгенерирован программно:

clone = x.nextSibling.cloneNode(true);
aa.insertBefore (clone,newnode);

Поскольку в переменной newnode в нашем примере хранилась ссылка на текстовый узел new node, то новый узел был вставлен в список дочерних узлов элемента <mydoc> перед этим текстовым узлом. Помимо методов для добавления нового узла в XML-объект, существует и метод для их удаления - removeNode. В отличие от методов создания, он не требует аргументов и просто удаляет из XML-объекта указанный узел. Например, если написать

а = new XML("<mydoc>ABCDE<emptytag /></mydoc>");
aa = a.firstChild;
x = aa.firstChild;
x.nextSibling.removeNode ();

то на выходе получим такой код:

<mydoc>ABCDE</mydoc>

То есть, элемент <emptytag>, на который ссылается x.nextSibling, будет удален.

Иногда бывает полезно во избежание ошибок узнать, имеет ли данный элемент дочерние узлы. Для этого в языке ActionScript предусмотрен метод hasChildNodes, который возвращает значение true, если дочерние узлы есть, и false в противном случае. Вот типичный пример использования этого метода:

if (a.hasChildNodes()) aa = a.firstChild;

То есть, перед обращением к дочернему узлу через firstChild бывает нелишне убедиться, что таковой имеется (если, конечно, это заранее не известно).

Таким образом, ActionScript предоставляет нам достаточно средств для загрузки, создания и изменения XML-данных. Однако, до сих пор измененный ХМL-объект оставался жить исключительно внутри сценария. В следующем разделе мы поговорим о том, каким образом его можно использовать.

[ предыдущая страница ] [ следующая страница ] [ содержание ]
Hosted by uCoz