6

我有一个正在解码的 json 结构,如下所示:

  person => {
    city => "Chicago",
    id => 123,
    name => "Joe Smith",
    pets => {
      cats => [
                { age => 6, name => "cat1", type => "siamese", weight => "10 kilos" },
                { age => 10, name => "cat2", type => "siamese", weight => "13 kilos" },
              ],
      dogs => [
                { age => 7, name => "dog1", type => "siamese", weight => "20 kilos" },
                { age => 5, name => "dog2", type => "siamese", weight => "15 kilos" },
              ],
    },
  },
}

我可以通过以下方式打印city, id:name

foreach my $listing ($decoded->{person})
{ 
    my $city = $listing->{city};
    my $name = $listing->{name};
    name - $city - \n";
}

但是,我不确定如何打印pets->catsor pets->dogs。我可以通过以下方式对它们进行转储:

my @pets = $listing->{pets}->{cats};
dump @pets;

但我不确定如何通过哈希结构访问它们。

4

3 回答 3

9

假设您$listing是一个,您必须取消引用数组和哈希引用。

# as long as we are assuming $listing is a person
# this goes inside the foreach you posted in your
# question.

# this will print all cats' names
foreach my $cat ( @{ $listing->{pets}->{cats} } )
{
    # here $cat is a hash reference

    say $cat->{name}; # cat's name
}

等等其他东西。

要从结构中访问它们,您可以执行以下操作:

say $listing->{pets}->{cats}->[0]->{name}; # this will print 'cat1'
于 2011-11-24T14:18:14.637 回答
9

一旦你知道了规则,挖掘一个大的结构就很简单了:

  • 包装散列键{}
  • 将数组索引包装在[]
  • 如果您的顶级变量是引用,->请在第一个标识符之前使用。
  • 在第一组大括号或方括号之后,附加箭头 ( ->) 是可选的。

所以:*$data->{person}{name}返回'Joe Smith' *$data->{person}->{name}也返回'Joe Smith' *$data->{pets}{cats}[0]{age}返回6

有关此主题的更多详细信息,请参阅Perl Data Structures Cookbook (perldoc perldsc)

当您使用这样的大型结构时,需要注意一些重要的事情。其中最大的是autovivification. Autoviv 意味着 Perl 会自动使数据结构元素突然出现,让您的生活更轻松。不幸的是,它也会使事情变得困难。

例如,当我这样做时,autoviv 非常棒:

my $data;
$data->{horse}[0]{color} = 'brown';

Autoviv 神奇地变成$data了一个 hashref,其中包含horse以数组 ref 作为其值的键。数组 ref 由哈希 ref 填充。最终的哈希 ref 然后得到 的键值对color => brown

当你在一个结构中行走并进行深度测试时,问题就出现了:

# Code from above continues:

if( exists $data->{cat}[5]{color} ) {
    print "Cat 5 has a color\n";
}

use Data::Dumper;
print Dumper $data;

在这里,自动生存通过在数据中创建一堆垃圾来烧伤你,这是程序输出:

$VAR1 = {
      'cat' => [
                 undef,
                 undef,
                 undef,
                 undef,
                 undef,
                 {}
               ],
      'horse' => [
                   {
                     'color' => 'brown'
                   }
                 ]
    };

现在你可以通过仔细测试你结构的每一层的存在来防范这种事情,但这是一个巨大的痛苦。相反,我更喜欢使用Data::Diver

use Data::Diver qw( Dive );

my $dog_20_color = Dive( $data, 'dog', 20, 'color' );
print "Dog 20 is $dog_20_color\n" if defined $dog_20_color;

$data这里不变。

此外,您可能已经注意到,由于Dive采用了键或索引列表,这意味着它很容易以编程方式构建键/索引列表并在代码中下降任意路径。

当您必须对大而不稳定的数据结构进行大量操作时,Data::Diver 可以成为真正的救星。

于 2011-11-24T16:59:00.277 回答
2
my @pets = $listing->{pets}->{cats};

这不是你认为的那样。$listing->{pets}->{cats}包含对数组的引用。您的新@pets数组最终只包含一个元素 - 数组引用。

你真正需要的是

my @pets = @{ $listing->{pets}{cats} };

这会尊重数组引用并为您提供实际的数组。请注意,我还删除了表达式中可选的第二个箭头。

一旦你得到了数组,它的每个元素都是一个哈希引用。

foreach (@pets) {
  say $_->{name};
  # etc ...
}

当然,您根本不需要中间数组。

foreach (@{ $listing->{pets}{cats} }) {
  say $_->{name};
}
于 2011-11-24T14:54:20.847 回答