2009-10-29 1 views
4

У меня возникла проблема, когда мне нужно сортировать элементы в зависимости от их значения, которое содержит числа, разделенные точками. Мне нужно отсортировать элементы в зависимости от значения числа перед первым периодом, затем числа между первым и вторым периодами и т. Д. Я не знаю, насколько глубока эта иерархия, и это самая большая проблема.XSL рекурсивный вид

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <ROW>2.0.1</ROW> 
    <ROW>1.2</ROW> 
    <ROW>1.1.1</ROW> 
    <ROW>1.2.0</ROW> 
    <ROW>1</ROW> 
</root> 

Результат Шоул выглядеть так:

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <ROW>1</ROW> 
    <ROW>1.1.1</ROW> 
    <ROW>1.2</ROW> 
    <ROW>1.2.0</ROW> 
    <ROW>2.0.1</ROW> 
</root> 

Возможно ли это вообще? Цените любую помощь.

+0

Версия XSLT? Отвращение к скрипту/расширителям или требуется чистый XSLT? Платформа? – AnthonyWJones

+0

Это должно быть сделано с Xalan 2.5.1 Java-библиотекой, поэтому это подразумевает XLST версию 1. – NSPKUWCExi2pr8wVoGNk

+0

должен 2.01 прийти до или после 2.1? должно ли быть 2,01? –

ответ

4

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

<xsl:template match="root"> 
    <xsl:copy> 
     <xsl:apply-templates select="ROW"> 
      <xsl:sort select="substring-before(concat(., '.'), '.')" data-type="number"/> 
     </xsl:apply-templates> 
    </xsl:copy> 
</xsl:template> 
<xsl:template match="ROW"> 
    <xsl:param name="prefix" select="''"/> 
    <xsl:choose> 
     <!-- end of recursion, there isn't any more ROW with more chucks --> 
     <xsl:when test=". = substring($prefix, 1, string-length($prefix)-1)"> 
      <xsl:copy-of select="."/> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:variable name="chuck" select="substring-before(concat(substring-after(., $prefix), '.'), '.')"/> 
      <!-- this test is for grouping ROW with same prefix, to skip duplicates --> 
      <xsl:if test="not(preceding-sibling::ROW[starts-with(., concat($prefix, $chuck))])"> 
       <xsl:variable name="new-prefix" select="concat($prefix, $chuck, '.')"/> 
       <xsl:apply-templates select="../ROW[starts-with(., $new-prefix) or . = concat($prefix, $chuck)]"> 
        <xsl:sort select="substring-before(concat(substring-after(., $new-prefix), '.'), '.')" data-type="number"/> 
        <xsl:with-param name="prefix" select="$new-prefix"/> 
       </xsl:apply-templates> 
      </xsl:if> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 
1

Единственная проблема в выполнении этого в работе с индивидуальными номерами (между периодами) различной длиной текста (т.е. сортировочных 1,0, 2,0 и 10,0, в указанном порядке). Если есть верхний предел размера отдельных номеров (например, n цифр), тогда создайте ключ сортировки, который является конкатенацией всех чисел с нулевым числом до n цифр. При n = 3 это приводит к

Row    Key (string) 
1     001 
1.0    001000 
1.0.1    001000001 
1.1    001001 
1.2.1    001002001 
2.0.1    002000001 
10.0.1   010000001 

Затем сортируйте по ключу. Если вы застряли в XSLT 1.0, вам придется прибегнуть к функциям расширения EXSLT, чтобы выполнить синтаксический анализ и нормализовать ключ.

+0

Ваша «единственная проблема» - это точно трудная часть. –

1

Это не очень хорошо, но вы можете использовать функцию xalan: nodeset для «предварительной обработки» чисел в набор узлов с легко сортируемым выражением, как описано Джим.

Этот пример работает для меня с Xalan 2.5.1:

<?xml version="1.0"?> 

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" 
    xmlns:xalan="http://xml.apache.org/xalan"> 

<xsl:output method="xml" indent="yes" /> 

<xsl:template match="/"> 
    <root> 
     <!-- Create a sort node with a sort expression wrapping each ROW --> 
     <xsl:variable name="nodes"> 
      <xsl:for-each select="/root/ROW"> 
       <xsl:variable name="sort-string"> 
        <xsl:call-template name="create-sort-string"> 
         <xsl:with-param name="sort-string" select="text()" /> 
        </xsl:call-template> 
       </xsl:variable> 
       <sort sort-by="{$sort-string}"> 
        <xsl:copy-of select="." /> 
       </sort> 
      </xsl:for-each> 
     </xsl:variable> 

     <!-- Now sort the sort nodes and copy out the ROW elements --> 
     <xsl:for-each select="xalan:nodeset($nodes)/sort"> 
      <xsl:sort select="@sort-by" data-type="text" /> 
      <xsl:copy-of select="*" /> 
     </xsl:for-each> 
    </root> 
</xsl:template> 

<xsl:template name="create-sort-string"> 
    <xsl:param name="sort-string" /> 
    <!-- Biggest number at each level --> 
    <xsl:variable name="max-num" select="1000" /> 
    <xsl:choose> 
     <xsl:when test="contains($sort-string, '.')"> 
      <xsl:value-of select="$max-num + number(substring-before($sort-string, '.'))" /> 
      <xsl:call-template name="create-sort-string"> 
       <xsl:with-param name="sort-string" select="substring-after($sort-string, '.')" /> 
      </xsl:call-template> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:value-of select="concat($max-num + number($sort-string), '0')" /> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

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