Perl XML::Simple output nested node - perl

As input I have
$XML = {'node1' => {'node2' => {'node3' => {'node4'}}}};
then I generate the out xml
print(XMLout($XML, KeepRoot => 1));
and I get
<node1>
<node2 name="node3" node4="" />
</node1>
How can I get this as output
<node1>
<node2>
<node3>
<node4></node4>
</node3>
</node2>
</node1>

perl -MXML::Simple -e '$XML = {"n1" => {"n2" => [{"n3" => [{"n4"=>[{}]}]}]}};
print(XMLout($XML, KeepRoot=>1));'
Gives
<n1>
<n2>
<n3>
<n4></n4>
</n3>
</n2>
</n1>
The use of XML::Simple is discouraged. https://metacpan.org/pod/XML::Simple

XML::Simple isn't actually simple to use. You are better off with XML::LibXML.
Here is an example that shows how you can find how the data structure should look.
use warnings;
use strict;
use XML::Simple;
use Data::Dumper;
my $sample =
"<node1>
<node2>
<node3>
<node4></node4>
</node3>
</node2>
</node1>";
my $xs = XML::Simple->new(ForceArray => 1, KeepRoot => 1);
my $ref = $xs->XMLin($sample);
print "\n---------------\n", Dumper($ref), "\n--------------\n";
my $xml = $xs->XMLout($ref);
print "\n----------------\n";
print $xml;
The output produced from Data::Dumper shows the data structure.
---------------
$VAR1 = {
'node1' => [
{
'node2' => [
{
'node3' => [
{
'node4' => [
{}
]
}
]
}
]
}
]
};
--------------
----------------
<node1>
<node2>
<node3>
<node4></node4>
</node3>
</node2>
</node1>
Without ForceArray it looks like the output you received.

Related

How to print an element from an array which is inside the hash in perl

I'm trying to print the outputs from an API which are in multidimensional format.
use strict;
use warnings;
use Data::Dumper;
my $content={
'school_set' => 'SSET1234',
'result' => [
{
'school_name' => 'school_abc',
'display_value' => 'IL25',
'school_link' => 'example.com',
'status' => 'registerd',
'status_message' => 'only arts',
'school_id' => '58c388d40596191f',
}
],
'school_table' => 'arts_schools'
};
print "school_name is=".$content{result}[0]{school_name};
print "school_status is=".$content{result}[3]{status};
output
Global symbol "%content" requires explicit package name (did you forget to declare "my %content"?) at test8.pl line 20.
Global symbol "%content" requires explicit package name (did you forget to declare "my %content"?) at test8.pl line 21.
I have to print the outputs like below from the result.
school_name = school_abc
school_status = registered
If $content is a hash reference, you need to dereference it first. Use the arrow operator for that:
$content->{result}[0]{school_name}
The syntax without the arrow is only possible for %content.
my %content = ( result => [ { school_name => 'abc' } ] );
print $content{result}[0]{school_name};
If you want to print all the results, you have to loop over the array somehow. For example
#!/usr/bin/perl
use warnings;
use strict;
my $content = {
'result' => [
{
'school_name' => 'school_abc',
'status' => 'registerd',
},
{
'school_name' => 'school_def',
'status' => 'pending',
}
],
};
for my $school (#{ $content->{result} }) {
print "school_name is $school->{school_name}, status is $school->{status}\n";
}
Your data structure assumes an array, perhaps it would be useful to utilize loop output for the data of interest.
The data presented as hash reference and will require de-referencing to loop through an array.
Following code snippet is based on your posted code and demonstrates how desired output can be achieved.
use strict;
use warnings;
use feature 'say';
my $dataset = {
'school_set' => 'SSET1234',
'result' => [
{
'school_name' => 'school_abc',
'display_value' => 'IL25',
'school_link' => 'example.com',
'status' => 'registerd',
'status_message' => 'only arts',
'school_id' => '58c388d40596191f',
}
],
'school_table' => 'arts_schools'
};
for my $item ( #{$dataset->{result}} ) {
say "school_name is = $item->{school_name}\n"
. "school_status is = $item->{status}";
}
exit 0;
Output
school_name is = school_abc
school_status is = registerd

Perl - Sort multidimensional array

