在perl中访问json结构的值

时间:2011-11-24 14:10:58

标签: json perl hash hash-of-hashes

我有一个我正在解码的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" },
              ],
    },
  },
}

我可以通过以下方式打印cityidname

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

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

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

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

3 个答案:

答案 0 :(得分:7)

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

  • {}
  • 中换行哈希键
  • []
  • 中换行数组索引
  • 如果您的顶级变量是引用,请在第一个标识符之前使用->
  • 在第一组大括号或括号后,其他箭头(->)是可选的。

所以: * $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转换为包含密钥horse的hashref,其中包含数组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;

在这里,autovivification通过在数据中创建一堆垃圾来焚烧你,这是程序输出:

$ VAR1 = {           'cat'=> [                      民主基金,                      民主基金,                      民主基金,                      民主基金,                      民主基金,                      {}                    ]           'horse'=> [                        {                          'color'=> '棕色'                        }                      ]         };

现在你可以通过仔细测试你的结构的每一层存在来防止这种事情,但这是一个巨大的痛苦。相反,我更喜欢使用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可以成为一个真正的救星,当你必须对大而不稳定的数据结构进行大量操作时。

答案 1 :(得分:6)

假设您的$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'

答案 2 :(得分: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};
}