我将如何在Perl中读取此数据结构?字典/哈希,键包含包含列表的列表.Python::内联给了我错误



我已经为这个简单的问题挣扎了大约3周。我不明白为什么,我愿意付出任何代价来解决它。哈哈。

我正在尝试从下面的数据结构中读取值。文档说这是一本字典,里面有包含该类型结果列表的关键字。

示例:我使用eval函数获得主查询回复。我查找关键字"song_hits"以获得该结构。然后我查找关键字"track"并对其进行解析。问题是"trace"部分。

当我按照Perl文档告诉我的方式进行操作时,我会通过包"Inline::Python::object::Data"找到Can't locate对象方法"FIRSTKEY"。

因此,我想知道是否有一种方法可以使用eval函数绕过ObjectData的哈希键限制来读取值,如果我知道确切的键,是否有另一种方法来读取它,或者我只是做得完全错误。

{
'album_hits': [
{
'album':
{
'albumArtRef': 'http://lh5.ggpht.com/DVIg4GiD6msHfgPs_Vu_2eRxCyAoz0fF...',
'albumArtist': 'J.Cole',
'albumId': 'Bfp2tuhynyqppnp6zennhmf6w3y',
'artist': 'J.Cole',
'artistId': ['Ajgnxme45wcqqv44vykrleifpji'],
'description_attribution':
{
'kind': 'sj#attribution',
'license_title': 'Creative Commons Attribution CC-BY',
'license_url': 'http://creativecommons.org/licenses/by/4.0/legalcode',
'source_title': 'Freebase',
'source_url': ''
},
'explicitType': '1',
'kind': 'sj#album',
'name': 'Work Out',
'year': 2011
},
'type': '3'
}],
'artist_hits': [
{
'artist':
{
'artistArtRef': 'http://lh3.googleusercontent.com/MJe-cDw9uQ-pUagoLlm...',
'artistArtRefs': [
{
'aspectRatio': '2',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/MJe-cDw9uQ-pUagoLlmKX3x_K...'
}],
'artistId': 'Ajgnxme45wcqqv44vykrleifpji',
'artist_bio_attribution':
{
'kind': 'sj#attribution',
'source_title': 'David Jeffries, Rovi'
},
'kind': 'sj#artist',
'name': 'J. Cole'
},
'type': '2'
}],
'playlist_hits': [
{
'playlist':
{
'albumArtRef': [
{
'url': 'http://lh3.googleusercontent.com/KJsAhrg8Jk_5A4xYLA68LFC...'
}],
'description': 'Workout Plan ',
'kind': 'sj#playlist',
'name': 'Workout',
'ownerName': 'Ida Sarver',
'shareToken': 'AMaBXyktyF6Yy_G-8wQy8Rru0tkueIbIFblt2h0BpkvTzHDz-fFj6P...',
'type': 'SHARED'
},
'type': '4'
}],
'situation_hits': [
{
'situation':
{
'description': 'Level up and enter beast mode with some loud, aggressive music.',
'id': 'Nrklpcyfewwrmodvtds5qlfp5ve',
'imageUrl': 'http://lh3.googleusercontent.com/Cd8WRMaG_pDwjTC_dSPIIuf...',
'title': 'Entering Beast Mode',
'wideImageUrl': 'http://lh3.googleusercontent.com/8A9S-nTb5pfJLcpS8P...'
},
'type': '7'
}],
'song_hits': [
{
'track':
{
'album': 'Work Out',
'albumArtRef': [
{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh5.ggpht.com/DVIg4GiD6msHfgPs_Vu_2eRxCyAoz0fFdxj5w...'
}],
'albumArtist': 'J.Cole',
'albumAvailableForPurchase': True,
'albumId': 'Bfp2tuhynyqppnp6zennhmf6w3y',
'artist': 'J Cole',
'artistId': ['Ajgnxme45wcqqv44vykrleifpji', 'Ampniqsqcwxk7btbgh5ycujij5i'],
'composer': '',
'discNumber': 1,
'durationMillis': '234000',
'estimatedSize': '9368582',
'explicitType': '1',
'genre': 'Pop',
'kind': 'sj#track',
'nid': 'Tq3nsmzeumhilpegkimjcnbr6aq',
'primaryVideo':
{
'id': '6PN78PS_QsM',
'kind': 'sj#video',
'thumbnails': [
{
'height': 180,
'url': 'https://i.ytimg.com/vi/6PN78PS_QsM/mqdefault.jpg',
'width': 320
}]
},
'storeId': 'Tq3nsmzeumhilpegkimjcnbr6aq',
'title': 'Work Out',
'trackAvailableForPurchase': True,
'trackAvailableForSubscription': True,
'trackNumber': 1,
'trackType': '7',
'year': 2011
},
'type': '1'
}],
'station_hits': [
{
'station':
{
'compositeArtRefs': [
{
'aspectRatio': '1',
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/3aD9mFppy6PwjADnjwv_w...'
}],
'contentTypes': ['1'],
'description': 'These riff-tastic metal tracks are perfect for getting the blood pumping.',
'imageUrls': [
{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh5.ggpht.com/YNGkFdrtk43e8H941fuAHjflrNZ1CJUeqdoys...'
}],
'kind': 'sj#radioStation',
'name': 'Heavy Metal Workout',
'seed':
{
'curatedStationId': 'Lcwg73w3bd64hsrgarnorif52r',
'kind': 'sj#radioSeed',
'seedType': '9'
},
'skipEventHistory': [],
'stationSeeds': [
{
'curatedStationId': 'Lcwg73w3bd64hsrgarnorif52r',
'kind': 'sj#radioSeed',
'seedType': '9'
}]
},
'type': '6'
}],
'video_hits': [
{
'score': 629.6226806640625,
'type': '8',
'youtube_video':
{
'id': '6PN78PS_QsM',
'kind': 'sj#video',
'thumbnails': [
{
'height': 180,
'url': 'https://i.ytimg.com/vi/6PN78PS_QsM/mqdefault.jpg',
'width': 320
}],
'title': 'J. Cole - Work Out'
}
}]

}

