Элемент () против узла () в XQuery - PullRequest
5 голосов
/ 19 декабря 2011

Может кто-нибудь сказать мне точную разницу между 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 DB.Эта функция получает данные, предоставленные запросом POST при использовании eXist через REST, анализирует их как XML и возвращает как node().Но по какой-то причине, когда я передаю данные в другую функцию, XQuery не признает их действительными element(), даже если это так.Если я извлекаю его вручную (т.е. копирую вывод и вставляю его в мой исходный код), назначаю его переменной и передаю его моей функции, и все идет хорошо.Но если я передам его напрямую, это даст мне ошибку во время выполнения (и действительно не пройдёт тест instance of).

Мне нужно иметь возможность либо игнорировать эту проверку типов, либо «приводить» данные кelement().

Ответы [ 3 ]

8 голосов
/ 20 декабря 2011

data() возвращает пустой элемент только потому, что тип аргумента node() звучит для меня как ошибка. Какой процессор XQuery вы используете?

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

Попробуйте это:

let $parameter := someNamespace:functionX() treat as element()
return local:myFunction($parameter)

Цитируя из 4-го издания «Большого опуса» Майкла Кея: «Оператор 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 (осведомленность об узлах документов), но я узнал кое-что из нашего обхода о статической проверке типов.

2 голосов
/ 19 декабря 2011

Это прекрасно работает с использованием 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()
1 голос
/ 19 декабря 2011

node() - это элемент, атрибут, инструкция обработки, текстовый узел и т. Д.

Но data() преобразует результат в строку, которая не является ни одной из них; это примитивный тип.

Возможно, вы захотите попробовать item(), который должен совпадать либо с

См. 2.5.4.2 Сопоставление ItemType и Item в спецификации W3C XQuery.

Хотя это не показано в вашем примере кода, я предполагаю, что вы действительно возвращаете значение (например, $value, с которым работаете) из local:myFunction.

...