1

I'm parsing a JSON string that is stored in a database.

{"name":"simon", "age":"23", "height":"tall"}

I'm pulling the data, then decoding. When running the code below, I'm receiving weird 'HASH' values back.

use JSON;

$data = decode_json($row->{'address'});
for my $key (keys %$data){
       if($data->{$key} ne ''){
               $XML .= "      <$key>$data->{$key}</$key>";
       }
}

// Returns data like so 

<company_type>HASH(0x27dbac0)</company_type>
<county>HASH(0x27db7c0)</county>
<address1>HASH(0x27dba90)</address1>
<company_name>HASH(0x27db808)</company_name>

The Error happens when I have a data set like so:

{"name":"", "age":{}, "height":{}}

I don't understand why JSON / Arrays / Hashes have to be so difficult to work with in Perl. What point am I missing?

Christopher Bottoms
  • 11,218
  • 8
  • 50
  • 99
Simon.
  • 1,886
  • 5
  • 29
  • 62
  • What perl module do you use for JSON? – redneb Sep 16 '16 at 09:45
  • JSON module! `use JSON`. Its a debian server – Simon. Sep 16 '16 at 09:47
  • Did you try to `print Dumper($data)` (with `use Data::Dumper`)? It works fine for me. – zdim Sep 16 '16 at 09:48
  • Try to provide a self-contained example, i.e. give us an explicit definition of `$row`, initialized with some string literal. – redneb Sep 16 '16 at 09:54
  • 1
    Oh, I see what you mean. It's not an error. In the line you show the `"age"` and `"height"` have a value of ... a hash reference, for another level of nesting (which is empty). It is probably meant to mean that there is nothing, but it is really nested data (but empty). So when you iterate over it by hand if you just print it you get `HASH(0x...)`, for a hash reference. Print it out with `Data::Dumper` and you'll see that it's fine. When you go through by-hand you'll have an extra level. – zdim Sep 16 '16 at 09:56
  • Your difficulty is not with JSON in Perl, you're difficulty is understanding what you have once the JSON is deserialized into a structure. And it seems a lack of knowledge of what prints when `{}` is the value. When you run the analog in Javascript, you get `[object Object][object Object]`, and python prints `{} {}`. Neither of which are any more helpful than `HASH(...)`. – Axeman Sep 16 '16 at 15:54
  • 2
    They are not "_difficult to work with in Perl_" -- on the contrary, JSON is one the cleanest ways to pass data around, and Perl has a simple and good support. The thing is (perhaps the "_missing point_") that after JSON is parsed you get back a complex (nested) data structure -- and you _don't know what's in it_. This is by the nature of what you're doing, loading some data. (It's the same in any language.) So you need to interrogate it. Use `Data::Dumper` to _see it_, or blindly parse nested hashes/arrays. I indicated this in my answer, let me know if more would be useful. – zdim Sep 16 '16 at 21:01

1 Answers1

5

You are processing a flat hash, while your data in fact has another, nested, hashref. In the line

{ "name":"", "age":{}, "height":{} }

the {} may be intended to mean "nothing" but are in fact JSON "object", the next level of nested data (which are indeed empty). In Perl we get a hashref for it and that's what your code prints.

The other pillar of JSON is an "array" and in Perl we get an arrayref. And that's that -- decode_json gives us back the top-level hashref, which when dereferenced into a hash may contain further hash or array references as values. If you print the whole structure with Data::Dumper you'll see that.

To negotiate this we have to test each time for a reference. Since a dereferenced hash or array may contain yet further levels (more references), we need to use either a recursive routine (see this post for an example) or a module for complex data structures. But for the first level

for my $key (keys %$data)
{
    next if $data->{$key} eq '';

    my $ref_type = ref $data->{$key};

    # if $data->{key} is not a reference ref() returns an empty string (false)
    if (not $ref_type) {
        $XML .= "      <$key>$data->{$key}</$key>";
    }
    elsif ($ref_type eq 'HASH') {
        # hashref,  unpack and parse. it may contain references
        say "$_ => $data->{$key}{$_}" for keys %{ $data->{$key} };
    }
    elsif ($ref_type eq 'ARRAY') {
        # arrayref, unpack and parse. it may contain references
        say "@{$data->{$key}}";
    }
    else { say "Reference is to type: $ref_type" }
}

If the argument of ref is not a reference (but a string or a number) ref returns an empty string, which evaluates as false, which is when you have plain data. Otherwise it returns the type the reference is to. Coming from JSON it can be either a HASH or an ARRAY. This is how nesting is accomplished.

In the shown example you are runnig into hashref. Since the ones you show are empty you can just discard them and the code for the specific example can reduce greatly, to one statement. However, I'd leave the other tests in place. This should also work as it stands with the posted example.

zdim
  • 64,580
  • 5
  • 52
  • 81