Как получить доступ к значению хеша по индексу, хранящемуся в переменной - PullRequest
0 голосов
/ 02 ноября 2019

Я работаю над кодом для извлечения данных, хранящихся в формате JSON, с веб-страницы. Данные JSON извлекаются и правильно декодируются в хэш.

Структура данных JSON очень сложна, я написал некоторый вспомогательный код / ​​функцию, которая «пересекает» хеш и помогает найти «индекс» (местоположение) значения, интересующего хеш.

Функция 'find' возвращает 'index' (местоположение) данных в хэше, которые хранятся в переменной.

Я пытаюсь использовать эту переменную (сохраненный 'index') в другомопераций, но пока безуспешно.

Пожалуйста, посмотрите включенный простой демонстрационный код для объяснения проблемы.

Спасибо, Белый Медведь

use strict;
use warnings;

use JSON qw(decode_json);

my $index;
my $slice;

my $data = decode_json( join '', <DATA> );

printf "TITLE: %-15s TIME: %5s TIMES: %5s FAVORITE: %s\n",
        $data->{playList}[1]{title},
        $data->{playList}[1]{time},
        $data->{playList}[1]{played},
        $data->{playList}[1]{favorite} ? "yes" : "no";

$index = '{playList}[1]';

$slice = $data{$index};     # does not pass 'use strict' compilation error
$slice = $data->{$index};   # empty slice 
$slice = $data->$index;     # Can't call method "{playList}[1]" on unblessed reference at

printf "TITLE: %-15s TIME: %5s TIMES: %5s FAVORITE: %s\n",
        $slice->{title},
        $slice->{time},
        $slice->{played},
        $slice->{favorite} ? "yes" : "no";

__DATA__
{
    "playList": [
      {
        "title": "Song name 1",
        "time": "3:25",
        "played": "240",
        "favorite": "1"
      },
      {
        "title": "Song name 2",
        "time": "4:12",
        "played": "30",
        "favorite": "0"
      },
      {
        "title": "Song name 3",
        "time": "2:56",
        "played": "85",
        "favorite": "0"
      }
    ]
}

Я ожидаю, чтополучить доступ к данным, используя «индекс», хранящийся в переменной, но я не смог найти способ достичь этого результата. Подробности см. В комментариях к коду.

ПРИМЕЧАНИЕ. В реальной жизни индекс выглядит следующим образом:

my $index = "{contents}{twoColumnBrowseResultsRenderer}{tabs}[0]{tabRenderer}{content}{sectionListRenderer}{contents}[0]{itemSectionRenderer}{contents}[0]{playlistVideoListRenderer}{contents}[0]{playlistVideoRenderer}{title}{accessibility}{accessibilityData}{label}";

РЕШЕНИЕ:

Я хотел бы расширить my' спасибо ' Хокону Огланду и Лордамире за предложенное решение

use Data::Diver qw/Dive/; # or Data::DPath, etc

# Capture web page, extract data JSON, convert to hash, assign hash ref to $data
my $data = ...; 

# Find index/location in the hash
#my $index = find($data, $value);

my $index = "{contents}{twoColumnBrowseResultsRenderer}{tabs}[0]{tabRenderer}{content}{sectionListRenderer}{contents}[0]{itemSectionRenderer}{contents}[0]{playlistVideoListRenderer}{contents}[0]{playlistVideoRenderer}{title}{accessibility}{accessibilityData}{label}";

$index =~ s/[{\[]//g; # throw away opening brackets

my @index = split /[}\]]/, $index; # split into array on closing brackets

pop @index for 1..8 # 8 levels to back up to

my $slice = Dive( $data, @index ); # extract hash slice of interest

# extract playlist
my $playlist = $slice->{playlistVideoListRenderer}{contents};

# go through playlist and extract information of our interest
foreach ( @$playlist ) {
    my $video = $_->{playlistVideoRenderer};
    printf "%s %8s %s\n",
            $video->{videoId},
            $video->{lengthText}{simpleText},
            $video->{title}{simpleText};
}

Они оба направили меня на use Data::Dive с помощью этого модуля, который я могу сделать создайте резервную копию нескольких уровней из глубины хэша и извлеките срез , представляющий интерес.

