I have created a script to get some information from various external sources, the results should then be in json
format. There is a lot of data and I push everything to an array in a loop, then print the json
array after everything has been completed, an extract of that loop part of the script:
#!/usr/bin/perl
use JSON -convert_blessed_universally;
use strict;
use warnings;
my @json_arr;
my @servers = ("SERVER1", "SERVER2");
my @details = ("SERVER1,10.1.2.3,Suse Linux",
"SERVER2,10.1.2.4,Windows 10",
"SERVER3,10.1.2.5,Windows XP");
my $json = JSON->new->convert_blessed;
foreach my $server(@servers) {
foreach (@details) {
my @detail = split(',',$_);
if ($server eq $detail[0]) {
push @json_arr, {name => "$server", ip => "$detail[1]", os => "$detail[2]"};
}
}
}
my $result = $json->encode(\@json_arr);
print $result;
This gives an output of:
[
{
"name" : "SERVER1",
"ip" : "10.1.2.3",
"os" : "Suse Linux",
},
{
"name" : "SERVER2",
"ip" : "10.1.2.4",
"os" : "Widows 10"
}
]
and a screenshot:
I am however trying to do it by setting a key element instead and having the additional data as children of the device name, ie:
{
"instance" : [
{
"SERVER1" : {
"ip" : "10.1.2.3",
"os" : "Suse Linux"
},
"SERVER2" : {
"ip" : "10.1.2.4",
"os" : "Windows 10"
}
}
]
}
So I have tried a few things, including something like the below, then pushing to array, but I am getting funny results and just not getting the desired results.
my $json = '{
"instance" : [
$server => {
ip => "$detail[0]",
os => "$detail[1]"
}
]
}';
push @json_arr, $json;
It only takes a small re-arrangement
use warnings;
use strict;
use feature 'say';
use Data::Dumper;
use JSON::XS;
...
my @json_ary;
foreach my $server (@servers) {
foreach (@details) {
my @detail = split /,/;
if ($server eq $detail[0]) {
#push @json_ary, {name => "$server", ip => "$detail[1]" ...
push @json_ary,
{ $server => { ip => $detail[1], os => $detail[2] } }
}
}
}
print Dumper \@json_ary;
# Encode `{ instance => \@json_ary }` for the desired output
my $json = JSON->new->convert_blessed;
my $result = $json->pretty->encode( { instance => \@json_ary } );
say $result;
A few notes
No need for quotes around variables, since they get evaluated anyway ( "$detail[0]"
--> $detail[0]
etc)
No need for quotes around hash keys, as a syntax convenience: key => 'value'
is OK (and if the value is a variable it's just key => $var
). That is, as long as there are no spaces in the key name.
One way to pretty-print an existing JSON string:
print JSON::XS->new->ascii->pretty->encode(decode_json $json_str);
There may be a question of whether a hashrefs around each server entry (JSON objects) is needed or not; the above was confirmed in a comment after a discussion so I settled with it.
But if the output only needs a hashref with server entries in the array (and not an array of hashrefs for each server) then the code in the question can be modified to
my %server_details;
foreach my $server (@servers) {
foreach (@details) {
my @detail = split /,/;
if ($server eq $detail[0]) {
$server_details{$server} = { ip => $detail[1], os => $detail[2] }
}
}
}
Now this hash has details for all servers, and can be encoded with the key instance
. Then it is not clear to me what the overall structure should be; one option is:
my $result = JSON->pretty->encode( { instance => [ \%server_details ] } );
The problem is that you are adding hashes to an array ( push @json_arr, ...
) when you mean to add to a hash ( $instance{ $server_name } =...
).
my %servers = map { $_ => 1 } @servers;
my %instance;
for ( @details ) {
my ( $name, $ip, $os ) = split /,/;
next if !$servers{ $name };
$instance{ $name } = {
ip => $ip,
os => $os,
};
}
my @instances = \%instance;
my $data = { instance => \@instances };
Or using map
:
my %servers = map { $_ => 1 } @servers;
my %instance = (
map { $_->[0] => { ip => $_->[1], os => $_->[2] } }
grep $servers{ $_->[0] },
map [ split /,/ ],
@details
);
my @instances = \%instance;
my $data = { instance => \@instances };
This produces the following, as requested:
{
"instance" : [
{
"SERVER1" : {
"ip" : "10.1.2.3",
"os" : "Suse Linux"
},
"SERVER2" : {
"ip" : "10.1.2.4",
"os" : "Windows 10"
}
}
]
}
(This is different than zdim's first solution.)
Note that I got rid of the nested loops because they are nasty. For a few items, it's not a problem. But performance would suffer is @servers
or @details
would become large. So it's a bad idea, and a bad habit to get into.
Having an array which only even has a single element is weird. Did you perhaps want
{
"instance" : {
"SERVER1" : {
"ip" : "10.1.2.3",
"os" : "Suse Linux"
},
"SERVER2" : {
"ip" : "10.1.2.4",
"os" : "Windows 10"
}
}
}
This would be achieved by replacing
my $data = { instance => \@instances };
with
my $data = { instance => \%instance };
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.