Доступ к значениям структуры JSON в Perl - PullRequest
3 голосов
/ 24 ноября 2011

У меня есть структура 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->cats или pets->dogs. Я могу сделать дамп из них:

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

но я не уверен, как получить к ним доступ через хеш-структуру.

Ответы [ 3 ]

7 голосов
/ 24 ноября 2011

Копаться в большую структуру довольно просто, если вы знаете правила:

  • Обернуть ключи хеша в {}
  • Обернуть индексы массива в []
  • Если ваша переменная верхнего уровня является справочной, используйте -> перед первым идентификатором.
  • После первого набора скобок или скобок дополнительные стрелки (->) являются необязательными.

Итак: * $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 в качестве значения.Ссылка на массив заполняется ссылкой на хэш.Окончательный хэш 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 может быть реальным спасателем, когда у вас естьделать много манипуляций с большими, вялыми структурами данных.

6 голосов
/ 24 ноября 2011

Предполагая, что ваш $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 голосов
/ 24 ноября 2011
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};
}
...