我对访问已解码的某些JSON数据的内容感到困惑。这是一个例子
我不明白为什么这个解决方案有效,而我自己没有。我的问题在下面重新提到
my $json_raw = getJSON();
my $content = decode_json($json_raw);
print Data::Dumper($content);
此时我的JSON数据已转换为此
$VAR1 = { 'items' => [ 1, 2, 3, 4 ] };
我的猜测告诉我,一旦解码,该对象将是一个散列,其中一个元素具有键items
并且数组引用为值。
$content{'items'}[0]
其中$content{'items'}
将获取数组引用,外部$...[0]
将访问数组中的第一个元素并将其解释为标量。但是这不起作用。我收到错误消息use of uninitialized value [...]
但是,以下方法确实有效:
$content->{items}[0]
其中$content->{items}
产生数组引用,[0]
访问该数组的第一个元素。
问题
为什么$content{'items'}
不返回数组引用?我甚至试过@{content{'items'}}
,认为一旦我从content{'items'}
得到了值,它就需要被解释为一个数组。但是,我仍然收到未初始化的数组引用。
如何在不使用箭头操作符的情况下访问数组引用?
答案 0 :(得分:7)
初学者对初学者的回答:)当然不是应该如此专业,但也许可以帮助你。
use strict; #use this all times
use warnings; #this too - helps a lot!
use JSON;
my $json_str = ' { "items" : [ 1, 2, 3, 4 ] } ';
my $content = decode_json($json_str);
您写道:
我的猜测告诉我,一旦解码,该对象将是一个散列 一个元素,其中包含键项和数组引用作为值。
是的,它是一个哈希值,但decode_json
返回一个引用,在本例中是对哈希的引用。 (来自文档)
需要一个UTF-8(二进制)字符串并尝试解析它 作为UTF-8编码的JSON文本, 返回结果引用。
在第
行my $content = decode_json($json_str);
您分配给SCALAR变量(不是哈希)。
因为你知道:它是一个参考,你可以做下一个:
printf "reftype:%s\n", ref($content);
#print: reftype:HASH ^
#therefore the +------- is a SCALAR value containing a reference to hash
这是一个hashref - 你可以转储所有密钥
print "key: $_\n" for keys %{$content}; #or in short %$content
#prints: key: items
你也可以将“items”(arrayref)的值赋予标量变量
my $aref = $content->{items}; #$hashref->{key}
#or
#my $aref = ${$content}{items}; #$hash{key}
但不
#my $aref = $content{items}; #throws error if "use strict;"
#Global symbol "%content" requires explicit package name at script.pl line 20.
$content{item}
正在请求散列%content
中的值,并且您从未定义/分配此类变量。 $content
是标量变量,而不是散列变量%content
。
{
#in perl 5.20 you can also
use 5.020;
use experimental 'postderef';
print "key-postderef: $_\n" for keys $content->%*;
}
现在再深入 - 再到arrayref - 再次打印出引用类型
printf "reftype:%s\n", ref($aref);
#reftype:ARRAY
打印数组的所有元素
print "arr-item: $_\n" for @{$aref};
但又不
#print "$_\n" for @aref;
#dies: Global symbol "@aref" requires explicit package name at script.pl line 37.
{
#in perl 5.20 you can also
use 5.020;
use experimental 'postderef';
print "aref-postderef: $_\n" for $aref->@*;
}
这是一条简单的规则:
my @arr; #array variable
my $arr_ref = \@arr; #scalar - containing a reference to @arr
@{$arr_ref} is the same as @arr
^^^^^^^^^^ - array reference in curly brackets
如果你有$arrayref
- 请在任何地方使用@{$array_ref}
使用数组。
my %hash; #hash variable
my $hash_ref = \%hash; #scalar - containing a reference to %hash
%{$hash_ref} is the same as %hash
^^^^^^^^^^^ - hash reference in curly brackets
如果你有$hash_ref
- 请在任何地方使用%{$hash_ref}
使用哈希。
对于整个结构,以下
say $content->{items}->[0];
say $content->{items}[0];
say ${$content}{items}->[0];
say ${$content}{items}[0];
say ${$content->{items}}[0];
say ${${$content}{items}}[0];
打印相同的值1
。
答案 1 :(得分:4)
$content
是哈希引用,因此您始终需要使用箭头来访问其内容。 $content{items}
会引用您没有的%content
哈希值。这就是你从中获得“使用未初始化的值”错误的地方。
答案 2 :(得分:0)
我实际上问了一个类似的问题here
答案:
在Perl中,函数只能真正返回标量或列表。
由于哈希值可以从列表中初始化或分配(例如%foo =(a => 1,b => 2)),我想您是在问为什么json_decode返回类似{a => 1,b => 2}(对匿名哈希的引用)而不是(a => 1,b => 2)(可以复制到哈希中的列表)。
我可以想到一些很好的理由:
在Perl中,数组或散列总是包含标量。所以在{" a":{" b":3}}之类的内容中,{" b":3}部分必须是标量;为了保持一致性,整个事物以同样的方式成为标量是有道理的。如果散列非常大(顶级的许多键),迭代所有元素以将其转换为列表,然后从该列表构建新的散列是毫无意义且昂贵的。 在JSON中,顶级元素可以是对象(= Perl哈希)或数组(= Perl数组)。如果json_decode在前一种情况下返回一个列表,那么在后一种情况下它不会清楚它会返回什么。解码JSON字符串后,您如何检查结果以了解如何处理它? (除非你已经知道你有一个哈希值,否则编写%foo = json_decode(...)是不安全的。)因此,json_decode的行为对任何具有通用库代码的行为都有效。使用它而不太了解它正在使用的数据。
我不得不想知道你作为一个数组传递给json_decode的确切内容,因为我的结果与你的不同。
#!/usr/bin/perl
use JSON qw (decode_json);
use Data::Dumper;
my $json = '["1", "2", "3", "4"]';
my $fromJSON = decode_json($json);
print Dumper($fromJSON);
结果为$VAR1 = [ '1', '2', '3', '4' ];
哪个是数组引用,其结果是哈希引用
那么你是否传递了一个带有元素项的哈希值,这是一个对数组的引用?
在我的示例中,您将通过执行
来获取数组my @array = @{ $fromJSON };
在你的
my @array = @{ $content->{'items'} }
答案 3 :(得分:0)
我不明白为什么你这么多不喜欢箭头操作员!
JSON
模块中的decode_json
函数将始终返回数据引用。
假设你有一个像这样的Perl程序
use strict;
use warnings;
use JSON;
my $json_data = '{ "items": [ 1, 2, 3, 4 ] }';
my $content = decode_json($json_data);
use Data::Dump;
dd $content;
输出此文本
{ items => [1 .. 4] }
显示$content
是哈希引用。然后,您可以使用
dd $content->{items};
显示
[1 .. 4]
您可以通过编写
来打印数组的第一个元素print $content->{items}[0], "\n";
,正如您所发现的那样,仅显示
1
这是数组的第一个元素。
正如@cjm
在评论中提到的那样命令式是每个 Perl的开头use strict
和use warnings
程序。如果你在试图访问$content{items}
的程序中有那些,你的程序将无法编译,你会看到消息
Global symbol "%content" requires explicit package name
这是一种(措辞不当)告诉你没有%content
的方式,因此没有items
元素。
标量变量$content
与哈希变量%content
完全独立,您正试图写$content{items}
时访问。之前从未提及%content
且它为空,因此没有items
元素。如果你曾尝试过@{$content->{items}}
那么它会起作用,就像@{${$content}{items}}
如果确实箭头操作符出现问题,那么您可以编写
print ${$content}{items}[0], "\n";
产生相同的输出;但我不明白原版本有什么问题。