How do I convert a blessed object into JSON in Perl? Following is the array I have:
#x = ({
'notificationtype' => 'TRAP',
'receivedfrom' => 'UDP: [10.127.34.212]:48909->[10.127.34.182]:162',
'version' => 1,
},
[
[
bless( {
'oidptr' => bless( do{\(my $o = '140059234062224)}, ''netsnmp_oidPtr' )
}, 'NetSNMP::OID' ),
'600',
67
],
[
bless( {
'oidptr' => bless( do{\(my $o = '140059236784112)}, ''netsnmp_oidPtr' )
}, 'NetSNMP::OID' ),
'OID: .iso.org.dod.internet.private.enterprises.14296.1.100.0.0.1',
6
]
]);
I am able to convert $VAR1 alone using encode_json \#x, but when I the use the blessed object, it's not working. I am getting the error:
encountered object 'NetSNMP::OID=HASH(0x136b278)', but neither allow_blessed nor convert_blessed settings are enabled at u.pl line 256.
I expected a serialized JSON object so that I can send it over to server.
How can I do it?
It depends, you want to preserve the visuals ? or the data and hierarchy ? same positions could not be rendered in other terminal sizes. I would preserve just the data and hierarchy. Probably I would diregard focus and events. I would start umarating the properties like the following and then creaginf a factory These are the ones I found for Box
container.options
container.position,
container.getLines()
container.getText()
container.getContent
container.children
container.parent
container.style
container.type
container.visible
container.height
container.hidden
container.index
subclasses have rows, data, lines, and more... I dont think there is a mechanic way of doing it...
if you want to recreate the hierarchy, you also need to iterate on children and parent, probaly the entire tree.
Thinking about it perhaps just with this.options is enough and children and parent.. good luck will try the same...
Related
I am currently working on a project and as the title suggests, what I want to do is to be able to search from an HTML a cluster already uploaded on elasticsearch and preview the results back in the HTML.
I thought of using Logstash to send the search input from HTML to elasticsearch but I can't figure a way of viewing those results back in the HTML. In general what I want to do, is to be able to work with elasticsearch the way kibana does, but from a website.
Appreciate any possible help :)
use php-elastic official library(https://github.com/elastic/elasticsearch-php).
You can use the following code to get the search result:
$this->client = ClientBuilder::create()->setHosts(["ELASTICSEARCH_HOST:ELASTICSEARCH_PORT"])->build();
$queryBody = ["match_all" => new \stdClass()];
if($search) {
$queryBody = [
"match" => [
"_all" => $search
]
];
}
$params = [
"from" => $page * $this->pageSize, // if you want data for pagination
"size" => $this->pageSize, // if you want data for pagination
"index" => $index,
"type" => $type,
"_source_include" => $fields, // Your required field array
"body" => [
"query" => $queryBody
]
];
//Get the search result
$response = $this->client->search($params);
I'm sending a post request in a test case, and I want to assert that a specific element, let's say with key 'x' exists in the response. In this case, I can't say seeJson(['x' => whatever]); because the value is unknown to me. and for sure, I can't do it with seeJson(['x']);.
Is there a way to solve this?
If it matters:
Laravel: v5.2.31
PHPUnit: 5.3.4
May it will be helpful for anyone else. You can write this test for your check response json structure
$this->post('/api/login/', [
'email' => 'customer3#example.com',
'password' => '123123123',
])->assertJsonStructure([
'status',
'result' => [
'id',
'email',
'full_name',
],
]);
Although it's not optimal at all, I chose to use this code to test the situation:
$this->post(URL, PARAMS)->see('x');
X is a hypothetical name, and the actual element key has a slim chance of popping up in the rest of the data. otherwise this nasty workaround wouldn't be practical.
UPDATE:
Here's the solution to do it properly:
public function testCaseName()
{
$this->post(route('route.name'), [
'param1' => 1,
'param2' => 10,
], [
'headers_if_any' => 'value'
]);
$res_array = (array)json_decode($this->response->content());
$this->assertArrayHasKey('x', $res_array);
}
I'm having a bit of a fiddle with JSON and D3, trying to represent some disk usage information in a 'bubble' style.
Based on this initially: http://bl.ocks.org/mbostock/4063269
The JSON is pretty simple - hierarchical data looking a bit like this:
http://bl.ocks.org/mbostock/raw/4063530/flare.json
{
"name": "flare",
"children": [
{
"name": "analytics",
"children": [
{
"name": "cluster",
"children": [
{"name": "AgglomerativeCluster", "size": 3938},
{"name": "CommunityStructure", "size": 3812},
{"name": "HierarchicalCluster", "size": 6714},
{"name": "MergeEdge", "size": 743}
]
}
]
}
]
}
Now, what I'm trying to do is take a quotas report from a NetApp - in XML form.
I have multiple 'per server' XML files looking approximately like:
<quotas>
<quota>
<quota-target>/vol/vol1/qtree1</quota-target>
<volume>vol1</volume>
<disk-used>554444</disk-used>
<disk-limit>2000000</disk-limit>
</quota>
<quota>
<quota-target>/vol/vol1/qtree2</quota-target>
<volume>vol1</volume>
<disk-used>1235655</disk-used>
<disk-limit>2000000</disk-limit>
</quota>
<quota>
<quota-target>/vol/vol2/qtree1</quota-target>
<volume>vol2</volume>
<disk-used>987664</disk-used>
<disk-limit>2000000</disk-limit>
</quota>
</quotas>
What I'm trying to do is assemble some JSON for use with D3 that's hierarchical:
site
server
volume
quota-target
disk-used
I'm doing ok with a foreach loop:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
use JSON;
my %sites = (
'site1' => [qw ( servera serverb )],
'site2' => [qw ( s2serverc s2serverd)],
);
my $data;
$data->{'name'} = "quotas";
foreach my $sitename ( keys %sites ) {
my $site = { 'name' => $sitename };
push( #{ $data->{'children'} }, $site );
foreach my $server ( #{ $sites{$sitename} } ) {
my $server = { 'name' => $server };
push( #{ $site->{'children'} }, $server );
$twig->parsefile("$server.quotas.xml");
foreach my $quota ( $twig->get_xpath('//quota') ) {
push(
#{ $server->{'children'} },
{ 'name' => $quota->first_child_text('quota-target'),
'size' => $quota->first_child_text('disk-used')
}
)
}
}
}
open( my $output, ">", "quotas.json" ) or die $!;
print {$output} to_json( $data, { 'pretty' => 1 } );
close($output);
This is broadly working, and producing me pretty pictures.
However I'm having two problems:
Ordering of the JSON changes each run, because I'm using a hash. Whilst not a show stopper - is there a way I can enforce an order in the JSON output? (Not necessarily just 'sorted' alphabetically)
Similarly - I'm looking at how to insert a 'volume' level node which isn't currently present, as I'm creating new anonymous hashes to insert into children at each layer of the foreach loop. This feels a bit clunky, but what I'm thinking is:
Extract a list of volumes with get_xpath('//volume') and uniqueify it.
Either iterate per volume finding subnodes that match (Is there an xpath expression to specify a child value?)
Or create a 'staging' hash of hashes that I then 'merge' into children in the JSON.
Does anyone have any better suggestions?
I can quite easily create a hash of the desired structure e.g.
$stuff{$site}{$server}{$volume}{$qtree} = $size;
But then would have to turn that into the appropriate JSON (which I suppose might be a better approach overall).
is there a way I can enforce an order in the JSON output?
Yeah, use an array instead of an object. JSON objects are unordered.
An object is an unordered set of name/value pairs.
But it seems you're already using arrays for your lists.
Maybe you're want to be able to perform diffs, in which case JSON.pm provides a coarse means of specifying keys in the form of sort_by. Useful if you want to perform diffs.
If you just want to "enforce" some sorting by the hash key, you can use the "canonical" feature, as also said here.
Having an application that returns partial JSON responses, how can I combine objects/variables into one?
I am using
$json = JSON->new->utf8->decode($response->content);
to convert response into object/variable, but that is for each single one.
Now I need it to combine several partial responses into one.
How to do that and how to deal with possible duplicates items to avoid overwriting?
UPDATE:
To better understand the above issue see sample response below.
Every partial response has same structure, but different data in 'Groups' , 'Message' and 'Data' sections. The last response comes with 'Finished' = true.
$VAR1 = {
'answer' => {
'Error' => bless( do{\(my $o = 0)}, 'JSON::XS::Boolean' ),
'Id' => 12345,
'Finished' => $VAR1->{'answer'}{'Error'},
'Groups' => [
{
'Code' => 'ABC',
'RegNum' => 123,
'Name' => 'John Doe'
},
{
...
}
],
'Message' => undef,
'Data' => [
{
'Column1' => 'c1',
'Column2' => 'c2'
},
{
...
}
],
}
}
You can merge hashes by Hash::Merge
This has absolutely nothing to do with JSON. You want to merge two data structures. From the little you said, there are three arrays to merge. You didn't specify how, so maybe you simply want to append the elements of the new response to the elements of the original response.
for (qw( Groups Message Data )) {
push #{ $orig->{answer}{$_} }, #{ $new->{answer}{$_} }
if $new->{answer}{$_};
}
I have a json structure that I'm decoding that looks like this:
person => {
city => "Chicago",
id => 123,
name => "Joe Smith",
pets => {
cats => [
{ age => 6, name => "cat1", type => "siamese", weight => "10 kilos" },
{ age => 10, name => "cat2", type => "siamese", weight => "13 kilos" },
],
dogs => [
{ age => 7, name => "dog1", type => "siamese", weight => "20 kilos" },
{ age => 5, name => "dog2", type => "siamese", weight => "15 kilos" },
],
},
},
}
I'm able to print the city, id, name by doing:
foreach my $listing ($decoded->{person})
{
my $city = $listing->{city};
my $name = $listing->{name};
name - $city - \n";
}
however, I'm unsure of how to print the pets->cats or pets->dogs. I'm able to do a dump of them by:
my #pets = $listing->{pets}->{cats};
dump #pets;
but I'm not sure how to access them through the hash structure.
Digging into a big structure is pretty simple, once you know the rules:
Wrap hash keys in {}
Wrap array indexes in []
If your top level variable is a reference, use -> before the first identifier.
After the first set of braces or brackets, additional arrows (->) are optional.
So:
* $data->{person}{name} returns 'Joe Smith'
* $data->{person}->{name} also returns 'Joe Smith'
* $data->{pets}{cats}[0]{age} returns 6.
For way more detail on this topic, see the Perl Data Structures Cookbook (perldoc perldsc)
When you work with big structures like this there are some important things to be aware of. The biggest of these is autovivification. Autoviv means that Perl will automatically make data structure elements pop into existence for you to make your life easier. Unfortunately it can also make things difficult.
For example, autoviv is great when I do this:
my $data;
$data->{horse}[0]{color} = 'brown';
Autoviv magically turns $data into a hashref that contains the key horse with an array ref as its value. The array ref gets populated by a hash ref. The final hash ref then gets the key value pair of color => brown.
The problem comes in when you are walking a structure and do deep tests for existence:
# Code from above continues:
if( exists $data->{cat}[5]{color} ) {
print "Cat 5 has a color\n";
}
use Data::Dumper;
print Dumper $data;
Here, autovivification burns you by creating a bunch of junk in data, here's the program output:
$VAR1 = {
'cat' => [
undef,
undef,
undef,
undef,
undef,
{}
],
'horse' => [
{
'color' => 'brown'
}
]
};
Now you can guard against this kind of thing by carefully testing each layer of your structure for existence, but it's a huge pain in the butt. Instead, I prefer to use Data::Diver.
use Data::Diver qw( Dive );
my $dog_20_color = Dive( $data, 'dog', 20, 'color' );
print "Dog 20 is $dog_20_color\n" if defined $dog_20_color;
$data is unchanged here.
Also, you may have noticed that since Dive takes a list of keys or indexes, that means its easy to programatically build up a list of keys/indexes and descend an arbitrary path in your code.
Data::Diver can be a real life saver when you have to do a lot of manipulation of big, wonky data structures.
Assuming your $listing is a person you have to dereference array and hash refs.
# as long as we are assuming $listing is a person
# this goes inside the foreach you posted in your
# question.
# this will print all cats' names
foreach my $cat ( #{ $listing->{pets}->{cats} } )
{
# here $cat is a hash reference
say $cat->{name}; # cat's name
}
and so on for other stuff.
To access them from the structure you can do:
say $listing->{pets}->{cats}->[0]->{name}; # this will print 'cat1'
my #pets = $listing->{pets}->{cats};
This isn't doing what you think it is. $listing->{pets}->{cats} contains a reference to an array. Your new #pets array ends up contains just one element - the array reference.
What you actually need is
my #pets = #{ $listing->{pets}{cats} };
This deferences the array reference and gets you the actual array. Notice that I've also dropped the optional second arrow in the expression.
Once you've got the array, each element of it is a hash reference.
foreach (#pets) {
say $_->{name};
# etc ...
}
Of course, you don't need the intermediate array at all.
foreach (#{ $listing->{pets}{cats} }) {
say $_->{name};
}