经过3周的不同尝试,已清理但已损坏的代码:(我尝试了for、foreach、while,但它读取的最远值可能是整个unicode数组、错误或空字符串)

sub search {
my $query = shift;
my $uri = 'googlemusic:search:' . $query;
if (my $result = $cache->get($uri)) {
return $result;
}
my $googleResult;
my $result = {
tracks => [],
albums => [],
artists => [],
};        
eval {
$googleResult = $googleapi->search($query, $prefs->get('max_search_items'));
};
if ($@) {
$log->error("Not able to search All Access for "$query": $@");
return;
}
#gives not an ARRAY refernce error
for my $hit (@{$googleResult->{song_hits}}) {
push @{$result->{tracks}}, to_slim_track($hit->{track});
}
#works, but gives an error on the next line, 'newlist' object has no attribute 'album' 
for my $hit ({$googleResult->{album_hits}}) {
push @{$result->{albums}}, album_to_slim_album($hit->{album});
}
#Perl and others recommended way, but gives  Can't locate object method "FIRSTKEY" via package "Inline::Python::Object::Data"
for my $hit (%{$googleResult->{artist_hits}}) {
push @{$result->{artists}}, artist_to_slim_artist($hit->{artist});
}
# Add to the cache
$cache->set($uri, $result, $CACHE_TIME);
return $result;
}

我试过阅读,但收到了很多错误,包括:

  • "密钥"不存在
  • 在使用严格引用时,不能将字符串("track")用作HASH引用
  • 引用上键的参数类型必须是未压缩的hashref或arrayref

