MkDwonderer MkDwonderer - 7 months ago 11
Perl Question

How to best reiterate through this perl data structure for desired output ? Is it possible without rebuilding existing one?

Perl beginner here, i have a script which makes api call, collects feedback in xml format, then using XML::Simple, massages data into below data structure, im trying to shoot for the following output:

filename1.req, UserFaulted,123

filename2.req, UserFaulted,321

Data Structure:

$VAR1 = {
'xmlns:i' => 'http://www.w3.org/2001/XMLSchema-instance',
'xmlns' => 'http://example.com',
'UserRequest' => {
'i1' => {
'Id' => 'e012',
'Dependencies' => [
{}
],
'xmlns:z' => 'http://schemas.microsoft.com/2003/10/Serialization/',
'IdentityUserNumber' => '123',
'Stage' => 'UserFaulted',
'StartTimestamp' => '2016-04-29T00:05:11',
'HomeFileName' => 'filename1.req',
'UseBypass' => 'false'
},
'i2' => {
'Id' => 'e013',
'Dependencies' => [
{}
],
'xmlns:z' => 'http://schemas.microsoft.com/2003/10/Serialization/',
'IdentityUserNumber' => '321',
'Stage' => 'UserFaulted',
'StartTimestamp' => '2016-04-19T19:50:18',
'HomeFileName' => 'filename2.req',
'UseBypass' => 'false'
}

}
};


Here is what i have so far, at this point im starting to think i shot myself in the foot, but any feedback or suggestions would be greatly appreciated

#!/usr/bin/perl
use strict;
use warnings;
use XML::Simple qw(:strict);
use Data::Dumper;



my $time = "2016-04-19";

my $api_faultedreqs = `curl -x 111.222.333.444:8080 -U user:pass -H "Accept: application/xml" -H "Content-Type: application/xml" "https://example.com" 2>/dev/null`;



my $xml_fault_reqs = XMLin($api_faultedreqs, KeyAttr => { UserRequest => 'Id' }, ForceArray => [ 'UserRequest', 'Dependencies' ]);
my %xml_fault_reqs = %$xml_fault_reqs;
my %clean_out = ();


print Dumper($xml_fault_reqs);

#print $xml_fault_reqs->{UserRequest}->{i1}->{HomeFileName};

for my $outer_key (keys %xml_fault_reqs){
next if $outer_key =~/xmlns/;
for my $req_ids2 (keys %{ $xml_fault_reqs{$outer_key} }){
for my $req_data (keys %{ $xml_fault_reqs{$outer_key}{$req_ids2} }){
next if $req_data =~/xmlns/ or $req_data =~/Dependencies/ or $req_data =~/UseBypass/ or $req_data =~/EndTimestamp/;
#print "$req_data, $xml_fault_reqs{$outer_key}{$req_ids2}{$req_data}\n";
print "$xml_fault_reqs{$outer_key}{$req_ids2}{HomeFileName}, $xml_fault_reqs{$outer_key}{$req_ids2}{Stage}\n";
}
}
}


XML output as requested:

<ArrayOfUserRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://example.com">
<UserRequest xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Id="i1">
<Dependencies/>
<HomeFileName>filename1.req</HomeFileName>
<IdentityUserNumber>123</IdentityUserNumber>
<Stage>UserFaulted</Stage>
<StartTimestamp>2016-04-29T00:05:11</StartTimestamp>
<UseBypass>false</UseBypass>
</UserRequest>
<UserRequest xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Id="i2">
<Dependencies/>
<HomeFileName>filename2.req</HomeFileName>
<IdentityUserNumber>321</IdentityUserNumber>
<Stage>UserFaulted</Stage>
<StartTimestamp>2016-04-20T15:44:51</StartTimestamp>
<UseBypass>false</UseBypass>
</UserRequest>



Answer

As simbabque intimated in his comment, XML::Simple is generally frowned upon for a number of reasons. You may want to read the Stack Overflow question Why is XML::Simple "Discouraged"? to understand better why that is

However you immediate problem is how to navigate a fairly ordinary Perl nested data structure, and you will find a useful tutorial on that in perldoc perlreftut

Here's a simple solution to your problem. The items of interest are the values of the second-level hash that has UserRequest, so this program iterates over those and prints the required fields from each of them

The printf uses a hash slice to access all three fields at once, with keys HomeFileName, Stage, and IdentityUserNumber. The printf format displays all three on a single line in the format that you have asked for

use strict;
use warnings qw/ all FATAL /;

use XML::Simple;

my $data = {
    'xmlns:i'     => 'http://www.w3.org/2001/XMLSchema-instance',
    'xmlns'       => 'http://example.com',
    'UserRequest' => {
        'i1' => {
            'Id'                 => 'e012',
            'Dependencies'       => [ {} ],
            'xmlns:z'            => 'http://schemas.microsoft.com/2003/10/Serialization/',
            'IdentityUserNumber' => '123',
            'Stage'              => 'UserFaulted',
            'StartTimestamp'     => '2016-04-29T00:05:11',
            'HomeFileName'       => 'filename1.req',
            'UseBypass'          => 'false'
        },
        'i2' => {
            'Id'                 => 'e013',
            'Dependencies'       => [ {} ],
            'xmlns:z'            => 'http://schemas.microsoft.com/2003/10/Serialization/',
            'IdentityUserNumber' => '321',
            'Stage'              => 'UserFaulted',
            'StartTimestamp'     => '2016-04-19T19:50:18',
            'HomeFileName'       => 'filename2.req',
            'UseBypass'          => 'false'
        }
    }
};

for my $request ( values %{ $data->{UserRequest} } ) {
    printf "%s, %s,%s\n", @{$request}{qw/ HomeFileName  Stage  IdentityUserNumber  /};
}

output

filename1.req, UserFaulted,123
filename2.req, UserFaulted,321