srowley
srowley

Reputation: 837

Perl, Parsing XML with XML::Simple and Having Issues

I am parsing xml files in Perl and everything seems to work great with one issue. I have files with the same schema, but they are returning different types of data from the parser. Here is a simplified example:

<tests>
       <test>
          <data1>Hi</data1>
          <data2>Hello</data2>
       </test>
       <test>
          <data1>Hi2</data1>
          <data2>Hello2</data2>
       </test>
  </tests>

In a dump, this returns the following: (Take note of test being an array of two hashes)

$VAR1 = {
          'test' => [
                    {
                      'data2' => 'Hello',
                      'data1' => 'Hi'
                    },
                    {
                      'data2' => 'Hello2',
                      'data1' => 'Hi2'
                    }
                  ]
        };

Now, for a similar set of data, but with only one 'test' entity like so:

  <tests>
       <test>
          <data1>Hi</data1>
          <data2>Hello</data2>
       </test>
  </tests>

This returns similar data, EXCEPT the test entity is no longer an array, but a singular hash:

$VAR1 = {
          'test' => {
                    'data2' => 'Hello',
                    'data1' => 'Hi'
                  }
        };

My dilemma is that my code expects an array there, as that is the norm. But on the slim chance when there is only one entity, it will return a hash for that entity. My question is, how can I handle the hash entity as though it were an array. Or test for it?

Right now my code for retrieving the array is like this:

foreach $test (@{$data->{'tests'}->{'test'}})
{
   do something with $test
}

But with the hash, it gives an error "Not an ARRAY reference". I hope this is enough detail! Thanks!!!

Upvotes: 1

Views: 3130

Answers (4)

zgpmax
zgpmax

Reputation: 2857

Whilst it appears that you could get the XML parser to behave more consistently, it would also not be difficult to make your code work on the variant output.

The Perl built-in function "ref" can be used to determine what type of object a reference refers to.

Your original code goes

foreach $test (@{$data->{'tests'}->{'test'}})
{
    do something with $test
}

(Rather than write $data->{'tests'}->{'test'}, I would tend to use the more compact $$data{tests}{test} , so I'll use that in my example.)

We could check the reference type and use that to push all possibilities into an array, so

foreach $test (
    (ref($$data{tests}{test}) eq 'ARRAY') ? (
        @{$$data{tests}{test}}
    ) : (
        $$data{tests}{test}
    )
)
{
    do something with $test
}

Upvotes: 0

Jonah Bishop
Jonah Bishop

Reputation: 12591

Perhaps the alternate form of the ForceArray option is what you want?

ForceArray => [ names ]

This alternative (and preferred) form of the 'ForceArray' option allows you to specify a list of element names which should always be forced into an array representation, rather than the 'all or nothing' approach above.

It is also possible (since version 2.05) to include compiled regular expressions in the list - any element names which match the pattern will be forced to arrays. If the list contains only a single regex, then it is not necessary to enclose it in an arrayref. Eg:

ForceArray => qr/_list$/

So I might try:

ForceArray => ['test']

Upvotes: 6

toolic
toolic

Reputation: 62236

XML::Simple

ForceArray => 1

This option should be set to '1' to force nested elements to be represented as arrays even when there is only one

Upvotes: 1

ennuikiller
ennuikiller

Reputation: 46985

You need to dereference the hash by using the hash sigil: '%'.

Upvotes: 0

Related Questions