Question

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!!!

Was it helpful?

Solution

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']

OTHER TIPS

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

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

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
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top