In the FAQ, it is mentioned that in the future Tie::IxHash could be use in XML::Simple. I am using v2.18 at the moment, is there any hack to preserve the order of the data in the hashref?

To the above question Grant McLean, the author of XML::Simple, answered:

Retaining element order is not and never will be a feature of XML::Simple. For some XML document types you might be able to hack it in by subclassing XML::Simple and overriding the new_hashref() method to supply a hashref tied to Tie::IxHash. That could solve the ABC case but it won't solve the ABA case.

The short answer is that if you care about element order then you should not use XML::Simple. XML::LibXML is an excellent alternative which for many use cases is really no harder to use than XML::Simple.

Sample XML file with ABA

In this example we have 3 elements inside the "order" root element. If I am not mistaken, that's what Grant referred to when he wrote 'ABA' case.

examples/order.xml

<order>
  <pizza>
    <name>Margarita</name>
    <size>large</size>
  </pizza>
  <drink>
    <name>Coke</name>
    <size>1.5 L</size>
  </drink>
  <pizza>
    <name>Funghi</name>
    <size>XXL</size>
  </pizza>
</order>

Using XML::Simple we can read in this XML file and immediately print it out.

examples/xml_simple_order.pl

use strict;
use warnings;
use XML::Simple qw(XMLin XMLout);

my $xml = XMLin('order.xml', ForceArray => 1, KeepRoot => 1);
print XMLout($xml, KeepRoot => 1);

The result is this:

examples/order_out.xml

<order>
  <drink>
    <name>Coke</name>
    <size>1.5 L</size>
  </drink>
  <pizza>
    <name>Margarita</name>
    <size>large</size>
  </pizza>
  <pizza>
    <name>Funghi</name>
    <size>XXL</size>
  </pizza>
</order>

The order of the elements has changed.

This might be acceptable in some cases, but in other cases the order of the elements might be important.

Dumping the XML as a Perl data structure

So why has XML::Simple messed up the order? The answer is in how it holds the data after reading the XML file. We can use Data::Dumper to see the content of the $xml variable:

examples/xml_simple_order_dump.pl

use strict;
use warnings;
use XML::Simple qw(XMLin XMLout);
use Data::Dumper qw(Dumper);


my $xml = XMLin('order.xml', ForceArray => 1, KeepRoot => 1);
print Dumper $xml;

The output looks like this:

examples/order_dump_out.txt

$VAR1 = {
          'order' => [
                     {
                       'pizza' => [
                                  {
                                    'size' => [
                                              'large'
                                            ],
                                    'name' => [
                                              'Margarita'
                                            ]
                                  },
                                  {
                                    'size' => [
                                              'XXL'
                                            ],
                                    'name' => [
                                              'Funghi'
                                            ]
                                  }
                                ],
                       'drink' => [
                                  {
                                    'name' => [
                                              'Coke'
                                            ],
                                    'size' => [
                                              '1.5 L'
                                            ]
                                  }
                                ]
                     }
                   ]
        };

Here we can see that XML::Simple has merged the content of the two 'pizza' tags into one hash. From this data structure we cannot rebuild the original order of pizza-drink-pizza.

Is this a bug in XML::Simple? You can say so, or you can say that it was a design decision. In either way Grant, the author of the module is not planning to change this behaviour and actually recommends agains the use of XML::Simple for new projects. Back when XML::Simple was created it was a good solution for a lot of problems, but today there are other solutions. He recommended XML::LibXML.

Let's see how this round-trip works using XML::LibXML.

Round-trip with XML::LibXML

examples/xml_libxml_order.pl

use strict;
use warnings;
use XML::LibXML;

my $dom = XML::LibXML->load_xml(location => 'order.xml');
print $dom->toString();

Running this code we'll get the XML back exactly in the same order as we had in the original file.

(This article was rescued from CPAN::Forum)