Выяснилось, что при использовании этого модуля индекс в виде массива с ним легче работать. Из-за этого фактора я изменю мою функцию поиска, чтобы вернуть индекс массив .

Ответы [ 4 ]

1 голос
/ 05 ноября 2019

Я бы хотел передать мои ' спасибо ' Хокону Огланду и Лордадмире за предложенное решение

use Data::Diver qw/Dive/; # or Data::DPath, etc

my $data = ...;

my $index = "{contents}{twoColumnBrowseResultsRenderer}{tabs}[0]{tabRenderer}{content}{sectionListRenderer}{contents}[0]{itemSectionRenderer}{contents}[0]{playlistVideoListRenderer}{contents}[0]{playlistVideoRenderer}{title}{accessibility}{accessibilityData}{label}";

$index =~ s/[{\[]//g;

my @index = split /[}\]]/, $index;

pop @index for 1..8 # 8 levels to back up to

my $slice = Dive( $data, @index );

my $playlist = $slice->{playlistVideoListRenderer}{contents};


foreach ( @$playlist ) {
    my $video = $_->{playlistVideoRenderer};
    printf "%s %8s %s\n",
            $video->{videoId},
            $video->{lengthText}{simpleText},
            $video->{title}{simpleText};
}

Они оба направили меня на use Data::Dive с помощью этого модуля я могу сделать резервное копирование нескольких уровней из глубины хэша и извлечь срез интереса.

Выяснилось, что с помощью этогоМодуль индекс в виде массив легче работать с. Из-за этого фактора я изменю мою функцию поиска, чтобы вернуть индекс массив .

1 голос
/ 04 ноября 2019

В комментариях вы сказали, что у вас есть функция, которая находит элемент в вашей структуре данных JSON и возвращает «путь» к этому элементу, и что ваш вопрос касается поиска высокоуровневого контейнера этого элемента.

Если бы это был XML, я бы использовал XPath, чтобы выполнить поиск и найти нужный контейнер. Но не беспокойтесь, кто-то разработал XPath-подобный язык для JSON , а кто-то предоставил эту функцию через модуль Perl JSON :: Path .

1 голос
/ 03 ноября 2019

Вы можете использовать Данные :: Diver :

use Data::Diver   qw( DiveVal );
use JSON qw(decode_json);

my $data = decode_json( join '', <DATA> );
my $slice = DiveVal( $data, qw( playList 1 ) );
printf "TITLE: %-15s TIME: %5s TIMES: %5s FAVORITE: %s\n",
        $slice->{title},
        $slice->{time},
        $slice->{played},
        $slice->{favorite} ? "yes" : "no";

Выход :

TITLE: Song name 2     TIME:  4:12 TIMES:    30 FAVORITE: no
0 голосов
/ 03 ноября 2019

Проблема в том, что ваш код возвращает все ключи и элементы, чтобы найти данный элемент, но не само значение элемента. Самый простой ответ - вернуть ссылку на это найденное значение.

... bunch of lookdown code
return \ $this_level->{the_key_I_want}

Таким образом, вызывающая сторона может иметь прямой доступ на чтение / запись к значению листа.

Если вы хотите использоватьСписок ключей и элементов напрямую, чтобы получить доступ к глубокому значению, вам нужно будет сделать строку eval. Это НЕ рекомендуется, если вы не доверяете данным на 1000%, потому что какой-то джокер может назвать хеш-ключ ";это еще одна функция поиска, которая берет список ключей и т. д. и обращается к ним один за другим.

HTH

PS: Я немного неправильно понял ваш исходный вопрос. Просто напишите в своем коде:

$slice = $data->{playList}[1];

2-й Изменить, чтобы фактически использовать индекс $:

use Data::Diver qw/Dive/; # or Data::DPath, etc
my $data = ...;
my $index = "{contents}{twoColumnBrowseResultsRenderer}{tabs}[0]{tabRenderer}{content}{sectionListRenderer}{contents}[0]{itemSectionRenderer}{contents}[0]{playlistVideoListRenderer}{contents}[0]{playlistVideoRenderer}{title}{accessibility}{accessibilityData}{label}";
my @index = split /[{}[\]]+/, $index;
shift @index;
pop @index for 1..2 # however many levels to back up to
my $slice = Dive( $data, @index );
...