bdizzle
bdizzle

Reputation: 419

creating hash from variables to be used in json encode/decode perl

i'm trying to create a test module to test json encoding. i am having issues creating variables that will output correctly with the json encode/decode. if i use just the $cat_1 in the @cats array, it will work fine. however, using both, it prints out "HASH(..." as you can see below.

use strict;
use JSON;
use Data::Dump qw( dump );

my $cat_1 = {'name' => 'cat1', 'age' => '6', 'weight' => '10 kilos', 'type' => 'siamese'};
my $cat_2 = {'name' => 'cat2', 'age' => '10', 'weight' => '13 kilos', 'type' => 'siamese'};

my @cats;
push(@cats, $cat_1);
push(@cats, $cat_2);

my $dog_1 = {'name' => 'dog1', 'age' => '7', 'weight' => '20 kilos', 'type' => 'siamese'};
my $dog_2 = {'name' => 'dog2', 'age' => '5', 'weight' => '15 kilos', 'type' => 'siamese'};

my @dogs;
push(@dogs, $dog_1);
push(@dogs, $dog_2);

my $pets = {'cats' => @cats, 'dogs' => @dogs};

my $a = { 'id' => '123',    'name' => 'Joe Smith',  'city' => "Chicago", 'pets' => $pets    };

my $json = JSON->new->allow_nonref;
my $encoded = $json->encode($a);
my $decoded = $json->decode( $encoded );

print "\ndump cat_1\n";
dump $cat_1;
print "\ndump cats\n";
dump @cats;

print "\n\nOriginal\n";
dump $a;
print "\n\n";

print "Encoded\n";
print $encoded;
print "\n\n";

print "Decoded\n";
dump $decoded;
print "\n\n";

output

dump cat_1
{ age => 10, name => "cat1", type => "siamese", weight => "10 kilos" }

dump cats
(
  { age => 10, name => "cat1", type => "siamese", weight => "10 kilos" },
  { age => 10, name => "cat2", type => "siamese", weight => "3 kilos" },
)


Original
{
  city => "Chicago",
  id => 123,
  name => "Joe Smith",
  pets => {
    "cats" => { age => 10, name => "cat1", type => "siamese", weight => "10 kilos" },
    "HASH(0x176c3170)" => "dogs",
    "HASH(0x1785f2d0)" => { age => 10, name => "dog2", type => "siamese", weight => "3 kilos" },
  },
}


Encoded
{"city":"Chicago","pets":{"HASH(0x1785f2d0)":{"weight":"3     kilos","name":"dog2","type":"siamese","age":"10"},"cats":{"weight":"10 kilos","name":"cat1","type":"siamese","age":"10"},"HASH(0x176c3170)":"dogs"},"name":"Joe Smith","id":"123"}

Decoded
{
  city => "Chicago",
  id => 123,
  name => "Joe Smith",
  pets => {
    "cats" => { age => 10, name => "cat1", type => "siamese", weight => "10 kilos" },
    "HASH(0x176c3170)" => "dogs",
    "HASH(0x1785f2d0)" => { age => 10, name => "dog2", type => "siamese", weight => "3     kilos" },
  },
}

Upvotes: 1

Views: 2238

Answers (2)

Mark Grimes
Mark Grimes

Reputation: 567

The problem is in the creation of $pets:

my $pets = {'cats' => @cats, 'dogs' => @dogs};

Is roughly equivalent to:

my $pets = {'cats', {name => 'cat1', ...}, {name => 'cat2', ...}, 
            'dogs', {name => 'dog1', ...}, {name => 'dog2, ...} };

Which is the same as:

my $pets = {
            'cats'                 => {name => 'cat1', ...}, 
            {name => 'cat2'},      => 'dogs', 
            {name => 'dog1', ...}, => {name => 'dog2} 
           };

You want to use ArrayRefs:

my $pets = {'cats' => \@cats, 'dogs' => \@dogs};

Which is:

my $pets = {
    'cats' => [
        {name => 'cat1', ...},
        {name => 'cat2', ...}, 
    ],      
    'dogs' => [ 
        {name => 'dog1', ...},
        {name => 'dog2', ...},  
    ],
}; 

Which is also how you could declare the whole data structure at once.

Upvotes: 6

mob
mob

Reputation: 118605

This line

my $pets = {'cats' => @cats, 'dogs' => @dogs};

is a red flag. It's valid Perl, but it's not doing what you would expect. Perl will flatten your lists in this construction, so if @cats contains ($cat_1,$cat_2) and @dogs containts ($dog_1,$dog_2), your expression is parsed as

my $pets = { 'cats', $cat_1, $cat_2, 'dogs', $dog_1, $dog_2 };

which is like

my $pets = { 'cats' => $cat_1, $cat_2 => 'dogs', $dog_1 => $dog_2 }

with the hash references $cat_2 and $dog_1 getting stringified before being used as hash keys.

Hash values must be scalar values, not arrays. But array references are OK. Try:

my $pets = {'cats' => \@cats, 'dogs' => \@dogs};

Upvotes: 7

Related Questions