简体   繁体   中英

XSLT sort child nodes under different parent nodes

I'm trying to sort xml with xslt but the problem is that my code only sort elements under specific node, here is a sample xml:

<region name="Germany">
    <company name="Mercedes" rank="2" />
    <company name="BMW" rank="3" />
</region>
<region name="Japan">
    <company name="Toyota" rank="1" />
    <company name="Mazda" rank="4" />
</region>

I tried the folowing but it didn't work

<xsl:template match="region">
    <Companies>
        <xsl:for-each select="company">
            <xsl:sort select="@rank" />
            <xsl:call-template name="companies">
        </xsl:for-each>
    </Companies>
</xsl:template>

<xsl:template name="companies">
     <Company>
        <xsl:value-of select="@name" />
     </Company>
</xsl:template>

the output should be:

<Companies>
    <Company>Toyota</Company>
    <Company>Mercedes</Company>
    <Company>BMW</Company>
    <Company>Mazda</Company>
</Companies>

From the output it seems you want to sort by rank and not name.

Regarding the sorting happening withing the node, since your template is running for each region and so the sorting is for company nodes within a region. You could run template for parent of region and then iterate over elements and sort by name. Here is the template with the matching output.

<xsl:template match="*[region]">
    <Companies>
        <xsl:for-each select="region/company">
            <xsl:sort select="@rank" />
            <xsl:call-template name="companies" />
        </xsl:for-each>
    </Companies>
</xsl:template>

<xsl:template name="companies">
     <Company>
        <xsl:value-of select="@name" />
     </Company>
</xsl:template> 

You haven't shown any container element for those region elements but assuming you have them inside one common container write a template matching that container (eg named root in the sample code below) and then I would simply suggest to apply-templates to the company grandchildren with an xsl:sort included based on the rank attribute.

Then add a template transforming from the attribute based company input elements to a value based element and you are done:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

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

  <xsl:template match="root">
      <Companies>
          <xsl:apply-templates select="region/company">
              <xsl:sort select="xs:integer(@rank)"/>
          </xsl:apply-templates>
      </Companies>
  </xsl:template>

  <xsl:template match="company">
      <xsl:copy>
          <xsl:value-of select="@name"/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/94rmq6B

Matching on each region doesn't seem to make sense if you want to process them all together in a sorted way.

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