Perl哈希。迭代foreach

时间:2013-01-31 21:43:17

标签: arrays perl hash foreach

Perl哈希有一些问题。 我有一个XML:

<?xml version="1.0" encoding="utf-8"?>
<SvnRequestUsers>
  <AccessCode>$TR0ngP@ssvv0rd!</AccessCode>
  <SvnUsers>
    <SvnUser>
      <Username>svn.testname</Username>
      <Password>jA=B*+q%</Password>
      <Create>true</Create>
    </SvnUser>
    <SvnUser>
      <Username>svn.testname2</Username>
      <Password>jA=B*+q%</Password>
      <Create>true</Create>
    </SvnUser>
  </SvnUsers>
</SvnRequestUsers>

我想循环通过SvnUser节点。当我使用

我的$ usersList = $ ref-&gt; {&#39; SvnUsers&#39;};

foreach my $key ( @{$usersList->{'SvnUser'}} )
{ ..... }

当有多个节点但不适用于一个节点时,这是有效的。

使用时

我的@usersList = $ ref-&gt; {&#39; SvnUsers&#39;} - &gt; {&#39; SvnUser&#39;};

foreach my $key ( @usersList )
{ ..... }

仅在恰好有一个节点时才有效。 诀窍在哪里???

5 个答案:

答案 0 :(得分:2)

如果您使用的是XML :: Simple,您可能想要全局启用ForceArray(这可能需要您在代码中添加更多数组解引用)或者可能具有一个或多个节点的特定元素;见https://metacpan.org/module/XML::Simple#ForceArray-names-in---important

答案 1 :(得分:1)

这看起来像是远离简单XML::Simple的工作。我相信它通过成为第一个声称它的命名空间而赢得了它的知名度,而IMO它有很多缺点。

如果我的猜测是正确的,那么你可以通过enabling the ForceArray option克服一些尴尬。模块的POD说

  

检查ForceArray,因为你几乎肯定想要打开它

在您的情况下,这将确保$ref->{SvnUsers}{SvnUser} 总是一个数组引用,即使XML中的那个点上只有一个<SvnUser>元素。因此$ref->{SvnUsers}{SvnUser}[0]将始终访问第一个或唯一的用户元素。

我的建议是升级到更一致的XML模块。 XML::Smart允许以与XML::Simple结构非常相似的方式访问数据,但它广泛使用tie来提供更好的DWIM接口。例如,在XML::Smart下,可以使用SvnUserSvnUsers访问第一个$ref->{SvnUsers}{SvnUser}元素的第一个$ref->{SvnUsers}[0]{SvnUser}[0]

只需切换到XML::Smart即可解决您的问题。例如,下面的代码将列出所有用户的用户名,无论是否有一个或多个用户名(或者实际上没有用户名,因为在这种情况下对象将返回一个空列表)。

我自己的偏好是一个“适当的”XML模块,如XML::LibXMLXML::Twig,但当然选择是你的。

use strict;
use warnings;

use XML::Smart;

my $ref = XML::Smart->new(\*DATA);

my $users_list= $ref->{SvnRequestUsers}{SvnUsers};

foreach my $key ( @{ $users_list->{SvnUser} } ) {
  print $key->{Username}, "\n";
}

__DATA__
<?xml version="1.0" encoding="utf-8"?>
<SvnRequestUsers>
  <AccessCode>$TR0ngP@ssvv0rd!</AccessCode>
  <SvnUsers>
    <SvnUser>
      <Username>svn.testname</Username>
      <Password>jA=B*+q%</Password>
      <Create>true</Create>
    </SvnUser>
    <SvnUser>
      <Username>svn.testname2</Username>
      <Password>jA=B*+q%</Password>
      <Create>true</Create>
    </SvnUser>
  </SvnUsers>
</SvnRequestUsers>

<强>输出

svn.testname
svn.testname2

答案 2 :(得分:0)

你用什么模块来读取ahref中的XML?如果有更多用户,我猜你需要像这样访问你的数据:

my @usersList = $ref->{'SvnUsers'}->[0]->{'SvnUser'};

这会给你第一个用户,因为在'SvnUsers'键下会有一个arrayref作为值

迭代可能如下所示:

my $maxusers = scalar(@{$ref->{'SvnUsers'}}); # get the number of elements in your ref
for my $i(0 .. $maxusers){
    my $svnuser = $ref->{'SvnUsers'}->[$i]->{'SvnUser'}; # get current
    ....... # do your stuff
}

答案 3 :(得分:0)

尝试这样做:

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

use XML::XPath;
use XML::XPath::XMLParser;

my $xp = XML::XPath->new(filename => 'file.xml');

my $nodeset = $xp->find('/SvnRequestUsers/SvnUsers/SvnUser'); # find svn users

foreach my $node ($nodeset->get_nodelist) {
    print "FOUND\n\n",
        XML::XPath::XMLParser::as_string($node),
        "\n\n";
}

结果:

FOUND

<SvnUser>
      <Username>svn.testname</Username>
      <Password>jA=B*+q%</Password>
      <Create>true</Create>
    </SvnUser>

FOUND

<SvnUser>
      <Username>svn.testname2</Username>
      <Password>jA=B*+q%</Password>
      <Create>true</Create>
    </SvnUser>

答案 4 :(得分:0)

像往常一样,我会推荐Mojo::DOM套件的Mojolicious。使用选择器和转换/迭代器方法,问题变得非常简单:

#!/usr/bin/env perl

use strict;
use warnings;

use Mojo::DOM;

my $dom = Mojo::DOM->new(<<'END');
<?xml version="1.0" encoding="utf-8"?>
<SvnRequestUsers>
  <AccessCode>$TR0ngP@ssvv0rd!</AccessCode>
  <SvnUsers>
    <SvnUser>
      <Username>svn.testname</Username>
      <Password>jA=B*+q%</Password>
      <Create>true</Create>
    </SvnUser>
    <SvnUser>
      <Username>svn.testname2</Username>
      <Password>jA=B*+q%</Password>
      <Create>true</Create>
    </SvnUser>
  </SvnUsers>
</SvnRequestUsers>
END

# get an array

my @users = $dom->find('SvnUser')
                ->pluck('Username')
                ->pluck('text')
                ->each;

# or do something with each element

$dom->find('SvnUser')
    ->pluck('Username')
    ->pluck('text')
    ->each(sub{ print "$_\n" });

当然,如果您实际上只是想要用户名,那么您可能只使用更加量身定制的选择器find('SvnUser Username'),上面的示例就是显示方法。