Frage

I previously posted this question on this portal for which I got a satisfactory solution. Now my problem has evolved a bit. What if my XML in the linked question is updated so that my numbers have a unit associated with them like cm, m, in, px etc. So that the new XML is:

<root>
<a>
    <b>12cm</b>
    <e>hello</e>
</a> 
<a>
    <b>11m</b>
    <e>how</e>
</a>
<a>
    <c>13m</c>
    <f>are</f>
</a>
<a>
    <b>21cm</b>
    <f>you</f>
</a>
<a>
    <d>22cm</d>
    <e>hello</e>
</a>
<a>
    <c>14m</c>
    <f>hi</f>
</a>

Now I would first need to transform theses numbers in a similar unit (lets say in cm) before I can sort them and find the maximum number. How can I do that?

I used the following XSL for my previous problem but now I need to update it so that I am able to transform my numbers before sorting them.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes" omit-xml-declaration="yes"/>

<xsl:template match="root">
    <xsl:for-each select="a/b | a/c | a/d">
        <xsl:sort select="." order="descending"/>   <!-- find the unit and transform it in centimeters before sorting -->
        <xsl:if test="position() = 1">
            <highest><xsl:copy-of select="."/></highest>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Note: I am using XSLT 1.0

War es hilfreich?

Lösung 2

I have adapted the answer of Dimitre Novatchev in here (xslt sort output xml).

If you apply the following stylesheet to your input:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ext="http://exslt.org/common"
    exclude-result-prefixes="ext">

    <xsl:output indent="yes" omit-xml-declaration="yes"/>

    <xsl:variable name="letters" select="'abcdefghijklmnopqrstuvwxyz'"/>

    <xsl:template match="/">
        <xsl:variable name="unsorted">
            <root>
                <xsl:for-each select="root/a/*">
                    <xsl:choose>
                        <xsl:when test="string(number(translate(., $letters, '')))!='NaN' and translate(current(), '0123456789', '') = 'cm'">
                            <xsl:element name="{local-name()}">
                                <xsl:attribute name="oldvalue"><xsl:value-of select="."/></xsl:attribute>
                                <xsl:value-of select="number(translate(., $letters, ''))"/>
                            </xsl:element>
                        </xsl:when>
                        <xsl:when test="string(number(translate(., $letters, '')))!='NaN' and translate(current(), '0123456789', '') = 'm'">
                            <xsl:element name="{local-name()}">
                                <xsl:attribute name="oldvalue"><xsl:value-of select="."/></xsl:attribute>
                                <xsl:value-of select="number(translate(., $letters, '')*100)"/>
                            </xsl:element>
                        </xsl:when>
                        <xsl:when test="string(number(translate(., $letters, '')))!='NaN' and translate(current(), '0123456789', '') = 'in'">
                            <xsl:element name="{local-name()}">
                                <xsl:attribute name="oldvalue"><xsl:value-of select="."/></xsl:attribute>
                                <xsl:value-of select="number(translate(., $letters, '')*2.54)"/>
                            </xsl:element>
                        </xsl:when>
                        <xsl:when test="string(number(translate(., $letters, '')))!='NaN' and translate(current(), '0123456789', '') = 'px'">
                            <xsl:attribute name="oldvalue"><xsl:value-of select="."/></xsl:attribute>
                            <xsl:element name="{local-name()}">
                                <xsl:value-of select="number(translate(., $letters, '') div 37.795275591)"/>
                            </xsl:element>
                        </xsl:when>
                    </xsl:choose>
                </xsl:for-each>
            </root>
        </xsl:variable>

        <xsl:variable name="vPass1" select="ext:node-set($unsorted)"/>

        <xsl:apply-templates select="$vPass1/*"/>
    </xsl:template>

    <xsl:template match="root">
        <root>
            <xsl:for-each select="*">
                <xsl:sort select="." data-type="number" order="descending"/>
                <xsl:if test="position() = 1">
                    <highest><xsl:copy-of select="."/></highest>
                </xsl:if>
            </xsl:for-each>
        </root>
    </xsl:template>

</xsl:stylesheet>

it outputs:

<root>
    <highest>
       <c oldvalue="14m">1400</c>
    </highest>
</root>

Andere Tipps

Do you like crazily complicated expressions? As an example, suppose you only had the units "m", "cm" and "mm" to consider, you could write your sort expression like this....

<xsl:sort select="number(translate(., 'cm', '')) * 
                  (
                     (translate(., '1234567890', '') = 'mm') * 1 + 
                     (translate(., '1234567890', '') = 'cm') * 10 + 
                     (translate(., '1234567890', '') = 'm') * 1000
                  )" 
          order="descending"/> 

This takes advantage of the fact in a numeric expression, true is evaluated to 1 and false to 0. In this particular case it is converting everything into millimetres for doing the sort.

This avoids having to use a node-set, but that's probably the only thing you can say in its favour.....

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top