|
Ранее уже отмечалось, что XML-документы с точки
зрения
Флэнга
- обычные термы,
формируемые с помощью предопределенного набора структур.
В главке "XML-документы"
термы во
Флэнг-программах
представлялись в стандартном XML-синтаксисе. Однако это всего лишь
синтаксический "сахар", позволяющий быстро писать выразительные и
компактные программы.
Во многих ситуациях удобнее использовать непосредственно термальное
представление
XML-выражений. В соответствии с типами вершин, используемыми в XML, во
Флэнге для этого имеется семь структур:
xElem/4
- 4-х аргументная структура, определяющая
XML-элемент
xDoc/1
- структура, определяющая внешнюю вершину
XML-документа
xEntityRef/1
- структура, определяющая сущности XML
xCDATA/1
- структура, определяющая необрабатываемые данные.
Это структура позволяет явно использовать в тексте ключевые символы
XML, например, '<'
xComments/1
- структура, определяющая вершину комментариев
xPI/3
- структура для определения XML-инструкций
xNS/2
- структура, задающая пространства имен
Рассмотрим использование этих структур более подробно.
xElem/4
Основной из предопределенных структур, с помощью
которых
строятся XML-документы,
является структура xElem/4:
?- xElem("hello-elem", newHash(["id"/"hello"]), ["Hello, everybody!"], _);
<hello-elem id="hello">Hello, everybody!</hello-elem>
В этом примере XML-документ вводится во Флэнг-систему
в термальном
виде.
Флэнг-система возвращает саму эту же структуру, используя по умолчанию
стандартный
XML-формат. Структура xElem имеет 4 аргумента:
xElem(TagName, TableOfAttrs, Contents, Namespaces)
Чтобы данная структура корректно представляла
XML-документ, каждый из
ее аргументов
должен сыграть определенную роль:
TagName
- первый аргумент содержит имя элемента,
представленное в виде строки или атома
TableOfAttrs
- этот аргумент определяет
атрибуты XML-элемента. Атрибуты представляются в виде хэш-таблицы.
Например, чтобы породить множество атрибутов для элемента
<ha a="1" b="ququ"/>
нужно определить хэш-таблицу:
newHash(["a"/1, "b"/"ququ"])
Вместо хэш-таблицы в качестве второго аргумента
допустима и свободная
переменная,
которая воспринимается Флэнгом как пустое множество атрибутов.
Contents
- содержимое элемента. Должно быть списком, либо
вектором
XML-термов.
Допускаются вложенные списки и векторы, а также их смешение. Флэнг при
"разворачивании" содержимого элемента автоматически использует
перечисление типа
eFlat/1.
Пример:
?- vec:=newVector(["2 ", ["3 "]]);
true
?- ^vec;
("2 ", ["3 "])
?- xElem("digits", FreeVar, ["1 ", [^vec], [["4 "]]], _);
<digits>1 2 3 4 </digits>
Пустой список в качестве третьего аргумента
соответствует пустому
XML-элементу
?- xElem("empty-elem", _, [], _);
<empty-elem/>
Namespaces
- Этот аргумент содержит информацию о
пространствах имен. Свободная переменная в качестве пространства имен
означает пространство имен по умолчанию. Пространству имен - очень
важному механизму
организации XML-документов - будет посвящена одна из следующих главок.
Приведем простые примеры использования структуры xElem.
Вложенность
элементов в XML-документе описывается через вложенность термов:
?- xElem("a", _, [xElem("b", _, [], _)], _);
<a><b/></a>
Манипулировать XML-документами можно опосредованно с
помощью функций:
?- makeElem(Attr, Conts) :- xElem("elem", newHash(["id"/Attr]), [Conts], _);
?- makeElem("elem1", makeElem("subelem", makeElem("subsubelem", [])));
<elem id="elem1"><elem id="subelem"><elem id="subsubelem"/> </elem></elem>
Основным методом работы с XML-документами является
рекурсия по глубине
вложенности элементов. Рассмотрим пример:
функция countElem(XMLDoc) подсчитывает число элементов в
документе XMLDoc:
countElem(xElem(_, _, Conts, _)) :- 1 + countConts(eFlat(Conts)) ; countElem(_) :- 0;
countConts(Enum) :- exhausted(Enum), 0 ; countConts(Enum) :- N = countElem(eValue(Enum)), N + countConts(eNext(Enum)) ;
В определении этой функции используются стандартные
рекурсивные приемы,
которые позволяют
обходить XML-дерево - элемент за элементом - и проводить нужные
преобразования. Рекурсия
получается "двухуровневая". Функция countElem работает на
уровне
XML-элементов, а функция countConts обеспечивает цикл по
элементам
содержимого XML-элементов. Обратите внимание на использование
перечисления eFlat, которое позволяет абстрагироваться от
конкретного формата представления
содержимого элементов.
?- xdoc() :- <a b="1"><c><a b="5"/>a</c></a>;
?- countElem(xdoc());
3
Функция countAttr(XML, AttrName),
определенная ниже,
подсчитывает в документе XML число элементов, содержащих
атрибут с именам AttrName.
countAttr(xElem(_, A, Conts, _), AttrName) :- hasKey(A, AttrName), 1 + countContents(eFlat(Conts), AttrName) ; countAttr(xElem(_, A, Conts, _), AttrName) :- countContents(eFlat(Conts), AttrName) ; countAttr(_, _) :- 0 ; countContents(Enum, _) :- exhausted(Enum), 0 ; countContents(Enum, AttrName) :- N = countAttr(eValue(Enum), AttrName), N + countContents(eNext(Enum), AttrName) ;
Данная программа организована аналогично предыдущей -
на основе
двухуровневой
рекурсии по элементам и содержимому элементов. Пример использования
функции
?- countAttr(xdoc(), "b");
2
Приведем еще один пример программы, которая
преобразует XML-документ,
заменяя
имена тегов. Функция replaceElem(XDoc, OldName, NewName)
переименовывет
элементы XDoc с именем OldName на NewName
replaceElem(xElem(Old, A, Conts, NS), Old, New) :- xElem(New, A, replaceConts(eFlat(Conts), Old, New), NS) ; replaceElem(xElem(Other, A, Conts, NS), Old, New) :- xElem(Other, A, replaceConts(eFlat(Conts), Old, New), NS) ; replaceElem(Other, _, _) :- Other;
replaceConts(Enum, Old, New) :- exhausted(Enum), [] ; replaceConts(Enum, Old, New) :- Result = replaceElem(eValue(Enum), Old, New), [Result | replaceConts(eNext(Enum), Old, New)] ;
Пример вычисления:
?- replaceElem(xdoc(), "a", "hrrr");
<hrrr b="1"><c><hrrr b="5"/>a</c></hrrr>
Легко заметить, что все три приведенные выше программы
очень похожи
друг на друга
с точки зрения организации обхода по структуре XML-документа. Последняя
программа демонстрирует,
каким образом XML-документ может во Флэнге преобразовываться.
xDoc/1
Следующей по важности структурой является xDoc/1.
Эта структура
является внешней структурой XML-документа и (информация для тех, кто
работал с XML
на Java) аналогична элементам класса Document в библиотеке jdom. Класс
Document и
структура xDoc необходимы, поскольку на самом внешнем
уровне у xml-документа может быть
несколько узлов. Только один из этих узлов являятся XML-элементом,
остальные же могут быть
xml-инструкциями, ссылками на dtd-файлы как, например, в XML-документе
<?xml version="1.0" encoding="ISO-8859-1"?> <?translate russian encoding="win-1251"?> <a/>
Этот документ на внешнем уровне содержит три элемента
- две инструкции
и тег с именем a. Они и будут содержимым структуры xDoc/1,
которая имеет
следующий формат:
xDoc(Contents)
Формат аргумента Contents аналогичен
формату третьего
аргумента структуры xElem (списки или вектора).
Подчеркнем, что xDoc/1 - всегда
внешняя структура. В правильно построенном XML-терме аргумент Contents
содержать элементы xDoc/1 не
может, поскольку
это не соответствует стандартной структуре XML-документа.
Правила использования xDoc/1 во
Флэнге
следующие:
- При чтении XML-файлов с помощью
функции
readXML
документы всегда
обрамляются структурой xDoc. Это надо учитывать при
доступе к элементам
XML-документа, загруженного с помощью этой функции.
- XML-термы, представленные во
Флэнг-программе в
стандартном
XML-синтаксисе,
транслируются Флэнг-системой в представление, не содержащее структуру
xDoc/1.
Например, тело правила
segm() :- <a b="1"><c/></a>;
при трансляции во внутреннее представление
Флэнг-системы переводится в
терм
xElem("a", newHash(["b"/1]), [xElem("b", newHash([]), [], _)], _)
Таким образом, при формировании этого XML-терма
используется только
структура xElem/4.
- При записи XML-терма в XML-файл,
например, с помощью
функции
writeXML,
обрамлять этот терм структурой xDoc/1
не обязательно. Если xDoc явно отсутствует, то
Флэнг-система добавит его
автоматически.
Если вернуться к определению функции countElem/1,
то
следует обратить
внимание на то, что данное определение не будет работать на XML-термах,
загруженных
из XML-файлов, поскольку в программе не учтен случай, когда внешним
термом является xDoc. Имеются несколько возможностей для
учета этой
ситуации.
Во-первых мы можем явно добавить в определение функции countElem/1
правило для xDoc:
countElem(xDoc(Conts)) :- countConts(eFlat(Conts));
Однако этот способ, вероятно, не самый удачный,
поскольку мы знаем, что
в XML-документе
структура xDoc/1 может встречаться только один раз, и то
на внешнем уровне.
Поэтому только что определенное правило будет проверять заведомо
неподходящие
вершины, что неэффективно. Более подходящим является вариант
определения отдельной
внешней функции, запускающей процесс вычисления, например:
count(xDoc(Conts)) :- countConts(eFlat(Conts)); count(XML) :- countElem(XML);
xEntityRef/1
Эта одноместная структура задает еще одну важнейшую
конструкцию XML -
сущности (entities).
В частности, часто употребляемая сущность XML
<
обозначающая значок "меньше", используемый в XML для
определения тегов,
во Флэнге
определяется с помощью структуры
xEntityRef("lt")
При трансляции XML-термов Флэнга в файл, например с
помощью функции
writeXML/3, эта
структура транслируется в стандартное представление сущностей,
начинающееся с амперсанда
'&', и заканчивающееся точкой с запятой.
xCDATA/1
Обычный текст в XML-документе понимается как
размеченный текст. Это
значит, что парсер (транслятор)
ищет в нем вкрапления
тегов и других специальных элементов языка XML. В частности, значок
'<' парсером
однозначно понимается как начало тега XML-элемента. Чтобы использовать
этот и другие подобные
значки как обычные символы, необходимо прилагать специальные усилия,
например, определять
эти символы через сущности. Имеется и иной способ - запретить парсеру
обрабатывать некоторый
сегмент текста. Для этого служит блок CDATA. В синтаксисе XML такой
блок организуется следующим образом:
<![CDATA[ ...необрабатываемый текст... ]]>
Во Флэнге для выделения необрабатываемого текста
служит специальная
структура - xCDATA:
xCDATA("...необрабатываемый текст...")
Работа этой структуры демонстрируется в следующем
примере:
?- xElem(a, _, ["&"], _);
<a>&</a>
?- xElem(a, _, [xCDATA("&")], _);
<a><![CDATA[&]]></a>s
В первом запросе значок амперсанда транслируется
Флэнгом в
соответствующую сущность,
поскольку содержимое элемента (третий аргумент xElem)
понимается как размеченный
текст, поэтому вхождения в текст символов, являющихся специальными
символами XML, должны быть
обработаны. Во втором случае значок амперсанда остается неизменным.
xComments/1
Данная структура служит для включения в XML-термы
комментариев. В
стандартном синтаксисе
комментарии включаются в XML-документ в следующем формате:
<!-- ...комментарии... -->
Пример организации комментариев с помощью структуры xComments/1
во Флэнге:
?- xElem(a, _, [xComments("комментарии")], _);
<a><!--комментарии--></a>
Processing instruction
Элемент "инструкция по обработке" (processing
instruction) позволяет
включать в XML
информацию, предназначенную для методов, которые ориентированы на
обработку данного документа.
Любой xml-документ начинается с такой инструкции, информирующей о
версии XML и задающей
дополнительную информацию для обработчиков. Например, в следующей
преамбуле XML-документа
включена информация о системе кодировки символов:
<?xml version="1.0" encoding="ISO-8859-1"?>
Синтаксически инструкция начинается и заканчивается
знаком вопроса,
обрамленным
стандартными угловыми скобками. В приведенном примере имеем имя
инструкции (идентификатор xml), а также параметры
инструкции, которые заданы
в формате 'атрибут'='значение'. Такой формат параметров является
наиболее удобным, хотя
с точки зрения стандарта XML
совсем не обязательным. Например, возможен такой вариант:
<?translate russian encoding="ISO-8859-1"?>
Здесь параметр russian состоит из одного
слова.
Инструкции по обработке реализуются во Флэнге с помощью специальной
структуры xPI/3. Инструкция из последнего примера будет
представлена с помощью
данной структуры в следующем виде:
xPI("translate", newHash(["encoding"/"win-1251"]), "russian encoding='win-1251'")
Первый аргумент - имя (идентификатор) инструкции. В
данном случае это translate.
Во втором аргументе с помошью таблицы собраны параметры, имеющие формат
'атрибут'='значение'.
В третьем аргументе все параметры собраны в виде одной строки.
Поскольку параметры в формате
'атрибут'='значение' включены и в третий аргумент, информация во втором
аргументе структуры
является избыточной. Однако это весьма полезно на практике, поскольку
обеспечивает легкий
доступ к "хорошо представленным" параметрам. Для других параметров при
обработке инструкции
придется обрабатывать строку - третий аргумент.
xNS/2
С помощью двуместной структуры xNS/2
определяется пространство имен
документа. Стандартное использование этой структуры - в качестве
четвертого
аргумента структуры xElem/4. Формат у xNS
следующий:
xNS('пространство имен данного элемента', 'таблица доп. пространств имен')
Первый аргумент определяет пространство имен текущего
элемента. Во
втором
аргументе в виде таблицы перечисляются пространства имен, в которых
могут
определяться подъэлементы данного элемента.
Заметим, что если используется только пространство
имен
по
умолчанию, то применять структуру xNS/2 не обязательно.
Вместо этого в качестве
четвертого аргумента xElem/4 можно подставить свободную
переменную. Мы
этим довольно часто
пользовались выше.
Во Флэнге реализованы полноценные, и в то же время
прозрачные
и простые механизмы работы
с пространствами имен. Подробно эти механизмы описаны в главке
"Пространства имен и их поддержка во Флэнге".
|