epoptai has asked for the wisdom of the Perl Monks concerning the following question:

I'm using XML::Simple to get chatterbox lines from the XML ticker. Calling XMLin($url) puts the data into a nested list. If there's only one line in the CB the data looks like this (simplified):
$data = { 'foo' => { 'one' => '1', 'two' => '2' } };
But if there's more, say two lines, we get this:
$data = { 'foo' => [{ 'one' => '1', 'two' => '2' }, { 'one' => '1', 'two' => '2' }] };
Thanks to a lesson from dkubb on references i know how to extract the data from either one, and setup some conditions to check if we have one or the other, but one still fails if the other succeeds.

If there's multiple cb lines the condition checking for one line:

if(exists $data->{'foo'}->{'one'}){ ..code.. }
fails with "Bad index while coercing array into hash"

And if there's one line the condition checking for multiple lines:

if(defined @{$data->{'foo'}}){ ..code.. }
fails with "Not an ARRAY reference".

I can see why this is happening, and pretty much understand the errors, but don't know how to properly test these conditions and escape the catch-22. Thanks in advance to whoever saves me from endless hours of "hack, hack, hack, did that work?" debugging.

Replies are listed 'Best First'.
Re: Testing Complex Data Structures
by btrott (Parson) on Feb 26, 2001 at 22:31 UTC
    You need to test whether the ref value of $data->{foo} is an arrayref: if so, it's a list of messages; otherwise it's a single message. And that's pretty easy:
    if (ref $data->{foo} eq "ARRAY") { # list } else { # single message }
    What I sometimes like to do in such situations--where I can have either an arrayref or a single value--is coerce the single value into an array ref. Then I can use the same code to deal with both situations:
    unless (ref $data->{foo} eq "ARRAY") { $data->{foo} = [ $data->{foo} ]; }

      Just a note that the ref() check will fail if the array is blessed while using UNIVERSAL::isa() doesn't have this problem (even though it is uglier code). I'll leave the choice of which consideration is more important in this case up to personal preference.

              - tye (but my friends call me "Tye")
Re: Testing Complex Data Structures
by mirod (Canon) on Feb 26, 2001 at 22:29 UTC

    You might want to use XML::Simple's forcearray option for XMLin, which will create an array even when there is only one nested element, thus removing the need for checks.

      The knowledge gained from both tye and btrott's wisdom will surely benefit me in the future, but mirod's offering seems like the right thing to do in this case. I was so focused on the lists it didn't occur to me to go back to XML::Simple's documentation for a way to alter the list. Adding forcearray to XMLin() eliminates the problem.
      my$data = XMLin($url, forcearray => 1);
      Results in the following for a single entry (note the [ and ], they're not present without forcearray):
      $data = { 'foo' => [{ 'one' => '1', 'two' => '2' }] };
      Thank you all!

      Update: I've found that sometimes you also must specify which hash to force by specifying the keyattr.
      my$data = XMLin($url, keyattr => 'foo', forcearray => 1);
(tye)Re: Testing Complex Data Structures
by tye (Sage) on Feb 26, 2001 at 22:28 UTC
    my $ref= $data->{foo}; my @elts= UNIVERSAL::isa( $ref, "ARRAY" ) ? @$ref : $ref;
            - tye (but my friends call me "Tye")