在perl中访问json结构的值

问题描述 投票:5回答:3

我正在解码的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;

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

json perl hash hash-of-hashes
3个回答
8
投票

假设您的$listingperson,则必须取消引用数组和哈希引用。

# 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'

8
投票

一旦知道规则,挖掘大结构就非常简单:

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

所以:* $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且其值为数组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在必须对大型,笨拙的数据结构进行大量处理的情况下可以成为现实的救星。


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

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

您实际需要的是

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

这将引用数组引用,并为您提供实际的数组。请注意,我还在表达式中放置了可选的第二个箭头。

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

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

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

foreach (@{ $listing->{pets}{cats} }) {
  say $_->{name};
}
© www.soinside.com 2019 - 2024. All rights reserved.