Возвращение хеша проанализированного документа (используя Twig в Perl) для использования в других подпрограммах

Мне ужасно не удается вернуть хэш анализируемого XML-документа с помощью ветки - чтобы использовать его в других подпрограммах для выполнения нескольких проверок валидации. Цель состоит в том, чтобы сделать абстракцию и создать повторно используемые блоки кода.

Блок XML:

<?xml version="1.0" encoding="utf-8"?>
<Accounts locale="en_US">
  <Account>
    <Id>abcd</Id>
    <OwnerLastName>asd</OwnerLastName>
    <OwnerFirstName>zxc</OwnerFirstName>
    <Locked>false</Locked>
    <Database>mail</Database>
    <Customer>mail</Customer>
    <CreationDate year="2011" month="8" month-name="fevrier" day-of-month="19" hour-of-day="15" minute="23" day-name="dimanche"/>
    <LastLoginDate year="2015" month="04" month-name="avril" day-of-month="22" hour-of-day="11" minute="13" day-name="macredi"/>
    <LoginsCount>10405</LoginsCount>
    <Locale>nl</Locale>
    <Country>NL</Country>
    <SubscriptionType>free</SubscriptionType>
    <ActiveSubscriptionType>free</ActiveSubscriptionType>
    <SubscriptionExpiration year="1980" month="1" month-name="janvier" day-of-month="1" hour-of-day="0" minute="0" day-name="jeudi"/>
    <SubscriptionMonthlyFee>0</SubscriptionMonthlyFee>
    <PaymentMode>Undefined</PaymentMode>
    <Provision>0</Provision>
    <InternalMail>asdf@asdf.com</InternalMail>
    <ExternalMail>fdsa@zxczxc.com</ExternalMail>
    <GroupMemberships>
      <Group>werkgroep X.Y.Z.</Group>
    </GroupMemberships>
    <SynchroCount>6</SynchroCount>
    <LastSynchroDate year="2003" month="12" month-name="decembre" day-of-month="5" hour-of-day="12" minute="48" day-name="mardi"/>
    <HasActiveSync>false</HasActiveSync>
    <Company/>
  </Account>
  <Account>
    <Id>mnbv</Id>
    <OwnerLastName>cvbb</OwnerLastName>
    <OwnerFirstName>bvcc</OwnerFirstName>
    <Locked>true</Locked>
    <Database>mail</Database>
    <Customer>mail</Customer>
    <CreationDate year="2012" month="10" month-name="octobre" day-of-month="10" hour-of-day="10" minute="18" day-name="jeudi"/>
    <LastLoginDate/>
    <LoginsCount>0</LoginsCount>
    <Locale>fr</Locale>
    <Country>BE</Country>
    <SubscriptionType>free</SubscriptionType>
    <ActiveSubscriptionType>free</ActiveSubscriptionType>
    <SubscriptionExpiration year="1970" month="1" month-name="janvier" day-of-month="1" hour-of-day="1" minute="0" day-name="jeudi"/>
    <SubscriptionMonthlyFee>0</SubscriptionMonthlyFee>
    <PaymentMode>Undefined</PaymentMode>
    <Provision>0</Provision>
    <InternalMail/>
    <ExternalMail>qweqwe@qwe.com</ExternalMail>
    <GroupMemberships/>
    <SynchroCount>0</SynchroCount>
    <LastSynchroDate year="1970" month="1" month-name="janvier" day-of-month="1" hour-of-day="1" minute="0" day-name="jeudi"/>
    <HasActiveSync>false</HasActiveSync>
    <Company/>
  </Account>
</Accounts>

Perl Block:

my $file = shift || (print "NOTE: \tYou didn't provide the name of the file to be checked.\n" and exit);
my $twig = XML::Twig -> new ( twig_roots => { 'Account' => \& parsing } ); #'twig_roots' mode builds only the required sub-trees from the document while ignoring everything outside that twig.
$twig -> parsefile ($file);