我的完整测试文件:http://pastebin.com/DMnDc56iGoogleApi PM(Python GAPI Hook):https://raw.githubusercontent.com/hechtus/squeezebox-googlemusic/master/GoogleMusic/GoogleAPI.pm

编辑:信息,有几个人想要修复未维护的旧代码,所以我主动提供帮助,并使除此部分外的一切都正常工作。

旧代码Git:https://github.com/hechtus/squeezebox-googlemusic

我使用的Google Api Python:https://github.com/simon-weber/gmusicapi

我认为显示的数据结构在$googleResult中。这"几乎"是JSON,您可以在简单清理后使用模块对其进行处理。我将使用JSON::XS。以下代码在获取$googleResult后开始。(在测试中,我实际上将问题中显示的数据复制到一个文件中并读入。)我首先用"和小写TrueFalse替换',以获得模块可以解码的有效JSON格式。

# Other code from the question ...
use JSON::XS;
# For tests I loaded shown data into $googleResult (did not run this eval)
eval {
$googleResult = $googleapi->search($query, $prefs->get('max_search_items'));
};
if ($@) {
$log->error("Not able to search All Access for "$query": $@");
return;
}
# The structure shown in the question needs a cleanup
# But this may be a road to madness, if there is more
$googleResult =~ s/'/"/g;        # ' turn off wrong editor coloring
$googleResult =~ s/False/false/g;
$googleResult =~ s/True/true/g;
my $coder = JSON::XS->new;    
# There are many options for how to set it up. Example:
# JSON::XS->new->ascii->pretty->allow_nonref;    
my $data = $coder->decode($googleResult);  
# Now this is a normal Perl data structure that we can work with. 
# Look at what's under 'album_hits' for example
my $ralbhits = $data->{'album_hits'};  
print Dumper($ralbhits);
# We get: VAR1 = [ { 'album' => { albumID => ... } } ]
# Array reference, with nested hash references as the sole element
# Extract the 'artist'
my $artist = $ralbhits->[0]->{'album'}->{'artist'};
print "$artistn";

这将打印J. Cole(在转储之后,此处省略)。为了方便起见,您可以先提取结构的一部分,然后更简单地查询它。例如

# Get the hashref for album
my $ralbum = $ralbhits->[0]->{'album'};
my $artist = $ralbum->{'artist'};

现在,一旦数据被解包,您就可以根据artist_to_slim_artist()的需求和功能来检索您需要的内容。这是一个正常的数据结构。

JSON解析模块返回Perl数据结构,请参阅JSON::XS中的映射。通常情况下,除了非常简单的情况外,它们都是嵌套的。关于如何使用它们,请参阅perldsc,一本关于复杂数据结构的食谱。


本例中给出的JSON对象虽然无效,但几乎不需要校正。然而,它可能会变得更加复杂。例如,在注释中链接了一个大得多的文档(~100kB),但存在这些问题。

  • 名称值对被包含在'中,而不是"值本身包含'(如isn't和其他缩写),使'对的匹配复杂化。

  • 名称和值开头的u'序列无效(需要删除u)。当它们结合在一起时,这可以与上述内容结合在一起。还有CCD_ 19。

  • 文本可能包含各种转义,例如某些重音编码,这些都不是有效的JSON。(该文档中有一个。)可以找到并修复(例如转义)。

花了几分钟的时间才想出几个正则表达式来更正链接处的文档,大小接近100kB,这样它就可以用上面的代码进行干净的解析。但问题是,很难判断下一份文件中还有什么其他问题。尽管如此,由于这可能是感兴趣的,这里是正则表达式。

名称和值不包含在一对"中,而是位于'之间,并且前面的名称和值还有一个额外的字符u'。更容易的是,关闭的'后面必须跟有, : ] }中的任何一个,我使用正向前瞻来断言这一点。最后,有一些u"的开头引号,首先去掉了u

$googleResult =~ s/False/false/g;
$googleResult =~ s/True/true/g;
$googleResult =~ s/u"/"/g;
# There are also escaped characters in text, escape that backslash
$googleResult =~ s|(\)|$1$1|g;
# Correct delimiters from u'...' to "...", see text below   
$googleResult =~ s/u'(.*?)' (?= []:},] )/"$1"/gx;
# We are good now, decode it
my $data = $coder->decode($googleResult); 
my $alb = $data->[0]{track}{album};
print "$albn";

