Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert XML to XML after sorting using XSLT

Tags:

sorting

xml

xslt

I'm trying to convert XML to XML using XSLT. The output XML should be sorted based on ModificationTime element in the input XML. Below is the xml code.

<?xml version="1.0" encoding="UTF-8"?>
<Process>
<currentDayAndHour>@Fri16</currentDayAndHour>

<!-- Few elements here. Need to retain them -->


<rowCount>1</rowCount>
<currentRow>1</currentRow>


<ClientList>
 <Status>0</Status>
 <ServerResponse>
  <Code>0</Code>
  <Text>OK</Text>
</ServerResponse>
<ServiceStartTime>2012-11-09 16:06:42.786</ServiceStartTime>
<ServiceEndTime>2012-11-09 16:06:42.827</ServiceEndTime>
<Files>
  <File>
    <Name>test.20121107215230411.txt</Name>
    <Size>29</Size>
    <Type>Regular</Type>
    <Permissions>-rw-r--r--</Permissions>
    <ModificationTime>1352343152</ModificationTime>
    <Owner>19737</Owner>
    <Group>70902</Group>
  </File>
  <File>
    <Name>test.20121107183757513.txt</Name>
    <Size>29</Size>
    <Type>Regular</Type>
    <Permissions>-rw-r--r--</Permissions>
    <ModificationTime>1352331478</ModificationTime>
    <Owner>19737</Owner>
    <Group>70902</Group>
  </File>
  <File>
    <Name>test1.20121107215230500.txt</Name>
    <Size>32</Size>
    <Type>Regular</Type>
    <Permissions>-rw-r--r--</Permissions>
    <ModificationTime>1352343152</ModificationTime>
    <Owner>19737</Owner>
    <Group>70902</Group>
  </File>
  <File>
    <Name>test1.txt</Name>
    <Size>32</Size>
    <Type>Regular</Type>
    <Permissions>-rw-r--r--</Permissions>
    <ModificationTime>1352323788</ModificationTime>
    <Owner>65174</Owner>
    <Group>75431</Group>
  </File>
  <File>
    <Name>HMP_test.txt</Name>
    <Size>28</Size>
    <Type>Regular</Type>
    <Permissions>-rw-r--r--</Permissions>
    <ModificationTime>1352199478</ModificationTime>
    <Owner>19737</Owner>
    <Group>70902</Group>
  </File>
  <File>
    <Name>test1.20121107183757585.txt</Name>
    <Size>32</Size>
    <Type>Regular</Type>
    <Permissions>-rw-r--r--</Permissions>
    <ModificationTime>1352331478</ModificationTime>
    <Owner>19737</Owner>
    <Group>70902</Group>
  </File>
  <File>
    <Name>client_access.20121108101411179.txt</Name>
    <Size>4182</Size>
    <Type>Regular</Type>
    <Permissions>-rw-r--r--</Permissions>
    <ModificationTime>1352387653</ModificationTime>
    <Owner>19737</Owner>
    <Group>70902</Group>
  </File>
  <File>
    <Name>TechMtngAgenda.txt</Name>
    <Size>107</Size>
    <Type>Regular</Type>
    <Permissions>-rw-r--r--</Permissions>
    <ModificationTime>1352044842</ModificationTime>
    <Owner>19737</Owner>
    <Group>70902</Group>
  </File>
  <File>
    <Name>test.txt</Name>
    <Size>29</Size>
    <Type>Regular</Type>
    <Permissions>-rw-r--r--</Permissions>
    <ModificationTime>1350063313</ModificationTime>
    <Owner>19737</Owner>
    <Group>70902</Group>
  </File>
</Files>
</ClientList>
<currentDocument>1</currentDocument>
</Process>

I need the output XML with all the input elements but Files tag should contain each File in the increasing order of ModificationTime. I'm kindof new to XSLT. I tried using xsl:sort but not able to get desired result.

like image 971
Raj Avatar asked Dec 01 '25 12:12

Raj


1 Answers

This stylesheet will do what you want in XSLT 1.0

<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

  <xsl:template match="Files">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates select="File">
        <xsl:sort order="ascending" data-type="number" select="ModificationTime"/>
      </xsl:apply-templates>      
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

This stylesheet will do what you want in XSLT 2.0

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

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

  <xsl:template match="Files">
    <xsl:copy>
      <xsl:sequence select="@*"/>
      <xsl:perform-sort select="File">
        <xsl:sort order="ascending" select="xs:integer(ModificationTime)"/>
      </xsl:perform-sort>      
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

In both cases the Identity template is used to copy the document exactly and a single xsl:template is introduced to override the the handling of the Files element. In the XSLT 1.0 example xsl:apply-templates is used with xsl:sort to sort the File elements by ModificationTime. The <xsl:apply-templates select="File"> the handling of the sorted File elements is passed back to the Identity template so it could potentially be overridden further.

In the XSLT 2.0 example xsl:sequence is used to directly insert the input node into the result tree. Likewise xsl:perform-sort returns the sorted sequence directly rather than performing additional instructions to copy the element. Note that these changes will probably make the stylesheet faster to execute but also reduce the flexibility of future maintenance. It's much harder to add overrides when you are selecting things directly. The XSLT 1.0 stylesheet or it's style of handling could be done in XSLT 2.0 without major changes. One final note, both of these examples omit any node() children of Files that is not a File element. To capture those you could add in XSLT 1.0

<xsl:apply-templates select="node()[not(self::File)]"/>

Or in XSLT 2.0 only

<xsl:apply-templates select="node() except File"/>

or

<xsl:sequence select="node() except File"/>
like image 121
nine9ths Avatar answered Dec 03 '25 03:12

nine9ths



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!