sub parsing {
    my ( $twig, $accounts ) = @_;
    my %hash = @_;
    my $ref = \%hash; #because was getting an error of Odd number of hash elements
    return $ref;
    $twig -> purge;

Он дает ссылку на хеш - которую я не могу правильно обработать (даже после тысячи попыток).

Опять же - просто нужна одна чистая функция (sub) для выполнения синтаксического анализа и возврата хеша всех элементов (в данном случае 'Accounts') - для использования в другой функции (valid_sub) для выполнения проверок валидации.

Я буквально застрял в этой точке - и буду очень признателен за вашу помощь.

2 ответа

Решение

Такой хэш не создан Twig, вы должны создать его самостоятельно.

Осторожно: команды после return никогда не будет достигнут

#!/usr/bin/perl
use warnings;
use strict;

use XML::Twig;
use Data::Dumper;

my $twig = 'XML::Twig'->new(twig_roots => { Account => \&account });
$twig->parsefile(shift);

sub account {
    my ($twig, $account) = @_;
    my %hash;
    for my $ch ($account->children) {
        if (my $text = $ch->text) {
            $hash{ $ch->name } = $text;
        } else {
            for my $attr (keys %{ $ch->atts }) {
                $hash{ $ch->name }{$attr} = $ch->atts->{$attr};
            }
        }
    }
    print Dumper \%hash;
    $twig -> purge;
    validate(\%hash);
}

Обработка вложенных элементов (например, GroupMemberships) оставлено в качестве упражнения для читателя.

И для проверки:

sub validate {
    my $account = shift;
    if ('abcd' eq $account->{Id}) {
        ...
    }
}

Проблема с понижением частоты XML в хеши, это что XML принципиально более сложная структура данных. У каждого элемента есть свойства, дочерние элементы и контент - и они упорядочены - где хэши... нет.

Поэтому я бы посоветовал вам не делать то, что вы делаете, и вместо передачи хеша использовать XML::Twig::Elt и передать это в вашу проверку.

К счастью, это именно то, что XML::Twig переходит к его обработчикам:

## this is fine:
sub parsing {
    my ( $twig, $accounts ) = @_;

но это чепуха - подумайте о том, что в @_ на данный момент - это ссылки на XML::Twig объекты - два из них, вы только что назначили их.

    my %hash = @_;

И это не имеет смысла в результате

    my $ref = \%hash; #because was getting an error of Odd number of hash elements

И куда ты его возвращаешь? (это вызывается при разборе XML::Twig)

    return $ref;
    #this doesn't happen, you've already returned
    $twig -> purge;

Но имейте в виду - вы возвращаете его в ваш процесс ветки, который разбирает, это... отбрасывает код возврата. Так что в любом случае это ничего не сделает.

Я бы предложил вместо этого "сохранить" $accounts ссылаться и использовать это для проверки - просто передайте его в подпрограммы для проверки.

Или еще лучше, настроить набор twig_handlers которые делают это для вас

my %validate = ( 'Account/Locked' => sub { die if $_ -> trimmed_text eq "true" },
                 'Account/CreationDate' => \&parsing, 
                 'Account/ExternalMail' => sub { die unless $_ -> text =~ m/\w+\@\w+\.\w+ } 
               );


my $twig = XML::Twig -> new ( twig_roots => \%validate );

Вы также можете die если вы хотите отказаться от всего, или использовать такие вещи, как cut удалить неверную запись из документа при разборе. (и возможно paste это в отдельный документ).

Но если вам действительно нужно превратить ваш XML в структуру данных perl - сначала прочтите это, почему это ужасная идея:

Почему XML::Simple "не рекомендуется"?

И затем, если вы действительно хотите продолжить этот путь, посмотрите на simplify вариант XML::Twig:

sub parsing {
    my ( $twig, $accounts ) = @_;
    my $horrible_hacky_hashref = $accounts->simplify(forcearray => 1, keyattr => [], forcecontent => 1  );
    print Dumper \$horrible_hacky_hashref;
    $twig -> purge;
    #do something with it. 
}

Редактировать:

Расширять:

XML::Twig::Elt это подмножество XML::Twig - это "строительный блок" XML::Twig структура данных - так в вашем примере выше, $accounts является.

sub parsing {
    my ( $twig, $accounts ) = @_;
    print Dumper $accounts;
}

Если вы сделаете это, вы получите много данных, потому что вы сбрасываете всю структуру данных, которая фактически является последовательной цепочкой XML::Twig::Elt объекты.

$VAR1 = \bless( {
                   'parent' => bless( {
                                        'first_child' => ${$VAR1},
                                        'flushed' => 1,
                                        'att' => {
                                                   'locale' => 'en_US'
                                                 },
                                        'gi' => 6,

....

                    'att' => {},
               'last_child' => ${$VAR1}->{'first_child'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'}->{'next_sibling'},
               'gi' => 7
             }, 'XML::Twig::Elt' );

Но он уже содержит информацию, которая вам нужна, а также структуру, которая вам требуется - вот почему XML::Twig использует это. И в немалой степени проиллюстрировано, почему, форсируя ваши данные в хэш / массив, вы потеряете данные.

Другие вопросы по тегам