Not an array reference message is thrown when using XML:Simple - perl

I am new to perl and trying to parse XML using XML:Simple
My xml is
<suite suiteId="45" instanceId="3485">
<project>Test project</project>
<testcase id="2346" name="abc" suite="TEst1" priority="1" severity="1" owner="domain" category="BAT" timeout="10">
<description>Checking Test1</description>
<testExecTimeInMins>2</testExecTimeInMins>
<status>Failed</status>
<testServer id="86" name="host1" ip="1.2.3.4" platform="Linux" database="MySQL" buildNo="" />
<error></error>
</testcase>
<testcase id="2346456" name="abc123" suite="TEst2" priority="1" severity="1" owner="domain" category="BAT" timeout="10">
<description>Checking Test2</description>
<testExecTimeInMins>6</testExecTimeInMins>
<status>Passed</status>
<testServer id="86" name="host1" ip="1.2.3.4" platform="Linux" database="MySQL" buildNo="" />
<error />
</testcase>
</suite>
How do I get the values of testcase id, name, suite?
How to get the values for testServer, id, name?
I tried to access it as shown below but it throws "Not an Array reference at"
$XMLData = XMLin($targetFile);
foreach my $testcases (#{$XMLData->{testcase}}){
$logger->info("$testcases->{id}");
}

If you examine the actual data that is coming out of XMLin, you would see that you don't have an array reference (just like the error message states), but instead have a hashref that is keyed on the name of the testcase:
use strict;
use warnings;
use Data::Dumper;
use XML::Simple;
my $xml = XMLin("/Users/mcmillhj/temp.xml");
print Dumper $xml;
__DATA__
{
'instanceId' => '3485',
'project' => 'Test project',
'testcase' => {
'abc123' => {
'owner' => 'domain',
'priority' => '1',
'status' => 'Passed',
'suite' => 'TEst2',
'testExecTimeInMins' => '6',
'description' => 'Checking Test2',
'timeout' => '10',
'error' => {},
'category' => 'BAT',
'id' => '2346456',
'severity' => '1',
'testServer' => {
'database' => 'MySQL',
'buildNo' => '',
'ip' => '1.2.3.4',
'name' => 'host1',
'platform' => 'Linux',
'id' => '86'
}
},
'abc' => {
'owner' => 'domain',
'priority' => '1',
'status' => 'Failed',
'suite' => 'TEst1',
'testExecTimeInMins' => '2',
'description' => 'Checking Test1',
'timeout' => '10',
'error' => {},
'category' => 'BAT',
'id' => '2346',
'severity' => '1',
'testServer' => {
'database' => 'MySQL',
'buildNo' => '',
'ip' => '1.2.3.4',
'name' => 'host1',
'platform' => 'Linux',
'id' => '86'
}
}
},
'suiteId' => '45'
};
To get all testcases, you just need to iterate over the keys:
foreach my $testcasename ( keys %{ $XMLData->{testcase} } ){
$logger->info($XMLdata->{testcase}->{$testcasename}->{id});
}

Related

How to retrieve hash value from session in Perl

