nims nims - 10 months ago 71
Perl Question

Parse json array using perl

I have written a test script to perform certain functions. Script works as expected. Currently,The arguments required to run the script are passed from the command line using

.I would like to move the command line arguments to a json file. The Endpoint ip will still be passed as command line arg.
I want the endpoint ip to act as a key. For instance, if the endpoint is, I want to get the client_ip,client_interface_ip,originip,....,port that is listed under endpoint id in the json config file mentioned below. How do i do that ?

current version of the script:

use Getopt::Long;

my ($self) = @_;

GetOptions (
"endpoint|e=s" => \$self->{'endpoint'},
"aggregator|a=s" => \$self->{'aggregator'},
"port|pt=s" => \$self->{'port'},
"client|c=s" => \$self->{'client'},
"client_interface|ci=s" => \$self->{'client_interface'},
"origin|o=s" => \$self->{'origin'},
"origin_interface|oi=s" => \$self->{'origin_interface'},
"interfacename|ot=s" => \$self->{'i1'},
"interfacename2|it=s" => \$self->{'i2'},
) || $self->abort( "Invalid command line options.
Valid options are endpoint,aggregator,port,client,client_interface,

#Terminate the script execution if the reqd args are not passed
my @required_args = qw(endpoint aggregator port client client_interface origin origin_interface

for my $command_line_arguments (@required_args) {
unless ($self->{$command_line_arguments}) {
$self->abort('missing required argument ' . $command_line_arguments);

$self->{'tObj'} = QA::crypto::tunnels->new
('host'=> $self->{'endpoint'})
or $self->abort('[Could not create a QA::Crypto::tunnels object.]');

json file for arguments:

"Endpoints": [{

"endpoint": "",
"client_ip": "",
"client_interface_ip": "",
"origin": "a.a.a.a",
"origin_interface": "",
"interfacename": "name",
"interfacename2": "name1",
"sl": 19,
"port": 362
}, {

"endpoint": "",
"client_ip": "",
"client_interface_ip": "",
"origin": "",
"origin_interface": "",
"interfacename": "interface name",
"interfacename2": "interfacename_2",
"sl": 19,
"port": 366


use strict;
use warnings;
use JSON;

my $json;
open my $fh, "<", "cfg.txt"
or die("Can't open file \"cfg.json\": $!\n");
local $/;
$json = <$fh>;

my $data = decode_json($json);
$json = JSON->new->utf8->pretty->encode($data);

Answer Source

The key "Endpoints" in your JSON doesn't seem to serve much purpose. You can rewrite the file so that the possible endpoint values are the keys, and their values are hashrefs with the key-related information. You can keep endpoint value there for reference as well, if you wish. You don't need the arrayref at all by what you describe.

If you do need "Endpoints", perhaps since there will be other types of keys, then you could have for its value another hashref, which will now contain a hashref with endpoint values as keys.

For example

   "Endpoints": { 
        "": { "client_ip": "",    ... },
        "": { "client_ip": "",... },
   "OtherKeys": { ... }, ...

The last values should not end with commas.

When you bring this into Perl you'll have nested hashrefs, like

$data = { 
    Endpoints => {  => { client_ip => '',     ... },  => { client_ip => '', ... },
    OtherKeys => { ... }, 

Then you retrieve the values simply as

my $client_ip = $data->{Endpoints}{''}{client_ip};

For example, retreive all endpoints and list this information for them

my @endpoints = keys %{$data->{Endpoints}};
foreach my $ip (@endpoints) {
    say $data->{Endpoints}{$ip}{client_ip};

See Using References in perlref, and also see perlreftut and perldsc.

Inspect (see) the whole data structure with Data::Dumper

use Data::Dumper;
my $data = decode_json($json);
print Dumper($data);

There is a number of other packages for working with nested data structures.

Note that the JSON package comes with encode_json and related methods, so you can create that file programmatically. This should help in getting its format right and it may help in the future with maintenance.