tags:

views:

77

answers:

2

I am using XML::Simple for parsing a XML file. Code is given below with XML file,

use Tie::IxHash;

tie %$data, "Tie::IxHash";

use XML::Simple;
use Data::Dumper;

$xml = new XML::Simple;
$data = $xml->XMLin("ship_order.xml");
print Dumper($data);

XML file, (ship_order.xml)

<?xml version="1.0" encoding="UTF-8" ?> 

<shipment>
    <shiptoaddress>
        <name>Prasad</name> 
        <address>AnnaNagar</address> 
    </shiptoaddress> 
    <items>
        <quantity>5</quantity> 
        <price>100</price> 
    </items> 
    <items>
        <quantity>6</quantity> 
        <price>50</price> 
    </items>
    <num_of_items>2</num_of_items>
</shipment>

Output is not coming in order, even though I am using Tie::IxHash module.

My output:

$VAR1 = {
          'num_of_items' => '2',
          'shiptoaddress' => {
                             'name' => 'Prasad',
                             'address' => 'AnnaNagar'
                           },
          'items' => [
                     {
                       'quantity' => '5',
                       'price' => '100'
                     },
                     {
                       'quantity' => '6',
                       'price' => '50'
                     }
                   ]
        };
+6  A: 

Ah, but you aren't using Tie::IxHash. Or more accurately, you start off using Tie::IxHash and then destroy it:

$data = $xml->XMLin("ship_order.xml");

This line discards the hash reference you created and assigns one from the method call to $data.

If you care about the order of the items (and you probably shouldn't have to since any decent XML format will include an attribute that tells you the order), you will need to use a parser that returns an object, not a data structure. The object will know the order the items were seen and provide you with a children method that returns them.

Alternatively, you could build the data structure yourself:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;

my $shipment;
my $t = XML::Twig->new(
    twig_handlers => {
        shiptoaddress => sub {
            my ($t, $elt) = @_;

            $shipment->{name}    = $elt->first_child("name")->text;
            $shipment->{address} = $elt->first_child("address")->text;

            $t->purge;
        },
        items => sub {
            my ($t, $elt) = @_;

            push @{$shipment->{items}}, {
                quantity => $elt->first_child("quantity")->text,
                price    => $elt->first_child("price")->text,
            };

            $t->purge;
        },
    },
);

$t->parse(join "", <DATA>); #FIXME: use parsefile later

use Data::Dumper;
print Dumper $shipment;

__DATA__
<?xml version="1.0" encoding="UTF-8" ?> 

<shipment>
    <shiptoaddress>
        <name>Prasad</name> 
        <address>AnnaNagar</address> 
    </shiptoaddress> 
    <items>
        <quantity>5</quantity> 
        <price>100</price> 
    </items> 
    <items>
        <quantity>6</quantity> 
        <price>50</price> 
    </items>
    <num_of_items>2</num_of_items>
</shipment>
Chas. Owens
+1  A: 

You could consider subclassing XML::Simple and overwritting necessary method that creates the hash with Tie::IxHash.

However seriously consider this answer given by the author of XML::Simple on the CPAN forum in this thread: how to preserve XML::Simple element order...

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 - as described in [1]

And also what he put in his code:

##############################################################################
# Method: new_hashref()
#
# This is a hook routine for overriding in a sub-class.  Some people believe
# that using Tie::IxHash here will solve order-loss problems.
# 

sub new_hashref {
  my $self = shift;

  return { @_ };
}

/I3az/

[1] - Stepping up from XML::Simple to XML::LibXML

draegtun