这将打印These Things Happen(正确)。在上面,我们捕获了u'和第一个'之间的内容,该内容后面是]:,}中的任何一个(使用字符类[...])。然后CCD_ 32被CCD_。在这个decode($googleResult);工作之后,我们得到了要查询的Perl数据结构。

有各种模块允许"放松"的方法,并将接受许多此类违规行为。然而,通过使用它们,我们同意使用无效的JSON,这是一种简单明了的数据格式,我不建议走这条路。请注意,在上面的链接中,几乎完整的格式规范可以很好地放在一个清晰而概括的页面中。有关一些示例,请参见JSON Example

我认为最好的办法是设法把它清理干净。像上面的代码一样运行解码器,然后查看错误消息。它将准确地指出问题所在。然后添加一个正则表达式来纠正特定的格式冲突。然后再去。如果你可能使用的各种文档或多或少都有相同的问题(比如上面的那些),那么它很可能会起作用。或者,如果新的违规行为不断出现,可能会带来太多麻烦,在这种情况下,你可能需要一种不同的方法。

最后,我不知道您是如何从最初的Python对象问题中得出这种格式的。可能是格式在翻译的某个地方被破坏了吗?我不知道情况会如何。它实际上不是JSON吗?然而,它离它太近了。

是否可以要求提供有效的JSON?

好吧,这不是一个真正的答案,但出于我的善意,我为您清理了数据。这是一个真正的Python语句。我不知道一些数值字符串值是否应该是int,所以我没有弄乱它们。这将由您来决定如何处理被截断的URL。

另一种方法是将True更改为true,将False更改为false,并将dict解析为JSON。

