0

I have the following XML

<?xml version="1.0" encoding="UTF-8"?>  
  <Objects >
    <Item1 elemId="id1" name="view" sort_id="3">
    </Item1>
    <Item2 elemId="id3" name="view" sort_id="4" >
    </Item2>
    <Item3 elemId="id5" name="view" sort_id="2">
    </Item3>
    <Item4 elemId="id9" name="view" sort_id="1">
    </Item4>
  </Objects>

I want to sort this data by the attribute sort_id to get the following:

<?xml version="1.0" encoding="UTF-8"?>  
  <Objects >
    <Item4 elemId="id9" name="view" sort_id="1">
    </Item4>
    <Item3 elemId="id5" name="view" sort_id="2">
    </Item3>
    <Item1 elemId="id1" name="view" sort_id="3">
    </Item1>
    <Item2 elemId="id3" name="view" sort_id="4" >
    </Item2>
  </Objects>

I know that I can't do this in XML::Simple, but I heard that I can sort with XML::LibXML. I couldn't find the solution.

nwellnhof
  • 32,319
  • 7
  • 89
  • 113
tchike
  • 154
  • 4
  • 21
  • 1
    First, you need to come to terms with the fact that you won't be sorting XML: You will be parsing the XML into a data structure and produce XML sorted by your criterion. – Sinan Ünür Dec 04 '16 at 17:41
  • 3
    "I couldn't find the solution." Well, what *have* you tried so far, and what part of it didn't work? – Matt Jacob Dec 04 '16 at 19:05

1 Answers1

5

There is nothing built into XML::LibXML that specifically provides for sorting elements, but it is simple to do using the available API

Something like this will do what you want. Unfortunately XML::LibXML isn't very good at producing neat output, although it is perfectly well-formed and valid. If you want something prettier then you should look at XML::LibXML::PrettyPrint, which will do this for you

use strict;
use warnings 'all';

use XML::LibXML;

my $doc = XML::LibXML->load_xml(location => 'sort_xml.xml');

my ($objects) = $doc->findnodes('/Objects');

my @items = $objects->findnodes('*');
@items = sort {
    $a->getAttribute('sort_id') <=> $b->getAttribute('sort_id')
} @items;

$objects->removeChildNodes;
$objects->appendChild( $_ ) for @items;

print $doc;

output

<?xml version="1.0" encoding="UTF-8"?>
<Objects><Item4 elemId="id9" name="view" sort_id="1">
    </Item4><Item3 elemId="id5" name="view" sort_id="2">
    </Item3><Item1 elemId="id1" name="view" sort_id="3">
    </Item1><Item2 elemId="id3" name="view" sort_id="4">
    </Item2></Objects>

Update

To use XML::LibXML::PrettyPrint, you need to add

use XML::LibXML::PrettyPrint;

at the top of the program, and replace

print $doc;

with

my $pp = XML::LibXML::PrettyPrint->new(indent_string => "  ");
$pp->pretty_print($doc);
print $doc;

output

<?xml version="1.0" encoding="UTF-8"?>
<Objects>
  <Item4 elemId="id9" name="view" sort_id="1"/>
  <Item3 elemId="id5" name="view" sort_id="2"/>
  <Item1 elemId="id1" name="view" sort_id="3"/>
  <Item2 elemId="id3" name="view" sort_id="4"/>
</Objects>
Borodin
  • 126,100
  • 9
  • 70
  • 144