Элемент () против узла () в XQuery
Может кто-нибудь сказать мне точную разницу между node()
а также element()
типы в XQuery? В документации говорится, что element()
является узлом элемента, в то время как node()
это какой-то узел, поэтому если я правильно понимаю element()
это подмножество node()
,
Дело в том, что у меня есть такая функция XQuery:
declare function local:myFunction($arg1 as element()) as element() {
let $value := data($arg1/subelement)
etc...
};
Теперь я хочу вызвать функцию с параметром, который получается другой функцией, скажем, functionX
(который я не могу контролировать):
let $parameter := someNamespace:functionX()
return local:myFunction($parameter)
Проблема в, functionX
возвращает node()
так что это не позволит мне пройти $parameter
непосредственно. Я попытался изменить тип моей функции, чтобы взять node()
вместо element()
, но тогда я не могу прочитать какие-либо данные из него. $value
просто пусто
Есть ли какой-либо способ преобразования узла в элемент или я просто что-то упустил?
РЕДАКТИРОВАТЬ: Насколько я могу сказать, проблема в той части, где я пытаюсь получить субэлемент, используя $arg1/subelement
, Видимо, вы можете сделать это, если $arg1
является element()
но не если это node()
,
ОБНОВЛЕНИЕ: я протестировал пример, представленный Dimitre ниже, и он действительно работает отлично, как с Saxon, так и с eXist DB (именно это я использую в качестве движка XQuery). Проблема на самом деле возникает с request:get-data()
функция из БД eXist. Эта функция получает данные, предоставленные запросом POST при использовании eXist через REST, анализирует их как XML и возвращает как node()
, Но по какой-то причине, когда я передаю данные в другую функцию, XQuery не признает их действительными element()
хотя это так. Если я извлекаю его вручную (т.е. копирую вывод и вставляю его в мой исходный код), назначаю его переменной и передаю его в мою функцию, и все идет хорошо. Но если я передаю его напрямую, это дает мне ошибку во время выполнения (и действительно instance of
тестовое задание).
Мне нужно иметь возможность либо игнорировать эту проверку типов, либо "приводить" данные к element()
,
3 ответа
data()
возвращает пустой элемент только потому, что тип аргумента node()
звучит как ошибка для меня. Какой процессор XQuery вы используете?
Похоже, вам нужно успокоить статическую проверку типов, которую вы можете сделать, используя treat as
выражение. Я не верю, что динамический тест с использованием instance of
будет достаточно.
Попробуй это:
let $parameter := someNamespace:functionX() treat as element()
return local:myFunction($parameter)
Цитата из 4-го издания опуса Майкла Кея "The treat as
По сути, оператор сообщает системе, что вы знаете, какой будет тип времени выполнения, и хотите, чтобы любая проверка была отложена до времени выполнения, потому что вы уверены, что ваш код верен. (стр. 679)
ОБНОВЛЕНИЕ: Я думаю, что выше на самом деле неправильно, так как treat as
это просто утверждение. Это не меняет тип аннотации node()
, это означает, что это также неверное утверждение и не поможет вам. Хм... Что я действительно хочу, так это cast as
, но это работает только для атомарных типов. Я думаю, что я в тупике. Может быть, вы должны изменить движки XQuery.:-) Я сообщу, если подумаю о чем-то другом. Кроме того, мне любопытно узнать, работает ли решение Димитра для вас.
ОБНОВЛЕНИЕ № 2: я раньше отступал здесь. Могу ли я вернуться обратно?;-) Теперь моя теория такова treat as
будет работать исходя из того, что node()
интерпретируется как объединение различных конкретных аннотаций типов узлов, а не как сама аннотация типов во время выполнения (см. "Примечание" в разделе "Типы элементов" формальной семантики XQuery.) Во время выполнения аннотация типов будет element()
, использование treat as
чтобы гарантировать тип проверки, что это будет правдой. Теперь я жду затаив дыхание: у тебя это работает?
ПОЯСНИТЕЛЬНОЕ ПРИЛОЖЕНИЕ: Предполагая, что это работает, вот почему. node()
это тип объединения. Актуальные элементы во время выполнения никогда не отмечаются node()
, "Тип элемента - это атомарный тип, тип элемента, тип атрибута, тип узла документа, тип текстового узла, тип узла комментария или тип инструкции обработки". 1 Обратите внимание, что node()
нет в этом списке. Таким образом, ваш движок XQuery не жалуется, что элемент имеет тип node()
; скорее жалуется, что не знает, какой будет тип (node()
означает, что это может оказаться attribute()
, element()
, text()
, comment()
, processing-instruction()
, или же document-node()
). Почему это должно знать? Потому что в другом месте вы говорите, что это элемент (в подписи вашей функции). Недостаточно ограничить его одним из шести вариантов. Статическая проверка типов означает, что вы должны гарантировать - во время компиляции - что типы будут совпадать (элемент с элементом, в данном случае). treat as
используется для сужения статического типа от общего типа (node()
) к более конкретному типу (element()
). Это не меняет динамический тип. cast as
с другой стороны, используется для преобразования элемента из одного типа в другой, изменяя как статический, так и динамический типы (например, xs:string на xs:boolean). Это имеет смысл, что cast as
может использоваться только с атомарными значениями (а не с узлами), потому что это будет означать для преобразования атрибута в элемент (и т. д.)? И нет такой вещи, как преобразование node()
предмет к element()
предмет, потому что нет такой вещи как node()
вещь. node()
существует только как статический тип объединения. Мораль истории? Избегайте процессоров XQuery, которые используют статическую проверку типов. (Извините за странный вывод; я чувствую, что заслужил право.:-))
НОВЫЙ ОТВЕТ НА ОСНОВЕ ОБНОВЛЕННОЙ ИНФОРМАЦИИ: Звучит так, будто статическая проверка типов - это красная сельдь (большая жирная). Я полагаю, что вы на самом деле имеете дело не с элементом, а с узлом документа, который является невидимым корневым узлом, который содержит элемент верхнего уровня (элемент документа) в представлении модели данных XPath правильно сформированного документа XML.
Таким образом, дерево моделируется следующим образом:
[document-node]
|
<docElement>
|
<subelement>
и не так
<docElement>
|
<subelement>
Я предполагал, что вы проходите <docElement>
узел. Но если я прав, вы фактически проходите узел документа (его родитель). Поскольку узел документа невидим, его сериализация (то, что вы скопировали и вставили) неотличима от узла элемента, и различие было потеряно, когда вы вставили то, что теперь интерпретируется как пустой конструктор элемента в вашем XQuery. (Чтобы создать узел документа в XQuery, вы должны обернуть конструктор элемента с помощью document{ ... }
.)
instance of
Тест не пройден, потому что узел - это не элемент, а узел документа. (Это не node()
по сути, потому что нет такой вещи; см. объяснение выше.)
Кроме того, это объясняет, почему data()
возвращается пустым, когда вы пытались получить <subelement>
дочерний элемент узла документа (после ослабления типа аргумента функции до node()
). Первое представление дерева выше показывает, что <subelement>
не является дочерним узлом документа; таким образом он возвращает пустую последовательность.
Теперь для решения. Перед передачей параметра (узла документа), получите его дочерний элемент (элемент документа), добавив /*
(или же /element()
что эквивалентно) вот так:
let $parameter := someNamespace:functionX()/*
return local:myFunction($parameter)
В качестве альтернативы, позвольте вашей функции взять узел документа и обновить аргумент, который вы передаете data():
declare function local:myFunction($arg1 as document-node()) as element() {
let $value := data($arg1/*/subelement)
etc...
};
Наконец, это выглядит как описание запроса eXist: функция get-data() полностью соответствует этому объяснению. В нем говорится: "Если это не двоичный документ, мы пытаемся проанализировать его как XML и вернуть document-node()". (выделение добавлено)
Спасибо за приключение. Оказалось, что это обычная проблема XPath (осведомленность об узлах документов), но я узнал кое-что из нашего обхода проверки статических типов.
Это отлично работает с использованием Saxon 9.3:
declare namespace my = "my:my";
declare namespace their = "their:their";
declare function my:fun($arg1 as element()) as element()
{
$arg1/a
};
declare function their:fun2($arg1 as node()) as node()
{
$arg1
};
my:fun(their:fun2(/*) )
когда приведенный выше код применяется к следующему документу XML:
<t>
<a/>
</t>
правильный результат выдается без сообщений об ошибках:
<a/>
Обновление:
Следующее должно работать даже с самой точной статической реализацией проверки типов XQuery:
declare namespace my = "my:my";
declare namespace their = "their:their";
declare function my:fun($arg1 as element()) as element()
{
$arg1/a
};
declare function their:fun2($arg1 as node()) as node()
{
$arg1
};
let $vRes := their:fun2(/*)
(: this prevents our code from runtime crash :)
return if($vRes instance of element())
then
(: and this assures the static type-checker
that the type is element() :)
my:fun(their:fun2(/*) treat as element())
else()
node()
элемент, атрибут, инструкция обработки, текстовый узел и т. д.
Но data()
преобразует результат в строку, которая не является ни одной из них; это примитивный тип.
Вы можете попробовать item()
, который должен соответствовать либо.
См. 2.5.4.2 Сопоставление ItemType и Item в спецификации W3C XQuery.
Хотя это не показано в вашем примере кода, я предполагаю, что вы на самом деле возвращаете значение (например, $value
вы работаете с) из local:myFunction
,