I have a variable that looks like this:
do {
my $a = {
computers => [
{
report_date_epoch => 1591107993595,
serial_number => "C02YK1TAFVCF",
username => "fake1\#example.com",
},
{
report_date_epoch => 1626877069476,
serial_number => "C03XF8AWJG5H",
username => "fake2\#example.com",
},
...
And I'd like to sort it by the epoch number into a new variable without the computers array.
The list of hashrefs in the arrayref for computer key sorted
use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd);
my $v = {
computers => [
{
report_date_epoch => 1591107993595,
serial_number => "C02YK1TAFVCF",
username => "fake1\#example.com",
},
{
report_date_epoch => 1626877069476,
serial_number => "C03XF8AWJG5H",
username => "fake2\#example.com",
}
]
};
my #by_epoch =
sort { $a->{report_date_epoch} <=> $b->{report_date_epoch} }
#{$v->{computers}};
dd $_ for #by_epoch;
I use Data::Dump to print complex data structures. (There are other good ones as well.)
Or use a core (installed) tool
use Data::Dumper;
...
say Dumper $_ for #by_epoch;
Sort::Key is so much cleaner than the builtin sort. Not much difference in this instance, but it only gets better as the task becomes more complex.
use Sort::Key qw( ikeysort );
my #by_report_date =
ikeysort { $_->{report_date_epoch} } # Sort them.
#{ $a->{computers} }; # Get the elements of the array of computers.

How to get "Var1" value of dumper in perl

When i use below code then it gives output, But i want "width", "file_media_type", "file_ext" values, But I am unable to get this value in individual. I am very new with Perl Please help me!
Code
use warnings ;
use strict;
use Image::Info qw[image_info];
use Data::Dumper;
my $file = 'd:\perl\test\a.jpg';
my $info = Dumper(image_info($file));
print $info;
Output
$VAR1 = {
'width' => 45,
'file_media_type' => 'image/png',
'file_ext' => 'png',
'PNG_Chunks' => [
'IHDR',
'gAMA',
'cHRM',
'IDAT',
'IEND'
],
'Chunk-cHRM' => ' z% Çâ ·  ÇF u0 O` :ù ?o',
'PNG_Filter' => 'Adaptive',
'color_type' => 'RGB',
'height' => 20,
'Gamma' => '0.45454',
'resolution' => '1/1',
'SampleFormat' => 'U8',
'Compression' => 'Deflate'
};
image_info($file) returns a hash reference. Looking at the dump you know the keys available (the keys are strings before =>)
$info = image_info($file);
foreach my $key ( qw/width file_media_type file_ext/ ){
print "$key:$info->{$key}\n";
}

Perl: overwrite structure member value

I am creating $input with this code:
push(#{$input->{$step}},$time);, then I save it in an xml file, and at the next compiling, I read it from that file. When i print it, i get the structure bellow.
if(-e $file)
my $input =XMLin($file...);
print Dumper $input;
and I get this structure
$VAR1 = {
'opt' => {
'step820' => '0',
'step190' => '0',
'step124' => '0',
}
};
for each step with it's time..
push(#{$input->{$step}},$time3);
XmlOut($file, $input);
If I run the program again, I get this structure:
$VAR1 = {
'opt' => {
'step820' => '0',
'step190' => '0',
'step124' => '0',
'opt' => {
'step820' => '0',
'step190' => '0',
'step124' => '0'
}
}
I just need to overwrite the values of steps(ex:$var1->opt->step820 = 2). How can i do that?
I just need to overwrite the values of steps(ex:$var1->opt->step820 = 2). How can i do that?
$input->{opt}->{step820} = 2;
I'm going to say what I always do, whenever someone posts something asking about XML::Simple - and that is that XML::Simple is deceitful - it isn't simple at all.
Why is XML::Simple "Discouraged"?
So - in your example:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $xml= XML::Twig->new->parsefile($file);
$xml -> get_xpath('./opt/step820',0)->set_text("2");
$xml -> print;
The problem is that XML::Simple is only any good for parsing the type of XML that you didn't really need XML for in the first place.
For more simple examples - have you considered using JSON for serialisation? As it more directly reflects the hash/array structure of native perl data types.
That way you can instead:
print {$output_fh} to_json ( $myconfig, {pretty=>1} );
And read it back in:
my $myconfig = from_json ( do { local $/; <$input_fh> });
Something like:
#!/usr/bin/env perl
use strict;
use warnings;
use JSON;
my $input;
my $time = 0;
foreach my $step ( qw ( step820 step190 step124 ) ) {
push(#{$input->{$step}},$time);
}
print to_json ( $input, {pretty=>1} );
Giving resultant JSON of:
{
"step190" : [
0
],
"step820" : [
0
],
"step124" : [
0
]
}
Although actually, I'd probably:
foreach my $step ( qw ( step820 step190 step124 ) ) {
$input->{$step} = $time;
}
print to_json ( $input, {pretty=>1} );
Which gives;
{
"step190" : 0,
"step124" : 0,
"step820" : 0
}
JSON uses very similar conventions to perl - in that {} denote key value pairs (hashes) and [] denote arrays.
Look at the RootName option of XMLout. By default, when "XMLout()" generates XML, the root element will be named 'opt'. This option allows you to specify an alternative name.
Specifying either undef or the empty string for the RootName option will produce XML with no root elements.

how to assign lines of variable length to a variable in perl?

I have a file I want to read that has a variable number of ids for each location that looks like this:
loc1 id1 id4 id5 id9
loc2 id2
loc3 id1 id11 id23
I would like to store this as follows locs(loc) = {all ids belonging to that location}
So that later, when I read another file I can do something like
if (grep id, locs(loc)){do something}
I tried to do this using a hash, but this is not working. I tried:
open my $loclist, '<', $ARGV[0];
my %locs;
while (<$loclist>) {
my #loclist_rec = split;
my $loclist_loc = #rlist_rec[0];
$locs{$loclist_loc} = #loclist_rec;
}
but this isnt working.
I new to perl and still trying to understand the different datatypes.
Any ideas? Thanks a lot!
This should do what you want.
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
open my $loclist, '<', "test.txt" or die $!;
my %locs;
while (<$loclist>) {
my ($loclist_loc, #loclist_rec) = split;
$locs{$loclist_loc} = \#loclist_rec;
}
print Dumper \%locs;
OUTPUT:
$ perl test.pl
$VAR1 = {
'loc2' => [
'id2'
],
'loc1' => [
'id1',
'id4',
'id5',
'id9'
],
'loc3' => [
'id1',
'id11',
'id23'
]
};
Also a possible choice would be a hash of hashes. When you want to look up an id, you could say if ($locs{$loc}{$id}) {do something}. The data structure would be
$VAR1 = {
'loc2' => {
'id2' => 1
},
'loc1' => {
'id1' => 1,
'id5' => 1,
'id4' => 1,
'id9' => 1
},
'loc3' => {
'id1' => 1,
'id11' => 1,
'id23' => 1
}
};