Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

What is correct way to reference?

by BradV (Sexton)
on May 22, 2017 at 17:13 UTC ( [id://1190864]=perlquestion: print w/replies, xml ) Need Help??

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

I'm writing some code to parse some xml files. One element could either have a single value or an array of values. I have:

if ( ref($xmlcollection->{phone}->{callTo}) eq "ARRAY" ) { printf( "Call To:" ); foreach ($xmlcollection->{phone}->{callTo}) { print "$_\n"; } } else { printf( "Call To: %s\n", $xmlcollection->{phone}->{callTo}); }

It just prints the array reference. If I add:

printf( "%s\n",$xmlcollection->{phone}->{callTo}[0]); printf( "%s\n", $xmlcollection->{phone}->{callTo}[1]);

it prints the correct values. The problem is I don't always know how many elements will be in the array. How do I correctly reference the array element of the hash in order to print out all of the values?

Replies are listed 'Best First'.
Re: What is correct way to reference?
by Eily (Monsignor) on May 22, 2017 at 17:19 UTC

    You can find more information on references in perlref and in the References tutorials.

    Here what you need is get the array from the reference, which is called dereferencing. This can be done either as: foreach (@{ $xmlcollection->{phone}->{callTo} }) or foreach ($xmlcollection->{phone}->{callTo}->@*). The latter is only available in recent versions of perl (by default in v5.24, or as an optional feature in v5.20 and v5.22, see Postfix Dereference Syntax).

Re: What is correct way to reference?
by haukex (Archbishop) on May 22, 2017 at 17:51 UTC
    One element could either have a single value or an array of values.

    I agree with Your Mother, sounds like you might be using XML::Simple. I have to strongly recommend against this module (as its own documentation does, see the section "Status of this Module"), except for maybe the very simplest of cases, of which this is probably not one. Instead, look into other XML parsing modules, such as XML::LibXML, XML::Twig, or XML::Rules, which can output data structures much like XML::Simple:

    use warnings; use strict; use Data::Dump; use XML::Rules (); my $XML1 = '<root><phone><callTo>X</callTo></phone></root>'; my $XML2 = '<root><phone><callTo>Y</callTo> <callTo>Z</callTo></phone></root>'; my $parser = XML::Rules->new( stripspaces => 3|4, rules => [ root => 'pass', phone => 'as is', callTo => 'content array', _default => sub { die "Unknown tag $_[0]" }, ] ); dd $parser->parse($XML1); dd $parser->parse($XML2); __END__ { phone => { callTo => ["X"] } } { phone => { callTo => ["Y", "Z"] } }

    As for XML::Simple, note its ForceArray option:

    use XML::Simple qw/:strict XMLin/; dd XMLin($XML1, KeyAttr=>{}, ForceArray=>[]); dd XMLin($XML2, KeyAttr=>{}, ForceArray=>[]); dd XMLin($XML1, KeyAttr=>{}, ForceArray=>['callTo']); __END__ { phone => { callTo => "X" } } { phone => { callTo => ["Y", "Z"] } } { phone => { callTo => ["X"] } }

    Lastly, to answer your original question, if you have a scalar that holds either a single value or an array reference, one way to handle that is like the following. However, it should be clear from the above that this is not the best way to fix your problem.

    foo( "one" ); foo( ["two", "three"] ); sub foo { my $x = shift; # the following does the trick my @values = ref $x eq 'ARRAY' ? @{$x} : $x; for my $val (@values) { print "<$val>\n"; } } __END__ <one> <two> <three>

    Update: Refactored last code example slightly to make the solution more clear. Update 2: Used callTo => 'content array' instead of 'as array' to make XML::Rules output identical to XML::Simple.

Re: What is correct way to reference?
by Your Mother (Archbishop) on May 22, 2017 at 17:30 UTC
Re: What is correct way to reference?
by BillKSmith (Monsignor) on May 22, 2017 at 20:27 UTC
    After you have solved the parse problem, you still have to print and array of an unknown number of strings. Use print rather than printf.
    { local $, = "\n"; my $ref = $xmlcollection->{phone}->{callTo}; print @{$ref}, "\n"; }
    Bill
      OK, yes using XML::Simple. Will look at moving up. I've run into another problem where it is having more problems. Got to get the right tool. :) thanks for all the replies!
Re: What is correct way to reference?
by shmem (Chancellor) on May 22, 2017 at 17:43 UTC
Re: What is correct way to reference?
by 1nickt (Canon) on May 22, 2017 at 18:41 UTC

    Hi, see what the Monks said about using the right tools to work with your XML.

    But in general, one way to do what you want when you don't know whether you have a simple scalar value or a reference to an array, without a loop or multiple print statements, is to use the default list separator, stored in the special global var $", and just interpolate the list, after you extract it based on your ref check:

    perl -Mstrict -wE ' my $foo = "bar"; my $baz = [ "qux", "quux" ]; # we have both kinds local $" = "\n"; # change list separator to a newline say ( ref $_ eq "ARRAY" ? "@$_" : $_ ) for ( $foo, $baz ); '
    Output:
    bar qux quux

    Hope this helps!


    The way forward always starts with a minimal test.
          local $" = "\n";             # change list separator to a newline

          say ( ref $_ eq "ARRAY" ? "@$_" : $_ ) for ( $foo, $baz );

      Wouldn't it be easier/quicker/clearer/more informative to use something like Data::Dumper::Dumper() (which is core) or Data::Dump::dd() (not core, but I like it a lot) to dump a scalar or reference and see exactly what its value or structure is? Why go through such specialized acrobatics?


      Give a man a fish:  <%-{-{-{-<

        Yes, of course! This is a completely contrived exercise to show $" ... simply for edification.

        The way forward always starts with a minimal test.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1190864]
Approved by Eily
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (5)
As of 2024-05-18 01:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found