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

Greetings. I am trying to parse the return from the API at companies house here in the UK to retrieve the name and address of company directors. In the example below these are the directors of Marks and Spencer PLC all of which is freely available as a matter of public record should you wish to look for it manually. I have this code:
use warnings; use strict; use JSON; #use vsutils; use Data::Dumper; use Ref::Util qw(is_arrayref is_hashref); my $companyNumber = 'NF001553'; my $request = "https://api.company-information.service.gov.uk/com +pany/".$companyNumber."/officers"; my $curl = `curl -s --user some-long-secret-api-access-string:"" +$request` || die "SOMETHING IS WRONG HERE\n"; my $decodedJSON = decode_json($curl); print Dumper($decodedJSON); if (is_hashref($decodedJSON)){ print "The JSON is a HashRef\n"; foreach my $key (keys %$decodedJSON) { if (is_hashref($key)) { print "its another hash\n" ; } elsif(is_arrayref($key)){ print "its an array ref" } else{ print Dumper($key); print $key." is neither a hashref nor an arrayref\n"; } } }
The output from this code is:
$VAR1 = { 'start_index' => 0, 'resigned_count' => 0, 'links' => { 'self' => '/company/NF001553/officers' }, 'etag' => '67c3a37590ae92fae81173063b52578626c754eb', 'items' => [ { 'links' => { 'self' => '/company/NF001553/app +ointments/MCJ22UBRM60V1quXQ6ieYR-BHoU', 'officer' => { 'appointments' => + '/officers/MCJ22UBRM60V1quXQ6ieYR-BHoU/appointments' } }, 'name' => 'EPSTEIN, Maurice', 'appointed_on' => '1967-10-11', 'officer_role' => 'secretary', 'address' => { 'locality' => 'London', 'address_line_2' => 'Highgate' +, 'address_line_1' => '12 Holly +Lodge Gds' } }, { 'links' => { 'officer' => { 'appointments' => + '/officers/9Bfo7qDNjzW8s8WIFiek6ZKbEWY/appointments' }, 'self' => '/company/NF001553/app +ointments/9Bfo7qDNjzW8s8WIFiek6ZKbEWY' }, 'name' => 'BRIMPTON, Sieff, Lord', 'officer_role' => 'director', 'address' => { 'address_line_2' => 'London', 'address_line_1' => '47/67 Bak +er St' }, 'appointed_on' => '1967-10-11' }, { 'appointed_on' => '1967-10-11', 'officer_role' => 'director', 'address' => { 'locality' => 'London', 'postal_code' => 'N6 4DE', 'address_line_1' => '10 Willow +dene, View Road' }, 'links' => { 'self' => '/company/NF001553/app +ointments/QqY-uC7Tj-1FbwxiOyFNUuWVFrc', 'officer' => { 'appointments' => + '/officers/QqY-uC7Tj-1FbwxiOyFNUuWVFrc/appointments' } }, 'name' => 'COLNE, Nigel L' }, { 'appointed_on' => '1967-10-11', 'officer_role' => 'director', 'address' => { 'address_line_2' => '9 Clare H +ill', 'address_line_1' => 'Low Felli +ng', 'region' => 'Surrey', 'locality' => 'Esher' }, 'links' => { 'officer' => { 'appointments' => + '/officers/-cMo0zdxbMKtbThp9_05cBHTlqU/appointments' }, 'self' => '/company/NF001553/app +ointments/-cMo0zdxbMKtbThp9_05cBHTlqU' }, 'name' => 'FROST, Albert E' }, { 'address' => { 'region' => 'Berkshire', 'address_line_1' => 'Tarbay La +ne', 'address_line_2' => 'Oakley Gr +een', 'postal_code' => 'SL4 4QG', 'locality' => 'Windsor' }, 'officer_role' => 'director', 'appointed_on' => '1967-10-11', 'name' => 'GREENBURY, Richard', 'links' => { 'self' => '/company/NF001553/app +ointments/qtxoixgvR76q-V_Dr7ciKqBOIws', 'officer' => { 'appointments' => + '/officers/qtxoixgvR76q-V_Dr7ciKqBOIws/appointments' } } }, { 'name' => 'HOWARD, William B F', 'links' => { 'self' => '/company/NF001553/app +ointments/0GNS6rBm4TRJYuUyvGB3pkU7A5Y', 'officer' => { 'appointments' => + '/officers/0GNS6rBm4TRJYuUyvGB3pkU7A5Y/appointments' } }, 'address' => { 'locality' => 'Gt Missenden', 'address_line_2' => 'Ballinger +', 'region' => 'Bucks', 'address_line_1' => 'Crawley F +arm' }, 'officer_role' => 'director', 'appointed_on' => '1967-10-11' }, { 'links' => { 'officer' => { 'appointments' => + '/officers/gWYmZKrU4OMJtPSsTNkjdGG4Beo/appointments' }, 'self' => '/company/NF001553/app +ointments/gWYmZKrU4OMJtPSsTNkjdGG4Beo' }, 'name' => 'LUSHER, John A', 'appointed_on' => '1967-10-11', 'officer_role' => 'director', 'address' => { 'locality' => 'Chalfont St Gil +es', 'address_line_2' => 'Three Hou +seholds', 'region' => 'Bucks', 'address_line_1' => 'Crumbledo +wn' } }, { 'appointed_on' => '1967-10-11', 'address' => { 'address_line_2' => 'Edinburgh +', 'address_line_1' => '17 Barton + Pk Drive' }, 'officer_role' => 'director', 'name' => 'LYNCH, Bryan J', 'links' => { 'officer' => { 'appointments' => + '/officers/vKKIOdwmGzk-ShkgLgoanrIwJT4/appointments' }, 'self' => '/company/NF001553/app +ointments/vKKIOdwmGzk-ShkgLgoanrIwJT4' } }, { 'appointed_on' => '1967-10-11', 'address' => { 'locality' => '47 Rue Plati', 'address_line_1' => 'Garden Pa +lace', 'region' => 'Monaco', 'address_line_2' => 'Apt 13' }, 'officer_role' => 'director', 'name' => 'OATES, John K', 'links' => { 'self' => '/company/NF001553/app +ointments/Y9VhpD-GeAPfXwKwS6wvkB-rblM', 'officer' => { 'appointments' => + '/officers/Y9VhpD-GeAPfXwKwS6wvkB-rblM/appointments' } } }, { 'address' => { 'locality' => 'London', 'address_line_2' => '5a Grevil +le Place', 'address_line_1' => 'Greville +Cottage' }, 'officer_role' => 'director', 'appointed_on' => '1967-10-11', 'name' => 'ORTON, Anthony S', 'links' => { 'self' => '/company/NF001553/app +ointments/R-YP23R5cK2Hf6JRrjOVpHyg9gA', 'officer' => { 'appointments' => + '/officers/R-YP23R5cK2Hf6JRrjOVpHyg9gA/appointments' } } }, { 'address' => { 'address_line_1' => '47/67 Bak +er St', 'address_line_2' => 'London' }, 'officer_role' => 'director', 'appointed_on' => '1967-10-11', 'name' => 'RAYNER, Lord', 'links' => { 'officer' => { 'appointments' => + '/officers/DJnCNLUlJj3unjEAQf0W2X6Dr3I/appointments' }, 'self' => '/company/NF001553/app +ointments/DJnCNLUlJj3unjEAQf0W2X6Dr3I' } }, { 'name' => 'SACHER, Simon J', 'links' => { 'self' => '/company/NF001553/app +ointments/hLXlIkT2RWch61wdFyZir-tQwMI', 'officer' => { 'appointments' => + '/officers/hLXlIkT2RWch61wdFyZir-tQwMI/appointments' } }, 'appointed_on' => '1967-10-11', 'address' => { 'address_line_1' => '30 Maida +Ave', 'address_line_2' => 'London' }, 'officer_role' => 'director' }, { 'address' => { 'locality' => 'Suburb', 'address_line_2' => 'Hampstead + Gd', 'address_line_1' => '12 Hampst +ead Way', 'region' => 'London' }, 'officer_role' => 'director', 'appointed_on' => '1967-10-11', 'name' => 'SALISSE, John J', 'links' => { 'self' => '/company/NF001553/app +ointments/LFN1hukWiIuVnIPQGDWxwCw-xcw', 'officer' => { 'appointments' => + '/officers/LFN1hukWiIuVnIPQGDWxwCw-xcw/appointments' } } }, { 'links' => { 'self' => '/company/NF001553/app +ointments/dp4apdzMGWp6l2rzj3YoGCrjhws', 'officer' => { 'appointments' => + '/officers/dp4apdzMGWp6l2rzj3YoGCrjhws/appointments' } }, 'name' => 'SIEFF, David D', 'officer_role' => 'director', 'address' => { 'address_line_1' => '47/67 Bak +er St', 'address_line_2' => 'London' }, 'appointed_on' => '1967-10-11' }, { 'address' => { 'locality' => 'Maidenhead', 'address_line_1' => 'Pinkneys +Foly', 'region' => 'Berks', 'address_line_2' => 'Pinkneys +Green' }, 'officer_role' => 'director', 'appointed_on' => '1967-10-11', 'name' => 'SILVER, Clinton V', 'links' => { 'officer' => { 'appointments' => + '/officers/SgbLq8ZnCTRfN-dNcQPm42w3nB0/appointments' }, 'self' => '/company/NF001553/app +ointments/SgbLq8ZnCTRfN-dNcQPm42w3nB0' } }, { 'appointed_on' => '1967-10-11', 'address' => { 'address_line_1' => '2 Wildwoo +d Rise', 'locality' => 'London', 'postal_code' => 'NW11 6SX' }, 'officer_role' => 'director', 'name' => 'SMITH, Alan K P', 'links' => { 'officer' => { 'appointments' => + '/officers/63SD6wwKhAdg1hlmzyF3gWlG4aY/appointments' }, 'self' => '/company/NF001553/app +ointments/63SD6wwKhAdg1hlmzyF3gWlG4aY' } }, { 'name' => 'SMITH, Alan K P', 'links' => { 'officer' => { 'appointments' => + '/officers/_uSPWXunF8LHVOOwXKKfgP8UaYA/appointments' }, 'self' => '/company/NF001553/app +ointments/_uSPWXunF8LHVOOwXKKfgP8UaYA' }, 'address' => { 'locality' => 'London', 'postal_code' => 'NW11 6SX', 'address_line_1' => '2 Wildwoo +d Rise' }, 'officer_role' => 'director', 'appointed_on' => '1967-10-11' }, { 'name' => 'SPRIDDELL, Peter H', 'links' => { 'self' => '/company/NF001553/app +ointments/GW363KMkDCtDvOLQBh_Kgr4BFds', 'officer' => { 'appointments' => + '/officers/GW363KMkDCtDvOLQBh_Kgr4BFds/appointments' } }, 'appointed_on' => '1967-10-11', 'address' => { 'address_line_1' => '37 Main A +venue', 'region' => 'Middlesex', 'postal_code' => 'HA6 2LH', 'locality' => 'Northwood' }, 'officer_role' => 'director' }, { 'name' => 'SUSMAN, David R', 'links' => { 'officer' => { 'appointments' => + '/officers/Ngd2iwiEoOPZdKZveZxP2w1Fyww/appointments' }, 'self' => '/company/NF001553/app +ointments/Ngd2iwiEoOPZdKZveZxP2w1Fyww' }, 'address' => { 'region' => 'Sth Africa', 'address_line_1' => 'Little Go +twick', 'address_line_2' => '1a Marlbo +ro Rd', 'locality' => 'Kenilworth' }, 'officer_role' => 'director', 'appointed_on' => '1967-10-11' }, { 'name' => 'TRANGMAR, Donald G', 'links' => { 'self' => '/company/NF001553/app +ointments/MfuzicUlq7mGfzADuwyieSaHSXY', 'officer' => { 'appointments' => + '/officers/MfuzicUlq7mGfzADuwyieSaHSXY/appointments' } }, 'appointed_on' => '1967-10-11', 'address' => { 'address_line_2' => 'St Johns +Wood Ok', 'address_line_1' => '13 Walsin +gham', 'locality' => 'London' }, 'officer_role' => 'director' } ], 'inactive_count' => 0, 'total_results' => 20, 'kind' => 'officer-list', 'active_count' => 20, 'items_per_page' => 35 }; The JSON is a HashRef $VAR1 = 'start_index'; start_index is neither a hashref nor an arrayref $VAR1 = 'resigned_count'; resigned_count is neither a hashref nor an arrayref $VAR1 = 'links'; links is neither a hashref nor an arrayref $VAR1 = 'etag'; etag is neither a hashref nor an arrayref $VAR1 = 'items'; items is neither a hashref nor an arrayref $VAR1 = 'inactive_count'; inactive_count is neither a hashref nor an arrayref $VAR1 = 'total_results'; total_results is neither a hashref nor an arrayref $VAR1 = 'kind'; kind is neither a hashref nor an arrayref $VAR1 = 'active_count'; active_count is neither a hashref nor an arrayref $VAR1 = 'items_per_page'; items_per_page is neither a hashref nor an arrayref
So what I think I am doing, is getting a hashRef back from decodeJSON, I then dereference that hash and for each key within it, try to determine if it is itself another hash or array. My expectation was that when I arrive at "items" I would find yet another hashRef or array, however what I appear to find is just a string, the name of the key. Can someone please point me in the right direction based on what data::dumper says is present in the return, and what I can actually access? Many thanks. Please be kind.

