返回已解析文档的哈希值(在 Perl 中使用 Twig)以用于在其他子程序中进行处理

Returning a hash of the Parsed document (using Twig in Perl) to be used for processing in other subs

我非常失败 return 使用 twig 的已解析 XML 文档的哈希 - 以便在其他 subs 中使用它来执行多项验证检查。目标是进行抽象并创建可重复使用的代码块。

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

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;

它提供了一个哈希引用——我无法正确地尊重它(即使做了数千次尝试)。

再次 - 只需要一个干净的函数(子)来进行解析和 returning 所有元素的散列('Accounts' 在这种情况下) - 用于其他其他功能(valid_sub) 用于执行验证检查。

我真的被困在这一点上了 - 非常感谢您的帮助。

这样的哈希不是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)作为练习留给 reader。

并进行验证:

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

XML 向下转换为散列的问题在于 XML 从根本上说是一个更复杂的数据结构。每个元素都有属性、子元素和内容 - 而且它是有序的 - 其中散列...没有。

所以我建议你不要做你正在做的事情,不要传递散列,而是使用 XML::Twig::Elt 并将 that 传递到你的验证中。

幸运的是,这正是 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

你在哪里 return 将它 发送到 ? (这是在 XML::Twig 解析时调用的)

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

但请记住 - 您正在 returning 它到正在解析的 twig 进程,那是......丢弃 return 代码。所以这无论如何都不会做任何事情。

我建议您 'save' 参考 $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::Twigsimplify 选项:

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::EltXML::Twig 的子集 - 它是 XML::Twig 数据结构的 'building block' - 所以在上面的示例中,$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 使用它的原因。并且在很大程度上要说明为什么将您的数据强制放入 hash/array,您将丢失数据。