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

Hi,

I seem to be having troubles and misunderstandings with processing JSON. My difficulty is likely in large part due to the fact that I don't really seem to understand the variable structures properly.

So, I have a JSON file, and I've a script which is using the JSON module, and I've decoded the data, etc, and now have a little loop where I want to work my way through the file and print it out. However, although I can print a few bits from the top level of the structure, I'm having problems trying to access anything else. FYI the JSON file is data retrieved from one of the wikimedia sites, but it seems valid.

The relevant Perl code is immediately below, while the JSON file data, as output by Dumper is below that:

my $data=decode_json($rawdata); # print Dumper($decoded); for ( @{ $data->{sections} } ) { # This line works; prints the 4 titles (one from each section) print $_->{title}."\n"; # No image info' in file anyway, so no output expected, but not sure i +f code is correct for ( @{ $_->{images} } ) { print $_."\n"; } # Nothing gets printed, but no error (figure it should print something + from top section) print $_{content}->{text}."\n"; # This fails with "Not a hash reference" # for ( @{ $_->{content}->{elements} } ) # { # print $_->{text}."\n"; # } }
The JSON data, as output by Dumper, is below: (text edited for readability, so it fits nicely)
$VAR1 = { 'sections' => [ { 'images' => [], 'level' => 1, 'content' => [], 'title' => '10-minute Turkey' }, { 'images' => [], 'level' => 2, 'content' => [ { 'text' => 'Makes 4 servings.', 'type' => 'paragraph' } ], 'title' => 'Description' }, { 'images' => [], 'level' => 2, 'content' => [ { 'elements' => [ { 'elements' => [], 'text' => 'abc' }, { 'elements' => [], 'text' => 'efg' }, { 'elements' => [], 'text' => 'hij' }, ], 'type' => 'list' } ], 'title' => 'Ingredients' }, { 'images' => [], 'level' => 2, 'content' => [ { 'elements' => [ { 'elements' => [], 'text' => 'tuv' }, { 'elements' => [], 'text' => 'wxy' }, { 'elements' => [], 'text' => 'z12' }, ], 'type' => 'list' } ], 'title' => 'Directions' } ] };

The actual URL I'm retrieving, for testing, is: http://recipes.wikia.com/api/v1/Articles/AsSimpleJson?id=24372

