Три редкоиспользуемые оси в XPath

    Основная мощь языка XPath заключается в осях, позволяющих добраться до любого элемента в исходном документе. Рассмотрим применение таких редкоиспользуемых осей, как ancestor, descendant и self.

    ancestor


    Задача: получить атрибут id элемента-«прадеда» foo.

    Обычно в таких случаях начинают рисовать лестницы:

    ../../../@id

    Такая запись плоха тем, что малопонятна без знания исходного xml. Автор рекомендует в подобных случаях использовать более информативное выражение:

    ancestor::foo[1]/@id

    Эта запись не только даёт представление об искомом элементе, но и продолжает работать даже в том случае, когда текущий элемент изменил своё положение в дереве.

    descendant


    Задача: найти первого потомка foo в текущем элементе.

    Когда похожий вопрос был задан на сайте StackOverflow.com, два человека тут же ответили .//foo[1] и были поддержаны другими участниками. Автору пришлось вмешаться и предупредить о неправильности данного выражения. Правильный ответ: descendant::foo[1], и вот почему.

    .//foo является короткой формой следующего выражения:

    self::node()/descendant-or-self::node()/child::foo

    Запись .//foo[1] означает все потомки foo, каждый из которых первый foo у своего родителя. Такое выражение вернёт два элемента в следующем документе:

    <list>
        <item><foo/></item>
        <item><foo/></item>
    </list>

    descendant::foo[1] вернёт ровно один элемент.

    Это различие описано в документации, но почему-то многие читают её невнимательно. Во избежание ошибок автор рекомендует всегда писать descendant::foo вместо .//foo, поскольку в подавляющем большинстве случаев именно это и имеется в виду.

    self


    Казалось бы, зачем нужна self, если есть более короткий вариант: . (точка). Однако и у этой оси нашлось своё применение.

    Задача: получить следующий элемент, если он называется foo.

    Очевидное решение:

    following-sibling::*[1][name() = 'foo']

    Более элегантное, на взгляд автора, выражение:

    following-sibling::*[1]/self::foo

    Комментарии и дополнения приветствуются.
    Share post

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 16

      0
      Спасибо, запомнил и на всякий случай добавил в избранное.
        +2
        Отличная статья, как раз то сто сейчас нужно! Спасибо автору!
          +1
          Оси используемые. Трудно даже представить как без них. Может за исключением self, наверное*)
          Куда интересней было прочитать, про «descendant::foo».
            0
            «редкоиспользуемые» — это вы конешно перегнули, но статья вполне себе ничего
              +2
              Соглашусь не со всем, self вообще не имеет смысла, особенно в этом контексте. Как выше было сказано, гораздо лучше писать following-sibling::foo[1]. Видимо эта ось навигации самая редкая.

              А чтобы разрешить проблему .//foo[1], пишите (.//foo)[1]. И вообще, если в вашем XPath выражении используется хоть один "/" и необходимо получить лишь первый элемент, используйте всегда скобки (xpath/xpath)[1], иначе рискуете получить не то что хотели.

              А если XPath выражение используется в <xsl:value-of select=""/>, то вообще нет необходимости использовать ограничитель [1] (синоним [position()=1]).
                0
                following-sibling::foo[1] не решают исходную задачу, прочтите внимательно условие. Спасибо за замечание про скобки.
                  0
                  > А если XPath выражение используется в <xsl:value-of select=""/>, то вообще нет необходимости использовать ограничитель [1]

                  А разве в этом случае у нас не выберется текстовое содержимое всех нод? А с ограничителем — текстовое содержимое первой ноды вроде только…
                    0
                    xsl:value-of вызывает функцию string:
                    A node-set is converted to a string by returning the string-value of the node in the node-set that is first in document order. If the node-set is empty, an empty string is returned.
                    • UFO just landed and posted this here
                  +1
                  Я бы тоже не назвал эти оси редкоиспользуемыми.

                  Вот здесь небольшая, но полезная шпора по осям. Кармы нет, поэтому в личном.
                    0
                    Самая удобная схема какую я видел, в книге Алексея Валикова «Технология XSLT». Себе даже перерисовал и не стенку вешал. В этой же вместо букв лучше числа использовать.
                      0
                      Нашёл её у себя, разместил в первом комментарии по ссылке выше.
                    0
                    В силу спицифики работы (разработка XML парсера) накапливаеться огромное количество XSLT-шек, которые могут конфликтовать между собой при дольнейшей разработки.
                    И поэтому от записей типа ../../../@id или .//foo пришлось отказаться потому что:
                    1. плохочитабельно
                    2. подобнуй путь не гибок

                    Из опыта рекомендую ичпользовать оси, которые в данном топики были представленны как редкоиспользуемые почаще:)

                    PS

                    following-sibling::*[1]/self::foo
                      0
                      сори за PS, он не несет смысловой нагрузки)
                        0
                        Я вообще когда начал использовать xslt учился на примерах, где подобных конструкций "../../../@id" не встречал в принципе. А работал только с осями. Поэтому для меня определение «редко используемые» было немножко неожиданным.
                          0
                          А самими примерами не поделитесь?

                      Only users with full accounts can post comments. Log in, please.