如何最好地通过此 perl 数据结构重申所需的输出?不重建现有的是否可能?

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

这里是 Perl 初学者,我有一个脚本可以进行 api 调用,以 xml 格式收集反馈,然后使用 XML::Simple,将数据按摩到下面的数据结构中,我正在尝试拍摄以下输出:

filename1.req, UserFaulted,123

filename2.req,用户故障,321

数据结构:

$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'
                             }

                     }
    };

这是我目前所知道的,在这一点上我开始认为我搬起石头砸自己的脚,但是任何反馈或建议将不胜感激

#!/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 按要求输出:

<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>

作为 , XML::Simple is generally frowned upon for a number of reasons. You may want to read the Stack Overflow question 更好地理解为什么会这样

但是您眼前的问题是如何导航一个相当普通的 Perl 嵌套数据结构,您将在 perldoc perlreftut

中找到一个有用的教程

这是解决您的问题的简单方法。感兴趣的项目是具有 UserRequest 的二级散列的值,因此该程序迭代这些值并从每个项目中打印所需的字段

printf 使用散列 切片 一次访问所有三个字段,键为 HomeFileNameStageIdentityUserNumberprintf 格式以您要求的格式在一行中显示所有三个

use strict;
use warnings 'all';

use XML::Simple;

# my $data = XMLin(...);

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  /};
}

产出

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

感谢您展示 XML 数据。很有帮助

这是一个使用 XML::LibXML module to parse the data. It's a little more complicated than it could be because your data uses the default namespace with xmlns="http://example.com". That namespace must be defined and used explicitly in an XPath expression, which means you also need to create an XPath context object using the XML::LibXML::XPathContext 模块的解决方案。这允许您注册名称空间并在 XPath 表达式中使用它们。即使是默认命名空间也必须有一个名称,所以我将其命名为 nul,并在每个节点名称前加上前缀 nul:

代码很简单。它使用 findnodes 定位所有 UserRequest 节点,并从每个节点中提取 HomeFileNameStageIdentityUserNumber 子节点的值,打印结果printf 通话

use strict;
use warnings 'all';
use feature 'say';

use XML::LibXML;

my $dom = XML::LibXML->load_xml(IO => \*DATA);
my $xpc = XML::LibXML::XPathContext->new($dom);
$xpc->registerNs(nul => 'http://example.com');

for my $request ( $xpc->findnodes('/nul:ArrayOfUserRequest/nul:UserRequest') ) {

    printf "%s, %s,%s\n",
            $xpc->findvalue('nul:HomeFileName', $request),
            $xpc->findvalue('nul:Stage', $request),
            $xpc->findvalue('nul:IdentityUserNumber', $request);
}

__DATA__
<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>
</ArrayOfUserRequest>

产出

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

您似乎只是想打印元素 HomeFileNameStage

既然如此,使用 XML::Twig 之类的东西可以让您:

use XML::Twig;

my @things = qw ( HomeFileName Stage IdentityUserNumber ); 

my $twig = XML::Twig->parse($api_faultedreqs);

foreach my $user_request ( $twig->get_xpath('//UserRequest') ) {
    print join ",", (map { $user_request -> first_child_text($_) } @things), "\n";
}