Search code examples
xmlawksedxmlstarlet

How to move elements in an xml file within the tree


I have an xml file of the following form:

<results>
 <sequence-name>name1</sequence-name>
 <repetitions>
  <window>
   <key1>1</key1>
  </window>
 </repetitions>
 <sequence-name>name2</sequence-name>
 <repetitions>
  <window>
   <key1>4</key1>
  </window>
  </repetitions>
</results>

I would like to move the element sequence-name such that it is the first element inside window, so the output should look like:

<results>    
 <repetitions>
  <window>
   <sequence-name>name1</sequence-name>
   <key1>1</key1>
  </window>
 </repetitions>    
 <repetitions>
  <window>
   <sequence-name>name2</sequence-name>
   <key1>4</key1>
  </window>
 </repetitions>
</results>

I tried using grep to produce two files, one containing just the lines with <sequence-name>, and one with all the other lines. But I can't figure out how to insert the lines containing <sequence-name> back into the place I would like. I'm guessing there is a solution using sed/awk. I am also happy to use a tool such as xmlstarlet.


Solution

  • The following stylesheet should do the trick:

    <?xml version="1.0"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:template match="/ | node()">
      <xsl:copy>
        <xsl:apply-templates select="node()[not(self::sequence-name)]" />
      </xsl:copy>
    </xsl:template>
    
    <xsl:template match="window">
      <xsl:copy>
        <xsl:copy-of select="preceding::sequence-name[1]" />
        <xsl:apply-templates select="key1"/>
      </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>
    

    You can apply it using xsltproc stylesheet data or xmlstarlet tr stylesheet data.