#xslt-2.0 #xpath-2.0
#xslt-2.0 #xpath-2.0
Вопрос:
Есть ли встроенный или простой способ генерировать последовательность значений, которые будут сортироваться в алфавитном порядке?
В качестве примера, следующие элементы элемента имеют дочерний элемент последовательности, предназначенный для сортировки. Данные генерируются для другой системы, которой требуется эта информация, поскольку она не работает с упорядочением документов.
<list>
<item>
<sequence>1</sequence>
</item>
<item>
<sequence>2</sequence>
</item>
<item>
<sequence>3</sequence>
</item>
<item>
<sequence>4</sequence>
</item>
...
<item>
<sequence>14</sequence>
</item>
<item>
<sequence>15</sequence>
</item>
Вместо числовой сортировки система сортирует в алфавитном порядке и не может изменить это поведение. В результате элементы сортируются как 1,10,11,12,13,14,15,2,3,4,5,6,7,8,9
Кроме того, фактическое количество элементов не ограничено, поэтому должен быть общий способ генерации алфавитной последовательности, которая будет сортироваться в правильном порядке. Тем не менее, количество элементов должно быть относительно небольшим, менее 1000.
Это то, что у меня есть до сих пор:
<xsl:variable name="idchars" as="xs:string *"
select="
('0','1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F','G','H','I','J','K','L','M',
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z')
"
/>
<xsl:variable name="idlen" as="xs:integer" select="count($idchars)" />
<xsl:function name="f:genId" as="xs:string">
<xsl:param name="pSeq" as="xs:integer" />
<xsl:variable name="vLen" as="xs:integer" select="$pSeq idiv $idlen" />
<xsl:value-of>
<xsl:for-each select="1 to $vLen">
<xsl:sequence select="$idchars[$idlen]" />
</xsl:for-each>
<xsl:sequence select="$idchars[$pSeq mod $idlen]" />
</xsl:value-of>
</xsl:function>
Комментарии:
1. Я не понял, что вы хотите сделать с образцом данных. Какой вывод вы хотите создать? Или где / как бы вы использовали «последовательность, которая будет сортироваться в алфавитном порядке»?
2. Образец — это то, что выводится в данный момент, но
item
элементы сортируются на основеsequence
элементов в алфавитном порядке, а не в числовом порядке системой, получающей данные. Получатель данных не может изменить это поведение, поэтому вместо этого я пытаюсь сгенерировать алфавитную последовательность. функция, которая у меня есть, работает, но мне любопытно, есть ли лучший или более стандартный способ.3. Можете ли вы показать, где вы используете опубликованный код? Я не совсем понимаю, как / где вы используете его в контексте сортировки представленных вами входных данных.
4. Этого действительно легко и просто достичь в XSLT, используя
<xsl:sort>
дочерний элемент<xsl:apply-templates>
or<xsl:for-each>
(и в XSLT 2.0 также of<xsl:for-each-group>
)
Ответ №1:
Вы могли бы использовать format-number()
для того, чтобы сгенерировать заполненную слева последовательность чисел с переменной длиной начальных нулей, затем преобразовать каждую из этих цифр в букву, используя codepoints-to-string()
, а затем соединить их вместе с string-join()
:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:f="local">
<xsl:output indent="yes"/>
<xsl:function name="f:genId" as="xs:string">
<xsl:param name="pSeq" as="xs:integer" />
<xsl:param name="digits" as="xs:integer"/>
<xsl:variable name="picture" select="string-join( ((1 to $digits) ! '0'), '')" />
<xsl:variable name="padded-string" select="format-number($pSeq, $picture)" as="xs:string"/>
<xsl:variable name="padded-letter-seq" select="(1 to string-length($picture)) ! substring($padded-string, ., 1) ! codepoints-to-string(xs:integer(.) 65)" as="xs:string*"/>
<xsl:sequence select="string-join($padded-letter-seq, '')"/>
</xsl:function>
<xsl:function name="f:genId" as="xs:string">
<xsl:param name="pSeq" as="xs:integer"/>
<xsl:sequence select="f:genId($pSeq, 4)"/>
</xsl:function>
<xsl:template match="/">
<xsl:sequence select="f:genId(3)"/>
<xsl:sequence select="f:genId(3,5)"/>
</xsl:template>
</xsl:stylesheet>
Ответ №2:
Просто используйте элемент <xsl:sort>
xslt, просто так:
<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:template match="/*">
<xsl:copy>
<xsl:apply-templates select="*">
<xsl:sort select="sequence"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Когда это преобразование применяется к предоставленному XML-документу:
<list>
<item>
<sequence>1</sequence>
</item>
<item>
<sequence>2</sequence>
</item>
<item>
<sequence>3</sequence>
</item>
<item>
<sequence>4</sequence>
</item>
...
<item>
<sequence>14</sequence>
</item>
<item>
<sequence>15</sequence>
</item>
</list>
получен требуемый правильный результат:
<list>
<item>
<sequence>1</sequence>
</item>
<item>
<sequence>14</sequence>
</item>
<item>
<sequence>15</sequence>
</item>
<item>
<sequence>2</sequence>
</item>
<item>
<sequence>3</sequence>
</item>
<item>
<sequence>4</sequence>
</item>
</list>
Обратите внимание:
Это:
<xsl:sort select="sequence"/>
эквивалентно:
<xsl:sort select="sequence" data-type="text"/>
поскольку «текст» является значением по умолчанию для data-type
атрибута.
Если необходимо выполнить числовую сортировку, следует явно использовать:
<xsl:sort select="sequence" data-type="number"/>