Search code examples
xmlxslt

xslt sort only selected node


transform and sort for selected nodes seems to be not working, for example:

when below XSLT:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  expand-text="yes">

  <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="/" name="xsl:initial-template">
    <xsl:next-match/>
    <xsl:comment xmlns:saxon="http://saxon.sf.net/">Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</xsl:comment>
  </xsl:template>
  
  <xsl:template match="array">
    <xsl:copy>
      <xsl:apply-templates select="map">
        <xsl:sort select="translate(string[@key='usdt'], '-T:', '')" data-type="number" order="descending"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

is applied to the XML:

<map xmlns="http://www.w3.org/2005/xpath-functions">
    <map key="msgHeader">
        <number key="msgNumber">1</number>
        <string key="msgDateTime">2024-05-30T04:43:34.545</string>
        <string key="msgAction">mm</string>
    </map>
    <null key="refId"/>
    <null key="associateId"/>
    <string key="tt">nn</string>
    <string key="iac">oo</string>
    <null key="agm"/>
    <map key="am">
        <string key="ac">nn</string>
        <string key="fn">oo</string>
        <null key="os"/>
        <number key="rias">1</number>
    </map>
    <null key="dgm"/>
    <map key="dm">
        <string key="ac">nn</string>
        <string key="fn">oo</string>
        <null key="os"/>
        <number key="rias">1</number>
    </map>
    <array key="p">
        <map>
            <string key="s">D1</string>
            <string key="usdt">2024-05-30T04:43:34</string>
        </map>
        <map>
            <string key="s">D1</string>
            <string key="usdt">2024-05-30T10:55:05</string>
        </map>
        <map>
            <string key="s">D1</string>
            <string key="usdt">2024-05-30T03:45:06</string>
        </map>
        <map>
            <string key="s">D1</string>
            <string key="usdt">2024-05-30T18:33:25</string>
        </map>
    </array>
    <array key="t">
        <map>
            <string key="dps">D1</string>
            <string key="sddt">2024-05-30T08:43:34</string>
        </map>
        <map>
            <string key="dps">D1</string>
            <string key="sddt">2024-05-30T11:55:05</string>
        </map>
        <map>
            <string key="dps">D1</string>
            <string key="sddt">2024-05-30T14:45:06</string>
        </map>
        <map>
            <string key="dps">D1</string>
            <string key="sddt">2024-05-30T19:33:25</string>
        </map>
    </array>
</map>

the result XML should be sorted like:

<array key="p">
    <map>
        <string key="s">D1</string>
        <string key="usdt">2024-05-30T03:45:06</string>
    </map>
    <map>
        <string key="s">D1</string>
        <string key="usdt">2024-05-30T04:43:34</string>
    </map>
    <map>
        <string key="s">D1</string>
        <string key="usdt">2024-05-30T10:55:05</string>
    </map>
    <map>
        <string key="s">D1</string>
        <string key="usdt">2024-05-30T18:33:25</string>
    </map>
</array>

Somehow the output is not sorted, Any idea why the sorting is not working in this case? Link to fiddle


Solution

  • You have a namespace issue. You need to add a xpath-default-namespace="http://www.w3.org/2005/xpath-functions" declaration to your stylesheet, otherwise the <xsl:template match="array"> will not be applied.

    And you could probably simplify the code to:

    <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xpath-default-namespace="http://www.w3.org/2005/xpath-functions"
    exclude-result-prefixes="#all">
    <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>
      
    <xsl:mode on-no-match="shallow-copy"/>
    
    <xsl:template match="array">
        <xsl:copy>
            <xsl:apply-templates select="map">
                <xsl:sort select="xs:dateTime(string[@key='usdt'])" order="descending"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>