Понимание многоуровневой ссылки XML с использованием Perl XML Parser?

Я прочесывал Интернет в течение 2 дней, пытаясь найти ответы на вопрос, как правильно ссылаться на многоуровневый XML-файл с помощью Perl XML Parsers. Я новичок в Perl, и это мой первый пост на этом форуме, так что мне есть чему поучиться. Я начинаю с XML::Simple. Я понимаю, что некоторые предпочитают другие библиотеки.

Пример XML-файла:

<events>
    <event>
        <EventObject>Application</EventObject>
        <EventType>Start</EventType>
        <Operation></Operation>
        <EventTimestamp>Sat 11/21/2015-14:02:57.76</EventTimestamp>
    </event>
    <source>
        <UserIPAddr>192.168.1.2</UserIPAddr>
        <UserHostName>ABC-PROD-BAR-15-01A</UserHostName>
        <UserUUID>EC2-User</UserUUID>
    </source>
    <target>
        <URL>"https://foo.com/"</URL>
    </target>
    <payload>
        <FormData></FormData>
        <PackageFilename></PackageFilename>
    </payload>

    <event>
        <EventObject>User</EventObject>
        <EventType>Download</EventType>
        <Operation>Acknowledge License</Operation>
        <EventTimestamp>Sat 11/21/2015-14:03:10.44</EventTimestamp>
    </event>
    <source>
        <UserIPAddr>10.120.30.4</UserIPAddr>
        <UserHostName>WSM24CN502</UserHostName>
        <UserUUID>simpson homer 750329 </UserUUID>
    </source>
    <target>
        <URL>"https://dev.catalog.com/"</URL>
    </target>
    <payload>
        <FormData></FormData>
        <PackageFilename>"eclipse.luna.5.2.tag.gz"</PackageFilename>
    </payload>
</events>

Пример кода:

#!perl

# use module
use XML::Simple;
use Data::Dumper;
use XML::Parser;

# create object
$xml = new XML::Simple (KeyAttr=>[]);

# read XML file
my $data = $xml->XMLin("auditfile3.xml",forcearray=>1);
#$data = $xml->XMLin("auditfile3.xml",KeyAttr=>{EventRecord=>'Event'});
print Dumper($data);

#print $data->{Events}->{Event};

#my $EventRecord = $data->{EventRecord};
#print Dumper($EventRecord);

#print $EventRecord->{EventObject};
#print $data->{EventObject};

# dereference hash ref
# access <EventRecord> array

foreach my $e (@{$data->{Event}})
    {
     print "EventObject: ",$e->{Event->{EventObject}}, "\n";
     print "EventType:  ", $e->{EventType}, "\n"; 
     print "Operation: ", $e->{Operation}, "\n";
     print "Timestamp: ", $e->{EventTimestamp}, "\n";
    }

person bbboomer    schedule 23.11.2015    source источник
comment
Когда люди рекомендуют не использовать XML::Simple, это потому, что они испытали боль и знают, что это причинит вам боль. Откажитесь от этого как можно раньше :-)   -  person Grant McLean    schedule 23.11.2015
comment
Почему XML::Simple не рекомендуется   -  person Sobrique    schedule 23.11.2015


Ответы (2)


Имена элементов XML чувствительны к регистру. Кроме того, у вас есть некоторые синтаксические ошибки в коде.

my $xml = 'XML::Simple'->new(KeyAttr => [], ForceArray => 1);
my $data = $xml->XMLin(...);

for my $e (@{ $data->{event} }) {
    print "EventObject: ", $e->{EventObject}[0], "\n";
    print "EventType: ", $e->{EventType}[0], "\n";
    print "Operation: ", ref $e->{Operation}[0] ? '-empty-'
                                                : $e->{Operation}[0], "\n";
    print "Timestamp: ", $e->{EventTimestamp}[0], "\n";
}
person choroba    schedule 23.11.2015
comment
Спасибо Хороба. Решение самое скромное. - person bbboomer; 23.11.2015
comment
почему необходимо ссылаться на массив [0]? И в чем разница между for и foreach в этом случае? Я думал, что между 'foreach' и @ он подбирает каждый элемент в массиве, созданном XMLin? - person bbboomer; 23.11.2015
comment
@bbboomer: ForceArray везде создает ссылки на массив, поэтому вам нужно использовать [0], чтобы получить их первый элемент. for и foreach — это одна и та же команда, но for можно набирать быстрее. - person choroba; 23.11.2015
comment
Еще раз спасибо за быстрый ответ на мои вопросы и решение. - person bbboomer; 23.11.2015

Используйте XML::LibXML

#!/usr/bin/env perl

use strict;
use warnings;
use feature qw(say);

use XML::LibXML;

my $xml = XML::LibXML->load_xml( IO => \*DATA );

for my $node ( $xml->findnodes('//event') ) {
    for my $property (qw(EventObject EventType Operation EventTimestamp)) {
        next unless my ($child) = $node->findnodes($property);
        say "$property: ", $child->textContent();
    }

    say '';
}

__DATA__
<events>
    <event>
        <EventObject>Application</EventObject>
        <EventType>Start</EventType>
        <Operation></Operation>
        <EventTimestamp>Sat 11/21/2015-14:02:57.76</EventTimestamp>
    </event>
    <source>
        <UserIPAddr>192.168.1.2</UserIPAddr>
        <UserHostName>ABC-PROD-BAR-15-01A</UserHostName>
        <UserUUID>EC2-User</UserUUID>
    </source>
    <target>
        <URL>"https://foo.com/"</URL>
    </target>
    <payload>
        <FormData></FormData>
        <PackageFilename></PackageFilename>
    </payload>

    <event>
        <EventObject>User</EventObject>
        <EventType>Download</EventType>
        <Operation>Acknowledge License</Operation>
        <EventTimestamp>Sat 11/21/2015-14:03:10.44</EventTimestamp>
    </event>
    <source>
        <UserIPAddr>10.120.30.4</UserIPAddr>
        <UserHostName>WSM24CN502</UserHostName>
        <UserUUID>simpson homer 750329 </UserUUID>
    </source>
    <target>
        <URL>"https://dev.catalog.com/"</URL>
    </target>
    <payload>
        <FormData></FormData>
        <PackageFilename>"eclipse.luna.5.2.tag.gz"</PackageFilename>
    </payload>
</events>

Выходы:

EventObject: Application
EventType: Start
Operation:
EventTimestamp: Sat 11/21/2015-14:02:57.76

EventObject: User
EventType: Download
Operation: Acknowledge License
EventTimestamp: Sat 11/21/2015-14:03:10.44
person Miller    schedule 23.11.2015
comment
Спасибо за ваш ответ и отличный пример решения. Поскольку проблема была решена, я никогда не искал дополнительных вкладов. Очень признателен. - person bbboomer; 03.10.2016