I am storing a hash in session. I am trying to retrieve the values from session, but is not successful. I want to loop through the hash value in session to generate a select box. Below is the session value
$VAR1 = {
'userDetails' => {
'roles' => [
{
'ln' => 'asdf',
'email' => 'test#example.com',
'session_id' => '14',
'is_active' => '0',
'role' => 'ndfbfd',
'facility_name' => 'jjjj',
'fn' => 'yyyyyy'
},
{
'ln' => 'asdf',
'email' => 'test#example.com',
'session_id' => '15',
'is_active' => '1',
'role' => 'ndfbfd',
'facility_name' => 'fbhsdf',
'fn' => 'yyyyyy'
},
{
'ln' => 'asdf',
'email' => 'test#example.com',
'session_id' => '16',
'is_active' => '1',
'role' => 'ndfbfd',
'facility_name' => 'mvsd',
'fn' => 'yyyyyy'
},
{
'ln' => 'asdf',
'email' => 'test#example.com',
'session_id' => '17',
'is_active' => '1',
'role' => 'bdfgre',
'facility_name' => 'jjjj',
'fn' => 'yyyyyy'
},
{
'ln' => 'asdf',
'email' => 'test#example.com',
'session_id' => '18',
'is_active' => '0',
'role' => 'gderere',
'facility_name' => 'jjjj',
'fn' => 'yyyyyy'
}
],
'ln' => 'asdf',
'logged_in' => '1',
'fn' => 'yyyyyy'
},
'logged_in' => '1',
'username' => 'test#example.com'
};
I am trying to retrieve the roles from the session
my %userDetails = $self->session('userDetails');
my %roles = $userDetails{'roles'};
foreach my $family ( keys %roles ) {
print "$family: { ";
for my $role ( keys %{ $HoH{$family} } ) {
print "$role=$HoH{$family}{$role} ";
}
print "}\n";
}
It is showing two errors.
Reference found where even-sized list expected.
Odd number of elements in hash assignment.
When I change the code my %userDetails = $self->session('userDetails'); to my %userDetails = \$self->session('userDetails'); I am getting the error
Odd number of elements in hash assignment.
Odd number of elements in hash assignment.
Just guessing blatantly
my %userDetails = %{$self->session('userDetails')};
my #roles = #{$userDetails{'roles'}};
require Data::Dumper;
foreach my $family ( #roles ) {
print Data::Dumper::Dumper($family);
}
In your example
'roles' => [
so this indicates that the value beneath the role attribute is an array reference.
$self->session('userDetails')
might be hash, or more probably a hash-reference. I guessed the later. So if you have a reference to an "element" you derefence it first. See perldoc perlref

Separates cart CodeIgniter based store

I am trying to build a cart system, after successfully entering the item to the cart. how can I display items according shop. I was a bit constrained here.
Controllers
function to_cart () {
$data = array(
array(
'id' => '1',
'qty' => 1,
'price' => 2.400,
'name' => 'Asus Eeepc',
'store' => 'My_store 1'
),
array(
'id' => '2',
'qty' => 1,
'price' => 1.500,
'name' => 'Accer AspireOne',
'store' => 'My_store 1'
),
array(
'id' => '3',
'qty' => 1,
'price' => 4.000,
'name' => 'Toshiba Satelite',
'store' => 'My_store 2'
),
array(
'id' => '4',
'qty' => 1,
'price' => 2.700,
'name' => 'Lenova ThinkCare',
'store' => 'My_store 1'
)
);
$this->cart->insert($data);
redirect($this->agent->referrer());
}
Views
<?php foreach ($this->cart->contents() as $item) {
echo '<td>'.$item['no'].'</td>';
echo '<td>'.$item['name'].'</td>';
echo '<td>'.$item['price'].'</td>';
echo '<td>'.$item['qty'].'</td>';
echo '<td>'.$item['store'].'</td>';
?>

accessing Data::Dumper output

I have this Perl subroutine:
sub ask_for_lease {
my $url = '/sp/api/v1/lease';
my $formdata = '{"classname":"lease",}';
my $c = REST::Client->new();
$c->setHost($wizhost);
$c->PUT (
$url
, $formdata
, $headers
);
my $r = from_json($c->responseContent());
#print Dumper($r);
#my #results = $r->{'results'};
my #items = %{#{$r->{'results'}}[0]}->{'items'};
print Dumper(#items);
for my $item (#items) {
print "=============\n";
print Dumper($item);
print "=============\n";
}
}
It produces this output:
$VAR1 = [
{
'owner' => undef,
'notes' => 'Simulation One',
'version' => undef,
'status' => 'Ready',
'name' => 'One',
'lease' => '7070',
'_oid' => '1'
},
{
'owner' => undef,
'notes' => 'Simulation Two',
'version' => undef,
'status' => 'Ready',
'name' => 'Two',
'lease' => '2',
'_oid' => '2'
},
{
'owner' => undef,
'notes' => 'Simulation Three',
'version' => undef,
'status' => 'Ready',
'name' => 'Three 2012',
'lease' => '3',
'_oid' => '3'
},
...
];
=============
$VAR1 = [
{
'owner' => undef,
'notes' => 'Simulation One',
'version' => undef,
'status' => 'Ready',
'name' => 'One',
'lease' => '7070',
'_oid' => '1'
},
{
'owner' => undef,
'notes' => 'Simulation Two',
'version' => undef,
'status' => 'Ready',
'name' => 'Two',
'lease' => '2',
'_oid' => '2'
},
{
'owner' => undef,
'notes' => 'Simulation Three',
'version' => undef,
'status' => 'Ready',
'name' => 'Three 2012',
'lease' => '3',
'_oid' => '3'
},
...
];
=============
What I want to be able to do is iterate over the items array and print out the status and the name, but I am not sure I am dereferencing $r correctly.
The line
my #items = %{#{$r->{'results'}}[0]}->{'items'}
is very suspicious. You are extracting the first element of the array referred to by $r->{results}, dereferencing that as a hash, and using that hash in reference syntax. You should have got
Using a hash as a reference is deprecated
if you have use strict and use warnings in place as you should.
It is best to extract complex nested data in layers. In this case you can get the reference to the items array into a scalar variable and use that.
my $items= $r->{results}[0]{items};
for my $item ( #$items ) {
printf "name: %s, $item->{name};
printf "status: %s, $item->{status};
print "--\n";
}
If you post your JSON data then we will be able to help much better

Web::Scraper nested structures & elements only containing spesific data

I have the following code to scrape a form for inputs and get the attributes id and name.
#!/usr/bin/perl
use warnings;
use strict;
use URI;
use Data::Dumper::Simple;
use Web::Scraper;
my $urlToScrape = "http://digitalarkivet.arkivverket.no/finn_kilde";
my $scrap = scraper {
process 'div.listGroup.open > ul.grouped > li.expandable', 'data[]' => scraper {
process 'input', 'id' => '#id', name => '#name';
process 'label', 'label_for' => '#for';
process 'span.listExpander ', 'Text' => 'TEXT';
process 'ul.sublist1', 'sublist[]' => scraper {
process 'input', 'id' => '#id', name => '#name';
process 'label', 'label_for' => '#for';
process 'span', 'label' => 'TEXT';
};
};
};
my $res = $scrap->scrape(URI->new($urlToScrape));
print Dumper($res);
which gives me (shortend $res to fit screen better)
$res = {
'data' => [
{
'label_for' => 'ka0',
'sublist' => [
{
'label' => 'Statlig folketelling',
'label_for' => 'ka0kt0',
'name' => 'kt[]',
'id' => 'ka0kt0'
}
],
'name' => 'ka[]',
'id' => 'ka0>',
'Text' => 'Folketellinger'
},
{
'sublist' => [
{
'label' => 'Manntall',
'name' => 'kt[]',
'label_for' => 'ka1kt0',
'id' => 'ka1kt0'
}
],
'label_for' => 'ka1',
'id' => 'ka1>',
'name' => 'ka[]',
'Text' => 'Manntall'
},
....
{
'label_for' => 'r0',
'sublist' => [
{
'label_for' => 'r0f0',
'id' => 'r0f0',
'name' => 'f[]',
'label' => "01 Østfold"
}
],
'id' => 'r0',
'name' => 'r[]',
'Text' => "Østlandet"
},
{
'Text' => "Sørlandet",
'id' => 'r1',
'sublist' => [
{
'label_for' => 'r1f0',
'name' => 'f[]',
'id' => 'r1f0',
'label' => '09 Aust-Agder'
}
],
'label_for' => 'r1',
'name' => 'r[]'
}
]
};
I' have 2 issues I need to fix. First, I only want to get data for inputs having 'name' = ka[] (at top level).
Second, I only get data for first ul.sublist1 (If you study the page I'm scraping you can see that several "Kildekategori" have subsets of data, which are revealed if expanded/ clicked upon. Putting brackets on Text[] only gets me the sublist textnames, but not their attributes.
I'm thinking I might have to grab data in 2 scrapes instead, since nested values are revealed by id and label_for.
Solved it by scraping three times, foreach "level"
#!/usr/bin/perl
use strict;
use warnings;
use URI;
use Web::Scraper;
use Data::Dumper::Simple;
my %site;
my #res;
my $i;
my $j;
my $label_for;
my #scrape;
$site{'siteID'} = 1;
$site{'url'} = "http://digitalarkivet.arkivverket.no/finn_kilde";
$site{'name'} = "finn_kilde";
open FIL, ">$site{'name'}.csv" or die $!;
my $seperator=";";
$scrape[0] = scraper {
process 'div.listGroup.open > ul.grouped > li.expandable', 'data[]' => scraper {
process 'input',
'id' => '#id',
'value' => '#value',
'type' => '#type',
'name' => '#name';
process 'label', 'label_for' => '#for';
process 'span.listExpander ', 'text' => 'TEXT';
};
};
$scrape[1] = scraper {
process 'ul.sublist1 > li', 'data[]' => scraper {
process 'input',
'id' => '#id',
'value' => '#value',
'type' => '#type',
'name' => '#name';
process 'label', 'label_for' => '#for';
process 'span', 'text' => 'TEXT';
}
};
$scrape[2] = scraper {
process 'ul.sublist2 > li', 'data[]' => scraper {
process 'input',
'id' => '#id',
'value' => '#value',
'type' => '#type',
'name' => '#name';
process 'label', 'label_for' => '#for';
process 'span', 'text' => 'TEXT';
}
};
for $i (0 .. $#scrape){
$res[$i] = $scrape[$i]->scrape(URI->new($site{'url'}));
unless ($i) {
print FIL join($seperator,"label_for","text","name","value","id","type")."\n";
}
for $j (0 .. $#{$res[$i]->{data}}) {
if (defined($res[$i]->{data}[$j]->{label_for})){
$label_for=$res[$i]->{data}[$j]->{label_for};
} else {
$label_for="";
}
if (length($label_for)>0) {
my $name=$res[$i]->{data}[$j]->{name};
my $text=$res[$i]->{data}[$j]->{text};
my $value=$res[$i]->{data}[$j]->{value};
my $id=$res[$i]->{data}[$j]->{id};
my $type=$res[$i]->{data}[$j]->{type};
my #row=($label_for,$text,$name,$value,$id,$type);
print FIL join($seperator,#row);
print FIL "\n";
}
}
sleep(2);
}
close FIL;
print Dumper(\#res);
1;

Combining 2+ 'deep' (multi-dimensional) hashes in perl

There is a question that explains exactly what I want here: how to merge 2 deep hashes in perl
However, the answer there does not seem to work for me (suggestions of using the Merge module).
I have two hashes like so:
$VAR1 = {
'57494' => {
'name' => 'John Smith',
'age' => '9',
'height' => '120'
},
'57495' => {
'name' => 'Amy Pond',
'age' => '17',
'height' => '168'
}
}
};
$VAR1 = {
'57494' => {
'name_address' => 'Peter Smith',
'address' => '5 Cambridge Road',
'post_code' => 'CR5 0FS'
}
}
};
If I use Hash::Merge or the %c = {%a,%b} format I get this every time:
$VAR1 = '57494';
$VAR2 = {
'name_address' => 'Peter Smith',
'address' => '5 Cambridge Road',
'post_code' => 'CR5 0FS'
};
(so it basically overwrote the first data with the second and messed up the keys) when I want:
$VAR1 = {
'57494' => {
'name' => 'John Smith',
'age' => '9',
'height' => '120'
'name_address' => 'Peter Smith',
'address' => '5 Cambridge Road',
'post_code' => 'CR5 0FS'
},
'57495' => {
'name' => 'Amy Pond',
'age' => '17',
'height' => '168'
}
}
};
So when the keys are the same, the data merges together, otherwise the new keys are just appended onto the end. I hope this make sense. Maybe I've done something incorrectly using Merge or need to 'manually' add them in loops, but I'm spending too much time thinking about it, regardless!
Edit: how I use Merge to see if I'm doing something silly:
I have:
use Hash::Merge qw( merge );
...hash data above as %hash1 and %hash2...
my %combined_hash = %{ merge( %hash1,%hash2 ) };
print Dumper(%combined_hash);
If I do it with references, it works like a charm.
use strict; use warnings;
use Data::Dumper;
use Hash::Merge qw(merge);
my $h1 = {
'57494' => {
'name' => 'John Smith',
'age' => '9',
'height' => '120'
},
'57495' => {
'name' => 'Amy Pond',
'age' => '17',
'height' => '168'
}
};
my $h2 = {
'57494' => {
'name_address' => 'Peter Smith',
'address' => '5 Cambridge Road',
'post_code' => 'CR5 0FS'
}
};
my $h3 = merge( $h1, $h2 );
print Dumper $h3;
Output:
$VAR1 = {
'57495' => {
'name' => 'Amy Pond',
'age' => '17',
'height' => '168'
},
'57494' => {
'name_address' => 'Peter Smith',
'name' => 'John Smith',
'post_code' => 'CR5 0FS',
'address' => '5 Cambridge Road',
'height' => '120',
'age' => '9'
}
};
If, however, I do it with hashes instead of hash refs, it doesn't:
my %hash1 = (
'57494' => {
'name' => 'John Smith',
'age' => '9',
'height' => '120'
},
'57495' => {
'name' => 'Amy Pond',
'age' => '17',
'height' => '168'
}
);
my %hash2 = (
'57494' => {
'name_address' => 'Peter Smith',
'address' => '5 Cambridge Road',
'post_code' => 'CR5 0FS'
}
);
my %h3 = merge( %hash1, %hash2 );
print Dumper \%h3;
__END__
$VAR1 = {
'57495' => undef
};
That is because the merge from Hash::Merge can only take references, but you are passing it hashes. In addition, you need to call it in scalar context.
Try it like so:
# +--------+--- references
# ,-- SCALAR context | |
my $combined_hash = %{ merge( \%hash1, \%hash2 ) };
print Dumper($combined_hash);
for my $key (keys %fromhash) {
if(not exists $tohash{$key}) {
$tohash{$key} = {};
}
for my $subkey (keys %{$fromhash{$key}}) {
${$tohash{$key}}{$subkey} = ${$fromhash{$key}}{$subkey};
}
}
With more or less braces depending on whether my last coffee was any good.
Python is definitely more comfortable for this kind of thing, because it doesn't make you think about references:
for key in fromdict:
if key not in todict:
todict[key] = {}
todict[key] = dict(fromdict[key].items() + todict[key].items())
Or if todict is a defaultdict (creating keys on read as well as assignment):
for key in fromdict:
todict[key] = dict(dict(fromdict[key]).items() + dict(todict[key]).items())