xsl: сортировка по переменной - PullRequest
2 голосов
/ 29 января 2011

Я просматривал темы о сортировке XML с использованием <xsl:sort> и переменных, и до сих пор не могу заставить мою сортировку работать.Вот некоторая структура XML для контекста:

<records>
  <record>
    <contributors>
      <authors>
        <author>Author 1</author>
        <author>Author 2</author>
      </authors>
    </contributors>
    <titles>
      <title>I'm a Title!</title>
      <secondary-title></secondary-title>
    </titles>
    <dates>
      <year>1901</year>
    </dates>
  </record>
  <record>...</record>
  <record>...</record>
</records>

А вот соответствующий XSL:

<xsl:variable name="sortby" 
              select="contributors/authors/author[1]" as="element()*"/>
<xsl:for-each select="//record">
  <xsl:sort select="$sortby" order="ascending"/>
  [a bunch of HTML to render the records as a bibliography]
</xsl:for-each>           

Если я скопирую строку в атрибуты «select» переменной и вставлю ее в sort,это:

<xsl:sort select="contributors/authors/author[1]" order="ascending">

тогда это работает.С переменной это не так.Я пробовал как с, так и без as="element()*" - Помощь?

Ответы [ 3 ]

1 голос
/ 29 января 2011

В общем случае невозможно выполнить динамическую оценку выражений XPath - ни в XSLT / Xpath 1.0, ни в XSLT / Xpath 2.0 .

При этом всегда можно реализовать сортировку по переменным, если есть ограничения на их содержимое.

Вот пример, который решает вашу конкретную проблему и класс подобных проблем:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pSortName" select="'authors'"/>
 <xsl:param name="pSortPosition" select="1"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="records">
  <records>
   <xsl:apply-templates>
     <xsl:sort select=
     ".//*[name()=$pSortName]/*
             [position()=$pSortPosition]"/>
   </xsl:apply-templates>
  </records>
 </xsl:template>
</xsl:stylesheet>

когда это преобразование применяется к этому документу XML :

<records>
    <record>
        <contributors>
            <authors>
                <author>X.Y.Z</author>
                <author>A.B.C</author>
            </authors>
        </contributors>
        <titles>
            <title>Title B</title>
            <secondary-title>Title AB</secondary-title>
        </titles>
        <dates>
            <year>1901</year>
        </dates>
    </record>
    <record>
        <contributors>
            <authors>
                <author>T.U.V</author>
                <author>D.E.F</author>
            </authors>
        </contributors>
        <titles>
            <title>Title A</title>
            <secondary-title>Title BA</secondary-title>
        </titles>
        <dates>
            <year>2001</year>
        </dates>
    </record>
</records>

желаемый, правильный результат (записи отсортированы по первому автору) :

<records>
   <record>
      <contributors>
         <authors>
            <author>T.U.V</author>
            <author>D.E.F</author>
         </authors>
      </contributors>
      <titles>
         <title>Title A</title>
         <secondary-title>Title BA</secondary-title>
      </titles>
      <dates>
         <year>2001</year>
      </dates>
   </record>
   <record>
      <contributors>
         <authors>
            <author>X.Y.Z</author>
            <author>A.B.C</author>
         </authors>
      </contributors>
      <titles>
         <title>Title B</title>
         <secondary-title>Title AB</secondary-title>
      </titles>
      <dates>
         <year>1901</year>
      </dates>
   </record>
</records>

Если мы изменим параметры на :

 <xsl:param name="pSortName" select="'authors'"/>
 <xsl:param name="pSortPosition" select="2"/>

, затем преобразование сортируется с использованием в качестве ключа сортировки второго author.

Если мы изменим параметры на :

 <xsl:param name="pSortName" select="'titles'"/>
 <xsl:param name="pSortPosition" select="1"/>

, затем преобразование сортируется с использованием в качестве ключа сортировки элемента titles/title .

Если мы изменим параметры на :

 <xsl:param name="pSortName" select="'titles'"/>
 <xsl:param name="pSortPosition" select="2"/>

, затем преобразование сортируется с использованием в качестве ключа сортировки элемента titles/secondary-title .

Do note : Здесь мы предполагаем, что будет существовать уникальный потомок любого сортируемого элемента, имя которого равно значению, указанному в pSortName. Мы также предполагаем, что этот элемент имеет дочерние элементы, а pSortPosition указывает позицию дочернего элемента, который будет использоваться в качестве ключа сортировки.

1 голос
/ 29 января 2011

Два других решения, которые не были упомянуты:

(a) У многих процессоров есть расширение, называемое чем-то вроде dyn :valu (), которое оценивает выражение XPath, представленное в виде символьной строки

(b) В некоторых средах целесообразно изменить таблицу стилей (используя, конечно, XSLT-преобразование) перед ее выполнением. Это позволяет вам вставлять любое выражение XPath, которое вам нужно. В XSLT 2.0 вы можете написать ключ сортировки как select="my:sort(.)", а затем определить my: sort () в отдельном модуле таблицы стилей xsl: включенный.

Другая связанная с этим опция, которую я видел, - это использование внешней сущности: select = "& sortkey;", где ссылка на сущность может быть программно перенаправлена ​​на другое выражение XPath с помощью EntityResolver, зарегистрированного с помощью синтаксического анализатора XML.

0 голосов
/ 29 января 2011

Нельзя использовать переменную в части select= элемента xsl:sort. В спецификации XSLT указано:

xsl:sort имеет атрибут select, значение которого является выражением. Для каждого обрабатываемого узла выражение оценивается с этим узлом в качестве текущего узла и с полным списком узлов, обрабатываемых в несортированном порядке, в качестве текущего списка узлов.

Выражение вычисляется только один раз , а не дважды, как вы ожидаете. Ваше выражение $sortby вычисляется один раз, и каждый раз получается что-то одинаковое (фактическое значение зависит от того, каким был текущий узел во время выполнения назначения xsl:variable). Поэтому сортировка не меняет порядок выбранных элементов.

Вы должны использовать конкретное выражение в качестве критерия сортировки, как вы обнаружили.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...