http://www.perlmonks.org?node_id=1194430

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

Hello and thank you again for your time.

I have been trying to figure this out for a few hours now. Arrays, hashes, array of hashes, hash of arrays, etc., they have always been confusing for me.

I am reading a small XML file with 6 values.

$VAR1 = { 'version' => '2.0', 'xmlns:media' => 'http://search.yahoo.com/mrss/', 'fileSet' => { 'time' => '1499387013', 'fileName' => 'Atlanta_Regency.gif', 'emailAddress' => 'johndoe@gmail.com', 'requesterName' => 'John Doe', 'showAbbreviation' => {}, 'zoneColor' => '0' } };

Only 'showAbbreviation' may or may not be empty. All others will always have a value. I've tried this...

my $showAbbreviation = $current->{'fileSet'}->{'showAbbreviation'}; if ( not defined $showAbbreviation ) { $showAbbreviation = ""; }

And this...

if (exists $current->{'fileSet'}->{'showAbbreviation'} ) { $showAbbreviation = $current->{'fileSet'}->{'showAbbreviation'}; print "Yes: $showAbbreviation\n"; } else { $showAbbreviation = ""; print "No: $showAbbreviation\n"; }

I thought one of those would do the trick, but it is giving me back a 'HASH(0xd6a9690)' type value instead of an empty String value. I've tried exists and defined and not defined etc., but nothing is giving me the response I want.

What am I missing? This should be fairly easy I would think. Please help me understand, and thank you!

Replies are listed 'Best First'.
Re: If hash not defined help
by kcott (Archbishop) on Jul 07, 2017 at 11:45 UTC

    G'day johnfl68,

    "Arrays, hashes, array of hashes, hash of arrays, etc., they have always been confusing for me. ... What am I missing?"

    While you may be having some difficulty with certain data structures, I suspect your underlying problem is in the areas of data types, their references and the commands to use to manage them. This is particularly borne out by comments about getting HASH(0xd6a9690) instead of a string and, in a separate reply, "Can't use string ... as a HASH ref".

    If you haven't already read perlintro, I suggest you do. If you have read it, I'd recommend revising the "Perl variable types" section. Don't be put off by terms such as "introductory" and "beginner"; this particular piece of documentation is peppered with links to more detailed information and advanced topics: it's an excellent stepping-stone when researching some specific area and you're not sure where to start. The section I pointed out has multiple links: for your immediate needs, perhaps start with perlreftut and perldata.

    I put together the following script, which uses a data structure similar to yours, but which has a whole range of data types and references. It shows how each can be examined: is it defined? is it a reference? what sort of reference? does it have content? how to dereference? The first half of the examples are likely to be things you're dealing with generally; the second half are somewhat more advanced (perhaps come back to them at a later date).

    #!/usr/bin/env perl -l use strict; use warnings; use IO::Handle; my $complex_data_hash_ref = { not_used_1 => '', example => { undefined => undef, empty_string => '', non_empty_string => 'string', empty_array_ref => [], non_empty_array_ref => [ 1, 2, 3 ], empty_hash_ref => {}, non_empty_hash_ref => { x => 24, y => 25, z => 26 }, scalar_ref => \42, glob_ref => \*STDOUT, code_ref => sub { print "\n\t\tHello, world!\n" }, ref_ref => \\99, blessed_ref => IO::Handle::->new(), ref_ref_ref => \\\999, regex => qr{match}, vstring => v65, vstring_ref => \v65, }, not_used_2 => '', }; my @example_keys_to_parse = qw{ undefined empty_string non_empty_string empty_array_ref non_empty_array_ref empty_hash_ref non_empty_hash_ref glob_ref code_ref ref_ref blessed_ref ref_ref_ref regex vstring vstring_ref }; for my $example_key (@example_keys_to_parse) { my $example_key_value = $complex_data_hash_ref->{example}{$example_key}; examine_data($example_key, $example_key_value); } sub examine_data { my ($in_key, $in_value) = @_; print "Examining '$in_key':"; if (ref $in_value) { print "\tThe value of '$in_key' is a reference."; print "\tThe value of '$in_key' is '$in_value'."; my $ref = ref $in_value; print "\tThe ref() value of '$in_key' is '$ref'."; if ($ref eq 'SCALAR') { my $deref = ${$in_value}; print "\tIts dereferenced value is '$deref'."; } elsif ($ref eq 'ARRAY') { my @deref = @{$in_value}; if (@deref) { print "\tIts dereferenced value has these elements: ", "'@deref'."; } else { print "\tIts dereferenced value has no elements."; } } elsif ($ref eq 'HASH') { my %deref = %{$in_value}; my @deref_keys = keys %deref; if (@deref_keys) { print "\tIts dereferenced value has these keys: ", "'@deref_keys'."; my @deref_values = values %deref; print "\tIts dereferenced value has these values: ", "'@deref_values'."; } else { print "\tIts dereferenced value has no key-value pairs +."; } } elsif ($ref eq 'GLOB') { print $in_value "\tA globref can be a filehandle opened fo +r writing."; } elsif ($ref eq 'CODE') { $in_value->(); } elsif ($ref eq 'REF') { examine_data('dereferenced__' . $in_key, ${$in_value}); } else { print "\tTODO: Further work on this type ", 'is left as an exercise for the reader.'; } } else { print "\tThe value of '$in_key' is not a reference."; if (defined $in_value) { print "\tThe value of '$in_key' is defined."; if (length $in_value) { print "\tThe value of '$in_key' is '$in_value'."; } else { print "\tThe value of '$in_key' is zero-length."; } } else { print "\tThe value of '$in_key' is not defined."; } } }

    Notes:

    • IO::Handle was only used to generate a value for "blessed_ref". It has no significance beyond the fact that its new() method returns a blessed reference.
    • While I've used ref() throughout; do read its documentation, particularly with respect to the reftype() function of Scalar::Util often being a better choice.
    • I've shown dereferencing as ${$ref}, @{$ref}, and so on. In simple situations like that, the braces are not necessary (i.e. $$ref, @$ref, etc. are fine); however, for more complex code, I recommend you use them (e.g. @{$obj->get_aref($h{x})}). If you're using a recent Perl version, consider Postfix Dereference Syntax (I personally think this makes the code easier to read, e.g. $obj->get_aref($h{x})->@*).
    • I've kept the syntax as simple as possible for didactic reasons. In a number of cases, I probably wouldn't normally be so verbose; you needn't be either.
    • You can find documentation for the functions I've used via Perl functions A-Z.

    The output is rather lengthy. It's in the spoiler, below. You may find it's a useful exercise to determine what you think the output of each example is before looking.

    — Ken

