use LWP::UserAgent;
I make this in php which is then called from perl:
$data = array("id"=> $id, "message" => $test);
echo json_encode($data);
And from print $data = perl->decoded_content script, it return :
{"id": "1234****", "message":"hi"};
But I can't manage to get id and message. I tried $data{'id'} and $data->{'id'}, but still no luck.
To decode the JSON, you'd use
use Cpanel::JSON::XS qw( );
my $data = decode_json($json);
This will produce a reference to a hash, so
$data->{id}
Related
im using this perl code to transform JSON into other form with some regular expressions:
use strict;
use warnings;
use feature 'say';
use JSON;
use utf8;
my %IDs = ( 'User awx01 logged in.' => 1001 );
my %levels = ( INFO => 4 );
my $data = <DATA>;
my $json = data2json($data);
my $record = decode_json($json);
say rec2msg($record);
sub data2json {
my $json = shift;
$json =~ s/[""]/"/g;
$json =~ s/\\//g;
$json =~ s/"(\{.*?\})"/$1/;
return $json;
}
sub rec2msg {
my $r = shift;
$r->{Message}{message} =~ /(\w+) (\w+) (.+)/;
my($user,$msg) = ($2,"$1 $3");
my $ID = $IDs{$r->{Message}{message}};
my $level = $levels{$r->{Message}{level}};
my $out = "$r->{Message}{'#timestamp'} host CEF:0|OpenSource|AWX|7.0.0|$ID|$msg|$level|src=127.0.0.1 dst=$r->{MessageSourceAddress} duser=$user";
return $out;
}
__DATA__
{"MessageSourceAddress":"192.168.81.20","EventReceivedTime":"2020-02-06 11:55:14","SourceModuleName":"udp","SourceModuleType":"im_udp","SyslogFacilityValue":1,"SyslogFacility":"USER","SyslogSeverityValue":5,"SyslogSeverity":"NOTICE","SeverityValue":2,"Severity":"INFO","EventTime":"2020-02-06 11:55:14","Hostname":"192.168.81.20","Message":"{\"#timestamp\": \"2020-02-06T08:55:52.907Z\", \"message\": \"User awx01 logged in.\", \"host\": \"awxweb\", \"level\": \"INFO\", \"logger_name\": \"awx.api.generics\", \"stack_info\": null, \"type\": \"other\", \"cluster_host_id\": \"awx-contr-01\", \"tower_uuid\": \"333b4131-495f-4460-8e4b-890241a9d73d\"}"}
But im getting this error:
2020-03-31 20:48:50 ERROR perl subroutine rec2msg failed with an error: 'Can't use string ("140511667030448") as a HASH ref while "strict refs" in use at /usr/libexec/nxlog/modules/extension/perl/event1.pl line 21.;'
What im doing wrong? How could i solve it?
You have JSON embedded in JSON, so you need to decode it twice. This often happens when you have one service passing through the response for another service.
Your data2json wasn't decoding that second level, so the value for the Message name was still a string. Since that value wasn't a hash reference, you get the error you reported.
You don't want to use a bunch of substitutions on the entire thing because you can inadvertently change things you shouldn't be messing with. Decode the top level just as you did, but then do the same thing for the Message value:
# read in all the data, even though it looks like a single line. Maybe it won't be later.
my $data = do { local $/; <DATA> };
# decode the first layer
my $decoded = decode_json( $data );
# decode the Message value:
$decoded->{Message} = decode_json( $decoded->{Message} );
Now, when you call rec2msg it should work out.
Note that this has the opposite problem to reverse it. You can't merely encode the entire thing to JSON again. The value for Message still needs to be a string, so you have to encode that first if you want to send it somewhere else. If you are doing that, you probably want to work on a copy. I use dclone to make a deep copy so whatever I do to $encoded does not show up in $decoded:
# make a deep copy so nested references aren't shared
use Storable qw(dclone);
my $encoded = dclone( $decoded );
$encoded->{Message} = encode_json( $encoded->{Message} );
my $new_data = encode_json( $encoded );
Then $new_data will have the same escaping as the original input.
Here it is altogether:
use strict;
use warnings;
use feature 'say';
use JSON;
use utf8;
my %IDs = ( 'User awx01 logged in.' => 1001 );
my %levels = ( INFO => 4 );
# read in all the data, even though it looks
my $data = do { local $/; <DATA> };
my $decoded = decode_json( $data );
$decoded->{Message} = decode_json( $decoded->{Message} );
say rec2msg($decoded);
sub rec2msg {
my $r = shift;
$r->{Message}{message} =~ /(\w+) (\w+) (.+)/;
my($user,$msg) = ($2,"$1 $3");
my $ID = $IDs{$r->{Message}{message}};
my $level = $levels{$r->{Message}{level}};
my $out = "$r->{Message}{'#timestamp'} host CEF:0|OpenSource|AWX|7.0.0|$ID|$msg|$level|src=127.0.0.1 dst=$r->{MessageSourceAddress} duser=$user";
return $out;
}
My snippet as below, I googled and cannot find a solution to pass variable to
the post request in my quoted string.
Most of the google result are just pass plain Json key value string pairs to the
post content. But I need to pass the parameter to the inner Json value part and call the related REST api. any suggestions? Thanks!
#!/usr/bin/perl -w
use strict;
use warnings;
# Create a user agent object
use LWP::UserAgent;
my $ua = LWP::UserAgent->new;
$ua->agent("MyApp/0.1");
# Create a request
my $req = HTTP::Request->new(POST => 'https://oapi.dingtalk.com/robot/send?access_token=foofb73f');
$req->content_type('application/json');
my $var1="value from var";
# works fine
my $message = '{"msgtype": "text","text":{"content":"plain string ok"}}';
# failed to compile
# my $message = '{"msgtype": "text","text":{"content":$var1}}';
$req->content($message);
# Pass request to the user agent and get a response back
my $res = $ua->request($req);
# Check the outcome of the response
if ($res->is_success) {
print $res->content;
} else {
print $res->status_line, "n";
}
You didn't properly convert the value into a JSON string.
use Cpanel::JSON::XS qw( encode_json );
my $message = encode_json({ msgtype => "text", text => { content => $var1 } });
Here is my issue with handling argument Perl. I need to pass Perl argument argument to a http request (Webservice) whatever Argument given to the perl file.
perl wsgrep.pl -name=john -weight -employeeid -cardtype=physical
In the wsgrep.pl file, i need to pass the above arguments to http post params.
like below,
http://example.com/query?name=john&weight&employeeid&cardtype=physical.
I am using LWP Package for this url to get response.
Is there any good approach to do this?
Updated:
Inside the wsgrep.pl
my ( %args, %config );
my $ws_url =
"http://example.com/query";
my $browser = LWP::UserAgent->new;
# Currently i have hard-coded the post param arguments. But it should dynamic based on the file arguments.
my $response = $browser->post(
$ws_url,
[
'name' => 'john',
'cardtype' => 'physical'
],
);
if ( $response->is_success ) {
print $response->content;
}
else {
print "Failed to query webservice";
return 0;
}
I need to construct post parameter part from the given arguments.
[
'name' => 'john',
'cardtype' => 'physical'
],
Normally, to url-encode params, I'd use the following:
use URI;
my $url = URI->new('http://example.com/query');
$url->query_form(%params);
say $url;
Your needs are more elaborate.
use URI qw( );
use URI::Escape qw( uri_escape );
my $url = URI->new('http://example.com/query');
my #escaped_args;
for (#ARGV) {
my ($arg) = /^-(.*)/s
or die("usage");
push #escaped_args,
join '=',
map uri_escape($_),
split /=/, $arg, 2;
}
$url->query(#escaped_args ? join('&', #escaped_args) : undef);
say $url;
I can't seem to figure out how to print just some of the return values such as: title, url, content
#!/usr/bin/perl
print "Content-type: text/html\n\n";
use REST::Google;
# set service to use
REST::Google->service('http://ajax.googleapis.com/ajax/services/search/web');
# provide a valid http referer
REST::Google->http_referer('http://www.example.com');
my $res = REST::Google->new(
q => 'ice cream',
);
die "response status failure" if $res->responseStatus != 200;
my $data = $res->responseData;
use Data::Dumper;
print Dumper( $data );
my #results = $data->results;
# CANT MAKE THIS WORK
foreach my $r (#result) {
print "\n";
print $r->title;
print $r->url;
print $r->content;
}
Try:
foreach my $r (#results) {
note the "s" -- if you put at the top of your script:
use strict;
use warnings;
you will catch these things
#!/usr/bin/perl
use strict;
print "content-type: text/html\n\n";
use REST::Google;
# set service to use
REST::Google->service(
'http://ajax.googleapis.com/ajax/services/search/web' );
# provide a valid http referer
REST::Google->http_referer( 'http://www.example.com' );
my $res = REST::Google->new( q => 'ice cream', );
die "response status failure" if $res->responseStatus != 200;
my $data = $res->responseData;
my #results = #{ $data->{results} };
foreach my $r ( #results ) {
print "\n";
print $r->{title};
print $r->{url};
print $r->{content};
}
A couple of problems here:
1) $data is not an object, so you can't treat it like an object.
$data->results would be the correct syntax if you're calling a method on an object. In this case $data is just a regular HASHREF, so the syntax is:
$data->{results}
2) $data->{results} is an ARRAYREF and not an ARRAY. So, you need to de-reference it in order to get at the values.
Now, my #results = $data->{results} becomes:
my #results = #{ $data->{results} };
#{ ARRAYREF } is how you dereference the array.
3) As you're iterating over #results, you're once again using the object syntax. However, the values of #results are also just plain HASHREFs. So, $r->title becomes:
$r->{title}
Using a tool like Data::Dumper to inspect return values can be key in sorting this kind of thing out. You may also want to look at Data::Printer, which is even sexier than Data::Dumper
I was wondering if you could shed some lights regarding the code I've been doing for a couple of days.
I've been trying to convert a Perl-parsed hash back to XML using the XMLout() and XMLin() method and it has been quite successful with this format.
#!/usr/bin/perl -w
use strict;
# use module
use IO::File;
use XML::Simple;
use XML::Dumper;
use Data::Dumper;
my $dump = new XML::Dumper;
my ( $data, $VAR1 );
Topology:$VAR1 = {
'device' => {
'FOC1047Z2SZ' => {
'ChassisID' => '2009-09',
'Error' => undef,
'Group' => {
'ID' => 'A1',
'Type' => 'Base'
},
'Model' => 'CATALYST',
'Name' => 'CISCO-SW1',
'Neighbor' => {},
'ProbedIP' => 'TEST',
'isDerived' => 0
}
},
'issues' => [
'TEST'
]
};
# create object
my $xml = new XML::Simple (NoAttr=>1,
RootName=>'data',
SuppressEmpty => 'true');
# convert Perl array ref into XML document
$data = $xml->XMLout($VAR1);
#reads an XML file
my $X_out = $xml->XMLin($data);
# access XML data
print Dumper($data);
print "STATUS: $X_out->{issues}\n";
print "CHASSIS ID: $X_out->{device}{ChassisID}\n";
print "GROUP ID: $X_out->{device}{Group}{ID}\n";
print "DEVICE NAME: $X_out->{device}{Name}\n";
print "DEVICE NAME: $X_out->{device}{name}\n";
print "ERROR: $X_out->{device}{error}\n";
I can access all the element in the XML with no problem.
But when I try to create a file that will house the parsed hash, problem arises because I can't seem to access all the XML elements. I guess, I wasn't able to unparse the file with the following code.
#!/usr/bin/perl -w
use strict;
#!/usr/bin/perl
# use module
use IO::File;
use XML::Simple;
use XML::Dumper;
use Data::Dumper;
my $dump = new XML::Dumper;
my ( $data, $VAR1, $line_Holder );
#this is the file that contains the parsed hash
my $saveOut = "C:/parsed_hash.txt";
my $result_Holder = IO::File->new($saveOut, 'r');
while ($line_Holder = $result_Holder->getline){
print $line_Holder;
}
# create object
my $xml = new XML::Simple (NoAttr=>1, RootName=>'data', SuppressEmpty => 'true');
# convert Perl array ref into XML document
$data = $xml->XMLout($line_Holder);
#reads an XML file
my $X_out = $xml->XMLin($data);
# access XML data
print Dumper($data);
print "STATUS: $X_out->{issues}\n";
print "CHASSIS ID: $X_out->{device}{ChassisID}\n";
print "GROUP ID: $X_out->{device}{Group}{ID}\n";
print "DEVICE NAME: $X_out->{device}{Name}\n";
print "DEVICE NAME: $X_out->{device}{name}\n";
print "ERROR: $X_out->{device}{error}\n";
Do you have any idea how I could access the $VAR1 inside the text file?
Regards,
newbee_me
$data = $xml->XMLout($line_Holder);
$line_Holder has only the last line of your file, not the whole file, and not the perl hashref that would result from evaling the file. Try something like this:
my $ref = do $saveOut;
The do function loads and evals a file for you. You may want to do it in separate steps, like:
use File::Slurp "read_file";
my $fileContents = read_file( $saveOut );
my $ref = eval( $fileContents );
You might want to look at the Data::Dump module as a replacement for Data::Dumper; its output is already ready to re-eval back.
Basically to load Dumper data you eval() it:
use strict;
use Data::Dumper;
my $x = {"a" => "b", "c"=>[1,2,3],};
my $q = Dumper($x);
$q =~ s{\A\$VAR\d+\s*=\s*}{};
my $w = eval $q;
print $w->{"a"}, "\n";
The regexp (s{\A\$VAR\d+\s*=\s*}{}) is used to remove $VAR1= from the beginning of string.
On the other hand - if you need a way to store complex data structure, and load it again, it's much better to use Storable module, and it's store() and retrieve() functions.
This has worked for me, for hashes of hashes. Perhaps won't work so well with structures which contain references other structures. But works well enough for simple structures, like arrays, hashes, or hashes of hashes.
open(DATA,">",$file);
print DATA Dumper(\%g_write_hash);
close(DATA);
my %g_read_hash = %{ do $file };
Please use dump module as a replacement for Data::Dumper
You can configure the variable name used in Data::Dumper's output with $Data::Dumper::Varname.
Example
use Data::Dumper
$Data::Dumper::Varname = "foo";
my $string = Dumper($object);
eval($string);
...will create the variable $foo, and should contain the same data as $object.
If your data structure is complicated and you have strange results, you may want to consider Storable's freeze() and thaw() methods.