So, I don't really seem to get how to access anything which is at a lower level, such as {sections}->{content}->{elements}->{text}, or {sections}->{content}->{text}. I'm also a bit confused as to how to distinguish, programatically, between the two bottom sections (i.e. "Directions" and "Ingredients"); for instance, how, if I only wanted "text" fields from the latter, could I distinguish it, in code, from the former (i.e. the third section, titled "Ingredients"?

Any assistance would be most welcome.

Thanks!

Replies are listed 'Best First'.
Re: Processing JSON with Perl
by huck (Prior) on Feb 17, 2017 at 20:53 UTC

    Perhaps this may help

    use strict; use warnings; my $data = { 'sections' => [ { 'images' => [], 'level' => 1, 'content' => [], 'title' => '10-minute Turkey' }, { 'images' => [], 'level' => 2, 'content' => [ { 'text' => 'Makes 4 servings.', 'type' => 'paragraph' } ], 'title' => 'Description' }, { 'images' => [], 'level' => 2, 'content' => [ { 'elements' => [ { 'elements' => [], 'text' => 'abc' }, { 'elements' => [], 'text' => 'efg' }, { 'elements' => [], 'text' => 'hij' }, ], 'type' => 'list' } ], 'title' => 'Ingredients' }, { 'images' => [], 'level' => 2, 'content' => [ { 'elements' => [ { 'elements' => [], 'text' => 'tuv' }, { 'elements' => [], 'text' => 'wxy' }, { 'elements' => [], 'text' => 'z12' }, ], 'type' => 'list' } ], 'title' => 'Directions' } ] }; deepdump($data,'$data->'); sub deepdump { my $data=shift; my $isname=shift; my $iam=ref $data; if ($iam eq 'HASH') { my @keys=keys(%$data); if (0==scalar(@keys)){print $isname.'={}'."\n";} else {for my $k (@keys) { deepdump($data->{$k},$isname.'{'.$k.'}'); +} } } # hash elsif ($iam eq 'ARRAY') { my $ict=0; if (0==scalar(@$data)){print $isname.'=[]'."\n";} else {for my $i (@$data) { deepdump($data->[$ict],$isname.'['.$ict.' +]'); $ict++} } } # array else {print $isname.'='.$data."\n";} } exit;
    Result:
    $data->{sections}[0]{content}=[] $data->{sections}[0]{title}=10-minute Turkey $data->{sections}[0]{images}=[] $data->{sections}[0]{level}=1 $data->{sections}[1]{level}=2 $data->{sections}[1]{title}=Description $data->{sections}[1]{images}=[] $data->{sections}[1]{content}[0]{text}=Makes 4 servings. $data->{sections}[1]{content}[0]{type}=paragraph $data->{sections}[2]{content}[0]{type}=list $data->{sections}[2]{content}[0]{elements}[0]{elements}=[] $data->{sections}[2]{content}[0]{elements}[0]{text}=abc $data->{sections}[2]{content}[0]{elements}[1]{elements}=[] $data->{sections}[2]{content}[0]{elements}[1]{text}=efg $data->{sections}[2]{content}[0]{elements}[2]{elements}=[] $data->{sections}[2]{content}[0]{elements}[2]{text}=hij $data->{sections}[2]{title}=Ingredients $data->{sections}[2]{images}=[] $data->{sections}[2]{level}=2 $data->{sections}[3]{images}=[] $data->{sections}[3]{title}=Directions $data->{sections}[3]{content}[0]{type}=list $data->{sections}[3]{content}[0]{elements}[0]{text}=tuv $data->{sections}[3]{content}[0]{elements}[0]{elements}=[] $data->{sections}[3]{content}[0]{elements}[1]{elements}=[] $data->{sections}[3]{content}[0]{elements}[1]{text}=wxy $data->{sections}[3]{content}[0]{elements}[2]{elements}=[] $data->{sections}[3]{content}[0]{elements}[2]{text}=z12 $data->{sections}[3]{level}=2

      Ah, yes. That looks very useful for me to understand it better. Thank you very much, I appreciate it. I'll have a play around over the next day or two, but I think I can figure it out given your post.

      Thanks!

        You are quick

        But what if the json is fluid, and $data->{sections}[3] is not always directions, but you want to do the directions? Just search for directions and keep its reference!

        use strict; use warnings; my $data = { 'sections' => [ { 'images' => [], 'level' => 1, 'content' => [], 'title' => '10-minute Turkey' }, { 'images' => [], 'level' => 2, 'content' => [ { 'text' => 'Makes 4 servings.', 'type' => 'paragraph' } ], 'title' => 'Description' }, { 'images' => [], 'level' => 2, 'content' => [ { 'elements' => [ { 'elements' => [], 'text' => 'abc' }, { 'elements' => [], 'text' => 'efg' }, { 'elements' => [], 'text' => 'hij' }, ], 'type' => 'list' } ], 'title' => 'Ingredients' }, { 'images' => [], 'level' => 2, 'content' => [ { 'elements' => [ { 'elements' => [], 'text' => 'tuv' }, { 'elements' => [], 'text' => 'wxy' }, { 'elements' => [], 'text' => 'z12' }, ], 'type' => 'list' } ], 'title' => 'Directions' } ] }; #deepdump($data,'$data->'); my $directions; for my $i (@{$data->{sections}}) { if ($i->{title} eq 'Directions') {$directions=$i} } if ($directions) { deepdump($directions,'$directions->'); } sub deepdump { my $data=shift; my $isname=shift; my $iam=ref $data; if ($iam eq 'HASH') { my @keys=keys(%$data); if (0==scalar(@keys)){print $isname.'={}'."\n";} else {for my $k (@keys) { deepdump($data->{$k},$isname.'{'.$k.'}'); +} } } # hash elsif ($iam eq 'ARRAY') { my $ict=0; if (0==scalar(@$data)){print $isname.'=[]'."\n";} else {for my $i (@$data) { deepdump($data->[$ict],$isname.'['.$ict.' +]'); $ict++} } } # array else {print $isname.'='.$data."\n";} } exit;
        Result:
        $directions->{title}=Directions $directions->{level}=2 $directions->{content}[0]{type}=list $directions->{content}[0]{elements}[0]{elements}=[] $directions->{content}[0]{elements}[0]{text}=tuv $directions->{content}[0]{elements}[1]{elements}=[] $directions->{content}[0]{elements}[1]{text}=wxy $directions->{content}[0]{elements}[2]{text}=z12 $directions->{content}[0]{elements}[2]{elements}=[] $directions->{images}=[]
        I think this second hint may also prove usefull!

Re: Processing JSON with Perl
by Anonymous Monk on Feb 17, 2017 at 20:58 UTC
    See perldsc, note how $sec->{content} is not a hash reference but an array reference, and don't use $_ so much.
    for my $sec ( @{ $data->{sections} } ) { print "Section: ", $sec->{title}, "\n"; for my $img ( @{ $sec->{images} } ) { print "Image: ", $img, "\n"; } print "Content Text: ", $sec->{content}[0]{text}//'(undef)' ,"\n"; for my $elem ( @{ $sec->{content}[0]{elements} } ) { print "Element Text: ", $elem->{text}, "\n"; } }
    Output:
    Section: 10-minute Turkey Content Text: (undef) Section: Description Content Text: Makes 4 servings. Section: Ingredients Content Text: (undef) Element Text: abc Element Text: efg Element Text: hij Section: Directions Content Text: (undef) Element Text: tuv Element Text: wxy Element Text: z12
      Thanks!