Pregunta

Estoy enfrentando un problema en el que necesito ordenar elementos, dependiendo de su valor, que contiene un número, separados por puntos. Necesito ordenar elementos según el valor del número antes del primer período, luego el número entre el primer y el segundo período, y así sucesivamente. No sé qué tan profunda puede llegar esta jerarquía y ese es el mayor problema.

<?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>

El resultado debería ser así:

<?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>

¿Es esto posible? Agradezco cualquier ayuda.

¿Fue útil?

Solución

hay un " fácil " respuesta que no usa ninguna extensión: divide los valores de las filas en mandriles y clasifícalos en ellos.

<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>

Otros consejos

El único problema para lograr esto es tratar con números individuales (entre los períodos) de diferentes longitudes de texto (es decir, ordenar 1.0, 2.0 y 10.0 en ese orden). Si hay un límite superior en el tamaño de los números individuales (digamos n dígitos), cree una clave de clasificación que sea la concatenación de todos los números con relleno de cero a n dígitos. Para n = 3, esto resulta en

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

Luego, ordena la clave. Si está atascado en XSLT 1.0, tendrá que recurrir a las funciones de extensión EXSLT para realizar el análisis y la normalización de teclas.

No es bonito, pero puede usar la función xalan: nodeset para " preprocesar " los números en un conjunto de nodos con una expresión fácilmente clasificable según lo descrito por Jim.

Este ejemplo me funciona con 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>

Personalmente, creo que escribir una función de extensión sería preferible, pero sé que no siempre es una opción.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top