【问题标题】:How to get a value from deep within a hash in perl如何从perl中的哈希深处获取值
【发布时间】:2016-04-08 09:28:14
【问题描述】:

我正在读取一个 xml 文件,

$myxml = XMLin("$configfile");

当我使用 Dumper (print Dumper($myxml);) 打印它时,我得到了这个......

$VAR1 = {
          'Timeout' => 5,
          'Roots' => {
                        'Root' => [
                                     {
                                       'Name' => 'Sales',
                                       'Level' => 'Indeterminate',
                                       'Profiles' => {
                                                    'Profile' => [
                                                                {
                                                                  'Name' => 'Bill',
                                                                  'Age' => '50',
                                                                  'Status' => Active
                                                                },
                                                                {
                                                                  'Name' => 'Bob',
                                                                  'Age' => '24',
                                                                  'Status' => Inactive
                                                                }
                                                              ]
                                                  },
                                       'Interval' => 'Order',
                                       'Action' => 'Reject'
                                     },
                                     {
                                      'Name' => 'User',
                                      'Level' => 'Indeterminate',
                                      'Profiles' => {
                                                   'Profile' => [
                                                            {
                                                              'Name' => 'User',
                                                              'Action' => 'Reject',
                                                              'User' => 'acount'
                                                             }, 
                                                            {
                                                              'Name' => 'Admin',
                                                              'Action' => 'Accept',
                                                              'User' => 'acount'
                                                             },                                                                   
                                   ]
                      }
        };    

我想读取此哈希并获取所有非活动“状态”的值,或者获取“鲍勃的状态”..

{
'Name' => 'Bob',
'Age' => '24',
'Status' => Inactive
}

开始编辑:

所以要获取一个人的个人资料信息..

Dumper($myxml->{'Roots'}->{'Root'}[0]{'Profiles'}{'Profile'}[2]); 

例如获取 Bob 的状态

if ($myxml->{'Roots'}->{'Root'}[0]{'Profiles'}{'Profile'}[1]{'Name'} eq "Bob") {
$status = $myxml->{'Roots'}->{'Root'}[0]{'Profiles'}{'Profile'}[1]{'Status'};
}

但是,如果 Bob 不在位置 [0] 中,我如何循环遍历此 xml,以便它会继续检查 {'Roots'}->{'Root'} 和 {'Profiles'}{'Profile'}和 [1]。双 foreach 循环?

结束编辑

我已经包含了一个 xml 的示例..

<Root Name="Sales" Level="Indeterminate" Profile="Order" Interval="Order" Action="Reject">
  <Profiles>
    <Profile Name="Bill" Age="50" Status=Active />
    <Profile Name="Bob" Age="24" Status=InActive />
    <Profile Name="Ben" Age="45" Status=Active />
  </Profiles>
</Root>

产生这个:

$VAR1 = {
      'Name' => 'Sales',
      'Type' => 'Indeterminate',
      'Profiles' => {
                   'Profile' => [

                               {
                                 'Name' => 'Bill',
                                 'Age' => '50',
                                 'Status' => Active
                               },
                               {
                                 'Name' => 'Bob',
                                 'Age' => '24',
                                 'Status' => InActive
                               },
                               {
                                 'Name' => 'Ben',
                                 'Age' => '45',
                                 'Status' => Active
                               }
                             ]
                 },
      'Interval' => 'Order',
      'Action' => 'Reject'
    };

谢谢,

约翰。

【问题讨论】:

  • 如果您给我们一些示例 XML,我们可以向您展示一个更简单的解决方案。我已经猜测了您的源 XML 是什么样子来说明这个概念的。

标签: perl hash data-dumper


【解决方案1】:

您已经找到了如何使用 XML::Simple 执行此操作的答案。但我建议不要,而是使用XML::Twig,这样就不会那么讨厌了。

Why is XML::Simple "Discouraged"?

我将假设您的 XML 看起来有点像这样:

<opt Timeout="5">
  <Roots>
    <Root Action="Reject" Interval="Order" Level="Indeterminate" Name="Sales">
      <Profiles>
        <Profile Age="50" Name="Bill" Status="Active" />
        <Profile Age="24" Name="Bob" Status="Inactive" />
      </Profiles>
    </Root>
  </Roots>
</opt>

我无法确定,因为那是XML::Simple 的乐趣所在。但是:

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;

my $twig = XML::Twig -> new -> parsefile ( $configfile );

print $twig -> get_xpath ( '//Profile[@Name="Bob"]',0 ) -> att('Status')

这使用xpath 来定位您想要的属性 - // 表示“树中的任何位置”搜索。

但你可以:

print $twig -> get_xpath ( '/opt/Roots/Root/Profiles/Profile[@Name="Bob"]',0 ) -> att('Status')

更简单你不同意吗?

或迭代所有“配置文件”:

foreach my $profile ( $twig -> get_xpath ('//Profile' ) ) {
    print $profile -> att('Name'), " => ", $profile -> att('Status'),"\n";
}

【讨论】:

    【解决方案2】:

    Root 的值是一个ArrayRef。您忽略了数组并将其视为其中的 HashRef 之一。您需要遍历数组或使用特定索引访问它。

    【讨论】:

      【解决方案3】:

      这是XML::Simple is strongly deprecated 的原因之一 - 当您期望哈希引用时,它偶尔会抛出数组引用。

      您可以看到$rulesxml-&gt;{'Roots'}-&gt;{'Root'} 的值是一个数组引用,而不是一个哈希引用,因为它以[ 而不是{ 开头。

      如果数据正是您在此处显示的,那么您需要做的就是在代码中插入一个数组查找。

      Dumper($myxml->{'Roots'}->{'Root'}->[0]->{'Profiles'}->{'Profile'}); 
      

      我使用了 0,因为(目前?)该数组中只有一个元素。如果您的数据比较复杂并且数组中有多个元素,那么您将需要使用循环。

      对于这样的工作,我强烈建议花时间学习 XPath 并使用支持该功能的模块(我喜欢 XML::LibXML)。

      简化代码的其他几种方法:

      1. 您在 XMLin() 调用中不必要地引用了变量。

        $myxml = XMLin($configfile); # Works fine without quotes

      2. 您可以在多级查找中省略中间箭头。

        $myxml-&gt;{'Roots'}{'Root'}[0]{'Profiles'}{'Profile'}

      3. 您还可以省略散列键名称周围的大多数引号。

        $myxml-&gt;{Roots}{Root}[0]{Profiles}{Profile}

      更新:我不愿意为您提供您添加的问题的解决方案,因为(正如我们在这里从不厌倦指出的那样)XML::Simple 是大多数 XML 问题的糟糕解决方案。

      但是,给定您的数据结构(或者更确切地说,我已经清理过的数据结构的一个版本,因此它实际上可以编译!)这就是您遍历它的方式(是的,它是一个嵌套循环)。

      for my $root (@{ $myxml->{Roots}{Root} }) {
        for my $profile (@{ $root->{Profiles}{Profile} }) {
          if ($profile->{Name} eq 'Bob') {
            say $profile->{Status};
          }
        }
      }
      

      但请不要使用这种方法。 Sobrique 建议的XML::Twig approach 是一个更好的答案。基于 XML::LibXML 的 XPath 方法也类似。

      无论何时从 XML 文档中提取数据,XPath 都可能是最好的解决方案。如果您正在处理 XML,那么您确实需要在您的工具包中包含 XPath。

      【讨论】:

      • 将代码放在注释中是个糟糕的主意。也许编辑您的问题以添加新的详细信息。
      • 对不起 cmets 流,我粘贴了一个可爱的格式化回复,但 cmets 打印出来没有空格。
      • 是的,是的,这是个糟糕的主意。我已经更新了上面的问题。
      猜你喜欢
      • 2016-07-22
      • 2010-09-08
      • 2021-12-30
      • 2020-11-24
      • 1970-01-01
      • 2014-10-17
      • 2013-02-12
      • 1970-01-01
      • 2010-12-19
      相关资源
      最近更新 更多