Возвращение хеша проанализированного документа (используя 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
использует это. И в немалой степени проиллюстрировано, почему, форсируя ваши данные в хэш / массив, вы потеряете данные.