XML - статьи

Семантика создания элементов


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

В XSLT 1.0 необходимо, чтобы дерево целиком было построено до того, как клюбой его части можно было обратиться с использованием выражения XPath. В XSLT 1.0 даже невозможно было получить доступ к построенному дереву, известному под названием фрагмент дерева результата, не используя функцию расширения node-set(), которая, несмотря на широкую распространенность, фактически не является частью стандарта. Это ограничение исчезло в XSLT 2.0, но до проекта мая 2003 года действовало другое ограничение, согласно которому XPath мог осуществлять операции только с деревьями, представляющими документ целиком (то есть дерево с узлом документа в качестве корня). Это было естественным следствием двухъязыковой модели, посредством которой инструкции XSLT могут вызывать выражения XPath, но не наоборот. Временное дерево всегда создается посредством объявления переменной, а к узлам в пределах дерева можно обратиться только с помощью выражения XPath, которое ссылается на эту переменную. Правила видимости переменных гарантируют, что дерево всегда будет полностью построено, прежде чем будет сделана ссылка на любой из его узлов. Это иллюстрируется листингом 3.2.

Листинг 3.2. Создание временного дерева в XSLT <xslt:variable name="tree"> <calendar> <month number="1" length="31"/> <month number="2" length="{if ($leap-year) then 29 else 28}"/> <month number="3" length="31"/> <month number="4" length="30"/> . . . <month number="12" length="31"/> </calendar> </xslt:variable> . . . <xslt:value-of select="sum($tree/calendar/month/@length)"/> . . .

Тот факт, что к деревьям нельзя получить доступ до завершения их создания, означает то, что можно описать создание дерева в XSLT с использованием нисходящей модели, в которой родительские узлы должны быть созданы перед своими дочерними узлами. Конечно, в функциональном языке фактический порядок выполнения не определен, так что это было бы просто способом описания эффекта языка и не обязательно описанием фактической работы реализации. Хотя на практике большинство XSLT 1.0 процессоров, вероятно, следовали этой модели весьма точно.

В отличие от этого конструктор элемента XQuery является простым выражением и может использоваться в любом месте, где могут встречаться другие виды выражений. Например, вполне правильно написать так: sum( <calendar> <month number="1" length="31"/> <month number="2" length="if ($leap-year) then 29 else 28"/> <month number="3" length="31"/> <month number="4" length="30"/> . . . <month number="12" length="31"/> </calendar> / month / @length )


Хотя отличие этого примера от предыдущего выглядит весьма незначительным, оно имеет большое значение для детальной семантики модели. XQuery рассматривает выражение, создающее узел атрибута или элемента, таким же образом, как илюбое другое выражение. Это означает, что семантика описана восходящим способом. Так же как в выражении x *(y-1) сначала выполняется вычитание, а затем умножение, так и в выражении <a>{$x+1}</a>, сначала выполняется сложение, затем создается текстовый узел, содержащий результат, а потом создается узел элемента в качестве родительского для этого текстового узла.

Это означает, что в XQuery возможно манипулировать частично построенными деревьями (деревьями, которые не имеют узела документа в качестве своего корневого узла). Например, выражение пути может ссылаться на узел атрибута, который не связан ни с одним элементом. Атрибут может быть присоединен к элементу «позже», как показано ниже: let $att1 := attribute code { "23" }, $att2 := attribute desc { "24" } return if ($condition) then <a> {$att1} </a> else <a> {$att2} </a>

Конечно, это также означает, что узел атрибута, представленный $att1 или $att2, можно добавить к нескольким различным элементам. Поскольку атрибут не может на практике иметь два родительских узла, то семантика требует создания идентичной копии каждый раз, когда атрибут присоединен к элементу. В формальной модели такое копирование осуществляется по всему дереву: каждый раз, когда дочерний узел добавляется к родительскому узлу, дочерний узел копируется. На практике процессоры XQuery обычно избегают этого копирования и в большинстве случаев будут использовать реализацию такой же нисходящей обработки, как и процессоры XSLT 1.0. Но для увеличения общности языка формальная модель в XQuery полностью отличается от модели XSLT.

Так как XQuery копирует поддеревья неявно, тогда, когда они добавляются к новому родительскому узлу, то не требуется явной инструкции для осуществления копирования. В отличие от этого XSLT 1.0 сначала создает дерево целиком, а затем позволяет, если требуется, копировать это дерево явно с использованием инструкции xslt:copy-of. В самом последнем проекте XSLT 2.0 модель обработки была изменена и стала очень близкой к модели XQuery. Создание дерева описано восходящим способом, и узлы доступны до того, как они будут присоединены к родительскому узлу. Большинство пользователей не заметит изменений, а различие затрагивает многие незначительные детали, такие как способ, которым новые узлы ратифицируются с помощью типов схемы, и способ работы пространства имен. Это также означает, что необходима некоторая осторожность при использовании абсолютных выражений пути. Например, если корнем дерева является элемент a с дочерним узлом b, то для того чтобы выбрать элементы b, следует написать /b, а не /a/b, как можно было бы ожидать. Синтаксис /a/b (который является сокращением для root(.)/child::a/child::b) работает только там, где элемент с именем a является дочерним корневого узла.


Содержание раздела