Search code examples
xmlsortingxslt

XSLT ordered list syntax for values not on the list


Thanks to this site, I have been able to figure out how to create a specificed order sorting on a child element(I do not work often with XSLT).

The issue I am having is when something is not on that list, It no longer respects any sorting.

The desired result is to have 5 defined child elements are always at the top in a specific order and anything else would come after as there are hundreds of possibilities' and more are created daily. The sorting for anything after does not matter, it is just important that the 5 defined are always on top.

XML Example when all element values are listed:

<Root>
    <Parent>
        <Child>
            <FieldValue>SAMPLE1</FieldValue>
        </Child>
    </Parent>
    <Parent>
        <Child>
            <FieldValue>SAMPLE2</FieldValue>
        </Child>
    </Parent>
        <Parent>
        <Child>
            <FieldValue>SAMPLE3</FieldValue>
        </Child>
    </Parent>
    <Parent>
        <Child>
            <FieldValue>SAMPLE4</FieldValue>
        </Child>
    </Parent>       
</Root>

XSLT

<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:param name="pSortingValues" select="'SAMPLE2,SAMPLE1,SAMPLE4,SAMPLE3'"/>
 <xsl:variable name="vSortingValues" select=
  "concat(',', $pSortingValues, ',')"/>

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

    <xsl:template match="/*">
      <xsl:copy>
       <xsl:apply-templates select="@*"/>
       <xsl:apply-templates select="*">
        <xsl:sort data-type="number" select=
        "string-length(substring-before($vSortingValues,concat(',',Child/FieldValue,',')))"/>
       </xsl:apply-templates>
      </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Result

<Root>
   <Parent>
      <Child>
         <FieldValue>SAMPLE2</FieldValue>
      </Child>
   </Parent>
   <Parent>
      <Child>
         <FieldValue>SAMPLE1</FieldValue>
      </Child>
   </Parent>
   <Parent>
      <Child>
         <FieldValue>SAMPLE4</FieldValue>
      </Child>
   </Parent>
   <Parent>
      <Child>
         <FieldValue>SAMPLE3</FieldValue>
      </Child>
   </Parent>
</Root>

Any ideas would be appreciated, I have not been successful so far. My attempts at using variables and if statements have failed spectacularly


Solution

  • Another possibility:

    <xsl:template match="/Root">
        <xsl:copy>
            <xsl:apply-templates select="Parent">
                <xsl:sort select="contains($vSortingValues, concat(',', Child/FieldValue, ','))" order="descending"/>
                <xsl:sort select="string-length(substring-before($vSortingValues, concat(',', Child/FieldValue, ',')))" data-type="number" />
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>