在Perl中解码和使用JSON数据

时间:2014-09-20 15:31:18

标签: json perl

我对访问已解码的某些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'}得到了值,它就需要被解释为一个数组。但是,我仍然收到未初始化的数组引用。

  • 如何在不使用箭头操作符的情况下访问数组引用?

4 个答案:

答案 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 strictuse 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";

产生相同的输出;但我不明白原版本有什么问题。