Ось предков XQuery не работает, но явный XPath работает

Рассмотрим следующий XML фрагмент:

<doc>
    <chapter id="1">
        <item>
            <para>some text here</para>
        </item>
    </chapter>
</doc>

В XQuery у меня есть функция, которая должна делать некоторые вещи на основе главы предка данного элемента "para", который передается в качестве параметра, как показано в урезанном примере ниже:

declare function doSomething($para){
    let $chapter := $para/ancestor::chapter
    return "some stuff"
};

В этом примере $chapter продолжает появляться пустым. Однако, если я напишу функцию, аналогичную следующей (т. е. без использования оси предков), я получу желаемый элемент "глава":

declare function doSomething($para){
    let $chapter := $para/../..
    return "some stuff"
};

Проблема в том, что я не могу использовать явные пути, как в последнем примере, потому что XMl, который я буду искать, не гарантирует, что элемент «chapter» каждый раз будет прародителем. Это может быть прапрадед или прапрапрадедушка и т. д., как показано ниже:

<doc>
    <chapter id="1">
        <item>
            <subItem>
                <para>some text here</para>
            </subItem>
        </item>
    </chapter>
</doc>

У кого-нибудь есть объяснение, почему ось не работает, а явный XPath работает? Кроме того, есть ли у кого-нибудь какие-либо предложения о том, как решить эту проблему?

Спасибо.


РЕШЕНИЕ:

Теперь тайна разгадана.

Рассматриваемый узел был воссоздан в другой функции, что привело к удалению из него всей информации о его предке. К сожалению, предыдущий разработчик не задокументировал эту замечательную маленькую функцию, что стоило всем нам немало времени.

Итак, ось-предок работала именно так, как и должна — она просто применялась к обманчивому узлу.

Я благодарю всех вас за ваши усилия в ответах на мои вопросы.


person mahdaeng    schedule 03.01.2012    source источник


Ответы (4)


Ось предков работает нормально. Я подозреваю, что ваша проблема заключается в пространствах имен. Пример, который вы показали и который я запустил (ниже), имеет XML без каких-либо пространств имен. Если в вашем XML есть пространство имен, вам нужно указать его в предке XPath, например: $para/ancestor:foo:chapter, где в этом случае prefix _foo_ is привязано к правильному пространству имен для элемента главы.

let $doc := <doc>
    <chapter id="1">
        <item>
            <para>some text here</para>
        </item>
    </chapter>
</doc>

let $para := $doc//para
return $para/ancestor::chapter

РЕЗУЛЬТАТ:

<?xml version="1.0" encoding="UTF-8"?>
<chapter id="1">
  <item>
    <para>some text here</para>
  </item>
</chapter>
person Clark Richey    schedule 03.01.2012
comment
Спасибо, Кларк. Я проверил, что $para — это то, что я ожидал, через журналы ошибок в MarkLogic 4.2. Я знаю, что это ДОЛЖНО работать, но почему-то не работает. Здесь происходит много головокружения. :^) - person mahdaeng; 03.01.2012
comment
Я подозреваю, что ваша проблема заключается в пространствах имен. Пример, который вы показали и который я запустил, имеет XML без каких-либо пространств имен. Если у вашего XML действительно есть пространство имен, вам нужно будет предоставить его в предке XPath, например: $para/ancestor:foo:chapter, где в этом случае префикс foo привязан к правильному пространству имен для элемента главы. - person Clark Richey; 03.01.2012
comment
@mahdaeng В качестве альтернативы вы могли объявить пространство имен элементов по умолчанию для некоторого конкретного пространства имен в верхней части вашего модуля, в то время как в вашем XML его нет или он другой. - person grtjn; 03.01.2012
comment
Спасибо, джентльмены, за то, что вы упомянули о возможной проблеме с пространствами имен. Однако в этом документе пространства имен не используются. - person mahdaeng; 04.01.2012

Эти вещи почти всегда сводятся к пространствам имен! Как даигностик, чтобы подтвердить 100%, что пространство имен не является проблемой, можете ли вы попробовать:

declare function local:doSomething($para) {
    let $chapter := $para/ancestor::*[local-name() = 'chapter']
    return $chapter
};
person Oliver Hallam    schedule 03.01.2012

Это кажется мне удивительным; какую реализацию XQuery вы используете? С BaseX следующий запрос...

declare function local:doSomething($para) {
    let $chapter := $para/ancestor::chapter
    return $chapter
};

let $xml :=
  <doc>
    <chapter id="1">
        <item>
            <para>some text here</para>
        </item>
    </chapter>
</doc>
return local:doSomething($xml//para)

...возвращается...

<chapter id="1">
  <item>
    <para>some text here</para>
  </item>
</chapter>
person Christian Grün    schedule 03.01.2012
comment
Спасибо, Кристиан. Я использую любую реализацию XQuery, предоставляемую MarkLogic 4.2. - person mahdaeng; 03.01.2012
comment
Да, я согласен, что это прекрасно работает с использованием оценщика запросов на странице developer.marklogic.com/try/ninja/page2. на базе MarkLogic 5. Какую версию 4.2 вы используете? - person Eric Bloch; 03.01.2012
comment
Я слышал, что приведенный выше код отлично работает в 4.2-4, поэтому, вероятно, что-то из окружающей среды может вас запутать. - person Eric Bloch; 04.01.2012

Я тоже подозреваю пространства имен. Если $para/../.. работает, а $para/parent::item/parent::chapter оказывается пустым, то вы знаете, что это вопрос пространств имен.

Найдите объявление xmlns вверху вашего контента, например:

<doc xmlns="http://example.com">
  ...
</doc>

Затем в вашем XQuery вам нужно связать это пространство имен с префиксом и использовать этот префикс в ваших выражениях XQuery/XPath, например:

declare namespace my="http://example.com";
declare function doSomething($para){
  let $chapter := $para/ancestor::my:chapter
  return "some stuff"
};

Какой префикс вы используете, не имеет значения. Важно то, что URI пространства имен (http://example.com в приведенном выше примере) совпадает.

Имеет смысл, что ../.. выбирает нужный элемент, потому что .. — это сокращение от parent::node(), которое выбирает родительский узел независимо от его имени (или пространства имен). В то время как ancestor::chapter будет выбирать только <chapter> элементов, которые не находятся в пространстве имен (если только вы не объявили пространство имен элементов по умолчанию, что обычно не является хорошей идеей в XQuery, поскольку влияет как на ввод, так и на вывод).

person Evan Lenz    schedule 03.01.2012