Как отсортировать группу в естественном порядке с помощью букв и цифр в XSLT2?

Я унаследовал код, где я не совсем понимаю, почему он не работает, но я также хочу, чтобы он делал больше. Очевидная проблема заключается в том, что group-by никогда не найдет группу, потому что атрибут, который он ищет, всегда является уникальным значением для каждого элемента. Кроме того, я также ищу сортировку в естественном порядке. Из того, что я могу найти, я думаю, что мне может понадобиться сначала отсортировать группу по буквам, а затем отсортировать результат по номерам, но я не уверен. В настоящее время даже базовая сортировка, которая есть, не работает. Существующий код XSLT:

<xsl:for-each-group select="datafield[@tag='856']" group-by="subfield[@code='u']">
    <xsl:sort select="number(normalize-space(substring-after(subfield[@code='z'], ',')))"/>
    <xsl:copy-of select="."/>
</xsl:for-each-group>

Используя эти данные:

<collection>
   <record>
      <datafield ind1="4" ind2="1" tag="856">
         <subfield code="u">https://www.example.com/ride02meys</subfield>
         <subfield code="z">Digital item, v.2</subfield>
      </datafield>
      <datafield ind1="4" ind2="1" tag="856">
         <subfield code="u">https://www.example.com/ride01meys</subfield>
         <subfield code="z">Digital item, v.1</subfield>
      </datafield>
      <datafield ind1="4" ind2="1" tag="856">
         <subfield code="u">https://www.example.com/ride12meys</subfield>
         <subfield code="z">Digital item, v.12</subfield>
   </record>
</collection>

Я пытаюсь получить следующее, где оно отсортировано как 1, 2, 12, а не 1, 12, 2.

<collection>
   <record>
      <datafield ind1="4" ind2="1" tag="856">
         <subfield code="u">https://www.example.com/ride01meys</subfield>
         <subfield code="z">Digital item, v.1</subfield>
      </datafield>
      <datafield ind1="4" ind2="1" tag="856">
         <subfield code="u">https://www.example.com/ride02meys</subfield>
         <subfield code="z">Digital item, v.2</subfield>
      </datafield>
      <datafield ind1="4" ind2="1" tag="856">
         <subfield code="u">https://www.example.com/ride12meys</subfield>
         <subfield code="z">Digital item, v.12</subfield>
   </record>
</collection>

Я, по общему признанию, слаб с XSLT, поэтому любые рекомендации будут оценены.

Заранее спасибо


person LOlliffe    schedule 19.08.2019    source источник


Ответы (2)


Чтобы отсортировать ваши данные сначала в алфавитно-цифровом порядке по строке перед точкой, а затем численно по строке после точки, вы можете использовать два xsl:sort, например:

<xsl:for-each-group select="datafield[@tag='856']" group-by="subfield[@code='u']">
    <xsl:sort select="normalize-space(substring-before(subfield[@code='z'],'.'))" data-type="text"  order="ascending" />
    <xsl:sort select="normalize-space(substring-after(subfield[@code='z'], '.'))" data-type="number" order="ascending" />            
    <xsl:copy-of select="."/>
</xsl:for-each-group>

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

Примечание:
group-by="subfield[@code='u']" из xsl:for-each-group приводит к тому, что обрабатывается только первый из subfield с одинаковым значением. Если вам нужны все subfield, либо переберите current-group() с xsl:for-each, либо используйте xsl:for-each в первую очередь.

person zx485    schedule 20.08.2019
comment
Эй, спасибо! Единственная загвоздка в том, что это не всегда v.. Это поле сильно варьируется, иногда это годы, или v., будучи vol. вместо. Но если бы он был хотя бы отсортирован по алфавиту, а потом по номерам, то это было бы большим подспорьем. Еще раз спасибо за group-by информацию. - person LOlliffe; 20.08.2019
comment
Это решение работает только в конкретном случае, когда ключ сортировки состоит из буквенной части, за которой следует точка, за которой следует числовая часть. Это не сработает, например, для такого ключа, как Volume 23 Issue 3. - person Michael Kay; 20.08.2019
comment
@MichaelKay - Хотя я выбрал очищенные примеры, мягко говоря, данные, с которыми я работаю, крайне противоречивы. Так что, даже несмотря на то, что я болезненно осознаю, насколько именно вы правы, любое подобие своего рода все еще является огромной помощью. - person LOlliffe; 20.08.2019

Вы должны иметь возможность использовать collation как http://www.w3.org/2013/collation/UCA?lang=en;numeric=yes на xsl:sort, как в

 <xsl:template match="record">
      <xsl:copy>
        <xsl:for-each-group select="datafield[@tag='856']" group-by="subfield[@code='u']">
            <xsl:sort select="normalize-space(substring-after(subfield[@code='z'], ','))"
               collation="http://www.w3.org/2013/collation/UCA?lang=en;numeric=yes"/>
            <xsl:copy-of select="."/>
        </xsl:for-each-group>          
      </xsl:copy>
  </xsl:template>

https://xsltfiddle.liberty-development.net/ncdD7nt

Подробнее о сопоставлениях читайте на странице https://www.w3.org/TR/xslt-30/#uca-collations.

Для более старых версий Saxon 9 вы можете использовать другую сортировку:

  <xsl:template match="record">
      <xsl:copy>
        <xsl:for-each-group select="datafield[@tag='856']" group-by="subfield[@code='u']">
            <xsl:sort select="normalize-space(substring-after(subfield[@code='z'], ','))"
               collation="http://saxon.sf.net/collation?lang=en;alphanumeric=yes"/>
            <xsl:copy-of select="."/>
        </xsl:for-each-group>          
      </xsl:copy>
  </xsl:template>

http://xsltransform.net/nbiCsZq содержит пример, документация находится по адресу http://saxonica.com/html/documentation9.6/extensibility/config-extend/collation/implementing-collation.html.

person Martin Honnen    schedule 20.08.2019
comment
Обратите внимание, что этот URI сопоставления UCA стандартизирован в XSLT 3.0, поэтому он должен предлагаться всеми процессорами XSLT 3.0. URI сортировки доступны в XSLT 2.0, но они не стандартизированы для разных процессоров. - person Michael Kay; 20.08.2019