{
'album_hits': [
{
'album':
{
'albumArtRef': 'http://lh5.ggpht.com/DVIg4GiD6msHfgPs_Vu_2eRxCyAoz0fF...',
'albumArtist': 'J.Cole',
'albumId': 'Bfp2tuhynyqppnp6zennhmf6w3y',
'artist': 'J.Cole',
'artistId': ['Ajgnxme45wcqqv44vykrleifpji'],
'description_attribution':
{
'kind': 'sj#attribution',
'license_title': 'Creative Commons Attribution CC-BY',
'license_url': 'http://creativecommons.org/licenses/by/4.0/legalcode',
'source_title': 'Freebase',
'source_url': ''
},
'explicitType': '1',
'kind': 'sj#album',
'name': 'Work Out',
'year': 2011
},
'type': '3'
}],
'artist_hits': [
{
'artist':
{
'artistArtRef': 'http://lh3.googleusercontent.com/MJe-cDw9uQ-pUagoLlm...',
'artistArtRefs': [
{
'aspectRatio': '2',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/MJe-cDw9uQ-pUagoLlmKX3x_K...'
}],
'artistId': 'Ajgnxme45wcqqv44vykrleifpji',
'artist_bio_attribution':
{
'kind': 'sj#attribution',
'source_title': 'David Jeffries, Rovi'
},
'kind': 'sj#artist',
'name': 'J. Cole'
},
'type': '2'
}],
'playlist_hits': [
{
'playlist':
{
'albumArtRef': [
{
'url': 'http://lh3.googleusercontent.com/KJsAhrg8Jk_5A4xYLA68LFC...'
}],
'description': 'Workout Plan ',
'kind': 'sj#playlist',
'name': 'Workout',
'ownerName': 'Ida Sarver',
'shareToken': 'AMaBXyktyF6Yy_G-8wQy8Rru0tkueIbIFblt2h0BpkvTzHDz-fFj6P...',
'type': 'SHARED'
},
'type': '4'
}],
'situation_hits': [
{
'situation':
{
'description': 'Level up and enter beast mode with some loud, aggressive music.',
'id': 'Nrklpcyfewwrmodvtds5qlfp5ve',
'imageUrl': 'http://lh3.googleusercontent.com/Cd8WRMaG_pDwjTC_dSPIIuf...',
'title': 'Entering Beast Mode',
'wideImageUrl': 'http://lh3.googleusercontent.com/8A9S-nTb5pfJLcpS8P...'
},
'type': '7'
}],
'song_hits': [
{
'track':
{
'album': 'Work Out',
'albumArtRef': [
{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh5.ggpht.com/DVIg4GiD6msHfgPs_Vu_2eRxCyAoz0fFdxj5w...'
}],
'albumArtist': 'J.Cole',
'albumAvailableForPurchase': True,
'albumId': 'Bfp2tuhynyqppnp6zennhmf6w3y',
'artist': 'J Cole',
'artistId': ['Ajgnxme45wcqqv44vykrleifpji', 'Ampniqsqcwxk7btbgh5ycujij5i'],
'composer': '',
'discNumber': 1,
'durationMillis': '234000',
'estimatedSize': '9368582',
'explicitType': '1',
'genre': 'Pop',
'kind': 'sj#track',
'nid': 'Tq3nsmzeumhilpegkimjcnbr6aq',
'primaryVideo':
{
'id': '6PN78PS_QsM',
'kind': 'sj#video',
'thumbnails': [
{
'height': 180,
'url': 'https://i.ytimg.com/vi/6PN78PS_QsM/mqdefault.jpg',
'width': 320
}]
},
'storeId': 'Tq3nsmzeumhilpegkimjcnbr6aq',
'title': 'Work Out',
'trackAvailableForPurchase': True,
'trackAvailableForSubscription': True,
'trackNumber': 1,
'trackType': '7',
'year': 2011
},
'type': '1'
}],
'station_hits': [
{
'station':
{
'compositeArtRefs': [
{
'aspectRatio': '1',
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/3aD9mFppy6PwjADnjwv_w...'
}],
'contentTypes': ['1'],
'description': 'These riff-tastic metal tracks are perfect for getting the blood pumping.',
'imageUrls': [
{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh5.ggpht.com/YNGkFdrtk43e8H941fuAHjflrNZ1CJUeqdoys...'
}],
'kind': 'sj#radioStation',
'name': 'Heavy Metal Workout',
'seed':
{
'curatedStationId': 'Lcwg73w3bd64hsrgarnorif52r',
'kind': 'sj#radioSeed',
'seedType': '9'
},
'skipEventHistory': [],
'stationSeeds': [
{
'curatedStationId': 'Lcwg73w3bd64hsrgarnorif52r',
'kind': 'sj#radioSeed',
'seedType': '9'
}]
},
'type': '6'
}],
'video_hits': [
{
'score': 629.6226806640625,
'type': '8',
'youtube_video':
{
'id': '6PN78PS_QsM',
'kind': 'sj#video',
'thumbnails': [
{
'height': 180,
'url': 'https://i.ytimg.com/vi/6PN78PS_QsM/mqdefault.jpg',
'width': 320
}],
'title': 'J. Cole - Work Out'
}
}]
}

我已经使用如下列表理解找到了一个解决方案:

use Inline::Python qw(py_eval);
my $song_hits = py_eval("[x for x in $googleResult->{song_hits}]", 0);
for my $hit (@$song_hits) {
push @{$result->{tracks}}, to_slim_track($hit->{track});
}

提交位置:https://github.com/squeezebox-googlemusic/squeezebox-googlemusic/commit/e6fa62d9da3bc7295023283ef5d25698737e5772

最新更新