Replies are listed 'Best First'.
Re: Parsing the data returned from the companies house API
by choroba (Cardinal) on Oct 17, 2022 at 14:36 UTC
    You are checking the $key, which is always a string. You should have been checking the value corresponding to the key:
    for my $key (keys %$decodedJSON) { if (is_hashref($decodedJSON->{$key})) { print "$key is another hash\n"; } elsif(is_arrayref($decodedJSON->{$key})){ print "$key is an array ref\n"; } else{ print Dumper($key); print "$key is neither a hashref nor an arrayref\n"; } }

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Parsing the data returned from the companies house API
by Corion (Patriarch) on Oct 17, 2022 at 14:33 UTC

    You are looking at the key when what you want is the value:

    foreach my $key (keys %$decodedJSON) { if (is_hashref($key)) { ...

    Most likely you wanted:

    foreach my $key (keys %$decodedJSON) { my $val = $decodedJSON->{ $key }; if (is_hashref($val)) { ...
      Thanks for your quick replies. I have managed to get a little farther, and might be sufficiently well informed to complete this now. I have new code like this:
      use warnings; use strict; use JSON; #use vsutils; use Data::Dumper; use Ref::Util qw(is_arrayref is_hashref); my $companyNumber = 'NF001553'; my $request = "https://api.company-information.service.gov.uk/com +pany/".$companyNumber."/officers"; my $curl = `curl -s --user sameAPIKeyAsBefore:"" $request` || die + "SOMETHING IS WRONG HERE\n"; my $decodedJSON = decode_json($curl) || die "this is not JSON\n"; if (is_hashref($decodedJSON)){ for (sort keys %$decodedJSON) { if ($_ eq 'items'){ print Dumper($decodedJSON->{$_}); } } }
      Now this is returning the directors and their addresses, so I should be able to move forward handling each element.
        In fact, you don't need to iterate over hash keys to find out whether a key exists. There is a more effective way to do it: use exists.
        my $decodedJSON = decode_json($curl) or die "this is not JSON\n"; if (is_hashref($decodedJSON)) { if (exists $decodedJSON->{items}) { for my $item (@{ $decodedJSON->{items} }) { .... } } }
        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]