【问题标题】:Perl hashes. iterate through foreachPerl 哈希。遍历 foreach
【发布时间】:2013-01-16 03:21:08
【问题描述】:

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 )
{ ..... }

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

【问题讨论】:

  • $ref 是
  • 你用的是什么 xml 解析器?
  • @ysth,当然是 XML::Simple

标签: arrays perl hash foreach


【解决方案1】:

您使用哪个模块来读取 ahref 中的 XML?我猜如果有更多用户,您将需要像这样访问您的数据:

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

这会给你第一个用户,因为在'SvnUsers'-key下会有一个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
}

【讨论】:

    【解决方案2】:

    尝试这样做:

    #!/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>
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        这看起来像极不简单的XML::Simple 的工作。我相信它通过成为第一个声明其命名空间的人而赢得了人气,而 IMO 它有很多缺点。

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

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

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

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

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

        我自己的偏好是像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
        

        【讨论】:

        • 谢谢,ForceArray 是我需要的东西。快速简单
        【解决方案5】:

        像往常一样,我会推荐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-12-30
          • 2016-08-20
          • 2011-10-28
          • 1970-01-01
          • 2022-07-21
          • 2012-08-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多