简体   繁体   中英

Sort XML nodes by child node, then sort different child nodes

I am attempting to create an XSLT file that will create a new XML from a source XML that will sort the types nodes by name, then sort the members inside of each types.

Source:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Ontario</members>
        <members>Quebec</members>
        <members>Alberta</members>
        <name>Provinces</name>
    </types>
    <types>
        <members>Vancouver</members>
        <members>Calgary</members>
        <members>Toronto</members>
        <members>Montreal</members>
        <name>Cities</name>
    </types>
    <version>43.0</version>
</Package>

Expected Output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Calgary</members>
        <members>Montreal</members>
        <members>Toronto</members>
        <members>Vancouver</members>
        <name>Cities</name>
    </types>    
    <types>
        <members>Alberta</members>
        <members>Ontario</members>
        <members>Quebec</members>
        <name>Provinces</name>
    </types>
    <version>43.0</version>
</Package>

I started by trying to sort the types by name but it doesnt seem to be sorting anything for me

XSL:

<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="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Package">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="types">
                <xsl:sort select="name"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Your script failed because you have forgotten about namespaces. Eg template matching Package refers to this name in the empty namespace, whereas your input Package element is in http://soap.sforce.com/2006/04/metadata namespace.

So the proper XSLT script should:

  • include this namespace in stylesheet tag, specyfying a prefix (eg m ),
  • refer to tag names using just this prefix.

Below you have a working script:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:m="http://soap.sforce.com/2006/04/metadata">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="m:Package">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates select="m:types">
        <xsl:sort select="m:name"/>
      </xsl:apply-templates>
      <xsl:apply-templates select="*[local-name() != 'types']"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="m:types">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates select="m:members">
        <xsl:sort select="."/>
      </xsl:apply-templates>
      <xsl:apply-templates select="m:name"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="node()|@*">
    <xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>
  </xsl:template>
</xsl:stylesheet>

As you can see, I added a template matching types , to provide sort for members , just as you specified.

<xsl:output method="xml" indent="yes"/>
    <xsl:template match="Package">
        <xsl:copy copy-namespaces="yes">
            <xsl:for-each select="types">
                <xsl:sort select="." order="descending"/>
                <types>
                    <xsl:for-each select="members">
                        <xsl:sort order="ascending"/>
                        <members>
                            <xsl:value-of select="."/>
                        </members>
                    </xsl:for-each>
                    <name>
                        <xsl:value-of select="name"/>
                    </name>
                </types>
            </xsl:for-each>
            <xsl:apply-templates select="version"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="version">
        <xsl:copy>
        <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
Check it

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM