1

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->{'SvnUsers'};

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

这在有多个节点但不适用于一个节点时有效。

使用时

我的@usersList = $ref->{'SvnUsers'}->{'SvnUser'};

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

这仅在只有一个节点存在时才有效。诀窍在哪里???

4

5 回答 5

2

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

于 2013-01-31T22:23:34.617 回答
1

这看起来像是 far-from-simple 的工作XML::Simple。我确信它通过成为第一个声明其命名空间的人而赢得了人气,而 IMO 它有很多缺点。

如果我的猜测是正确的,那么您可以通过启用该ForceArray选项来克服一些尴尬。模块的 POD 说

查看 ForceArray,因为您几乎肯定会想要打开它

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

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

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

我自己的偏好是一个“适当的”XML 模块,比如XML::LibXMLor XML::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
于 2013-01-31T22:55:07.530 回答
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
}
于 2013-01-31T21:58:10.407 回答
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>
于 2013-01-31T22:03:01.227 回答
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'),我上面的示例是展示一个方法。

于 2013-02-01T04:53:13.523 回答