Re: If hash not defined help
by huck (Prior) on Jul 07, 2017 at 02:10 UTC

    use strict; use warnings; use Data::Dumper; { my $current= { 'version' => '2.0', 'xmlns:media' => 'http://search.yahoo.com/mrss/', 'fileSet' => { 'time' => '1499387013', 'fileName' => 'Atlanta_Regency.gif', 'emailAddress' => 'johndoe@gmail.com', 'requesterName' => 'John Doe', 'showAbbreviation' => {}, 'zoneColor' => '0' } }; if (exists($current->{'fileSet'}->{'showAbbreviation'}) && defined ($current->{'fileSet'}->{'showAbbreviation'}) && ( scalar(keys %{$current->{'fileSet'}->{'showAbbreviation'}}) > +0) ) { my $showAbbreviation = $current->{'fileSet'}->{'showAbbreviation +'}; print "Yes: showAbbreviation\n"; print Dumper($showAbbreviation)."\n"; } else { print "No: showAbbreviation\n"; } } { my $current= { 'version' => '2.0', 'xmlns:media' => 'http://search.yahoo.com/mrss/', 'fileSet' => { 'time' => '1499387013', 'fileName' => 'Atlanta_Regency.gif', 'emailAddress' => 'johndoe@gmail.com', 'requesterName' => 'John Doe', 'showAbbreviation' => {a=>1}, 'zoneColor' => '0' } }; if (exists($current->{'fileSet'}->{'showAbbreviation'}) && defined ($current->{'fileSet'}->{'showAbbreviation'}) && ( scalar(keys %{$current->{'fileSet'}->{'showAbbreviation'}}) > +0) ) { my $showAbbreviation = $current->{'fileSet'}->{'showAbbreviation +'}; print "Yes: showAbbreviation\n"; print Dumper($showAbbreviation)."\n"; } else { print "No: showAbbreviation\n"; } }

      johnfl68: If  $current->{'fileSet'}{'showAbbreviation'} could exist and be defined and yet be something other than a hash reference, it might be wise to add a test for hash-refitude to the other tests proposed by huck (attempting to dereference a non-hashref as a hash is fatal under strict refs, which you should, of course, have enabled :) (untested | since tested):
          if (exists($current->{'fileSet'}->{'showAbbreviation'})
              && defined ($current->{'fileSet'}->{'showAbbreviation'})
              && 'HASH' eq ref($current->{'fileSet'}->{'showAbbreviation'})
              && ( scalar(keys %{$current->{'fileSet'}->{'showAbbreviation'}}) >0)
              ) {
              ...
              }

      Update: In fact, since ref returns the empty string for an undefined argument, the explicit defined test is not necessary, making the whole thing a bit less messy:
          if (exists($current->{'fileSet'}->{'showAbbreviation'})
              && 'HASH' eq ref($current->{'fileSet'}->{'showAbbreviation'})
              && ( scalar(keys %{$current->{'fileSet'}->{'showAbbreviation'}}) >0)
              ) {
              ...
              }


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

      Thank you. That helps now for when showAbbreviation is empty, but unfortunately if it has a value in it, for example 'Test', I get this error now...

      Can't use string ("Test") as a HASH ref while "strict refs" in use.

      I give up for tonight.

        Can't use string ("Test") as a HASH ref ...

        Maybe take a look at this.


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

Re: If hash not defined help
by haukex (Archbishop) on Jul 07, 2017 at 11:52 UTC

    It sounds like you might be using XML::Simple. I would not recommend that module except for the very simplest of XML inputs, of which your XML is apparently not one, because the data structures it outputs are often unreliable, which would be exactly the problem you are experiencing. As a replacement I could suggest XML::Rules, which provides more reliable data structures as its output. Other good modules for reading XML are XML::LibXML (which supports XPath expressions) and XML::Twig.

    Update: Added the bit about unreliable data structures.

      Yes, I am using XML::Simple as I am on a shared server so I can't install modules. I've been using for years for a few other things, and it has been less of a headache on more complex XML. It was supposed to be a Simple XML solution, I don't understand why it is being a pain on these small file sets.

      I think XML::LibXML is also available, I will look at moving to that in the long term.

      As for the short answer, I'm about to just read each line one at a time and regex out the XML to get what I need for this, since it is so small. I know that is not really the right answer, but it will serve the purpose this time.

      "I have not failed 10,000 times. I have successfully found 10,000 ways that will not work." -- Thomas Edison

      Thanks to everyone for their help and feedback.