在Perl中,如何区分JSON编码中的数字和字符串哈希值?

时间:2017-08-02 14:03:31

标签: perl

在下面的代码中,我有一个绑定到哈希的类。在FETCH函数中,我正在打印密钥的JSON编码:

package Example::Tie;
use JSON;
my $json = JSON->new();

sub TIEHASH {
    my ($pkg,@list) = @_;
    bless { @list }, $pkg;
}

sub FETCH {
    my ($tied,$key) = @_;
    return $json->encode({key => $key});
}

package Example;

sub new {
    my ($pkg,@list) = @_;
    my $self = {};
    tie %$self, 'Example::Tie', @list;
    bless $self, $pkg;
}

package main;
my $exp = Example->new();
print($exp->{0} . "\n");

我得到以下输出:

{"key":"0"}

这导致0被编码为字符串。有没有办法将它编码成数字呢?

print($exp->{0} . "\n"); # this should print {"key":0}
print($exp->{'0'} . "\n"); # this should print {"key":"0"}

2 个答案:

答案 0 :(得分:5)

由于Perl中没有字符串或数字的真实概念,只有标量,这很棘手。 JSON module tries通过查看它编码的值的最后一个上下文来实现它。

  

Simple Perl标量(任何不是引用的标量)是最难编码的对象:此模块将未定义的标量编码为JSON空值,最后在编码为JSON字符串之前在字符串上下文中使用的标量,以及其他任何数字值:

# dump as number
encode_json [2]                      # yields [2]
encode_json [-3.0e17]                # yields [-3e+17]
my $value = 5; encode_json [$value]  # yields [5]

# used as string, so dump as string
print $value;
encode_json [$value]                 # yields ["5"]

# undef becomes null
encode_json [undef]                  # yields [null]

FETCH中的代码不具体执行此操作。所以它必须在其他地方。

我的猜测是,Perl自动引用哈希键是罪魁祸首。

$exp->{0}; # this should print {"key":0}
$exp->{'0'}; # this should print {"key":"0"}

这两个表达式是等价的。 Perl会自动将{}内部的哈希(ref)元素中的内容视为引用,并且它们成为字符串。因为这很容易被遗忘,there is best practice总是使用单引号''

Perldata says(强调我的):

  

哈希是由关联的字符串键索引的标量值的无序集合。

这个想法是没有哈希的数字键。如果有数字键,可以订购,然后你有一个数组。

您可以直接使用不带引号的号码FETCH作为arg来进一步证明。

Example::Tie->FETCH(1);

这将导致

{"key":1}

因此,我得出结论,将tie与JSON模块一起使用是不可能的,除非您明确尝试将其强制转换为数字。 JSON模块的文档中有一个示例。

  

您可以通过编号强制类型为数字:

my $x = "3"; # some variable containing a string
$x += 0;     # numify it, ensuring it will be dumped as a number
$x *= 1;     # same thing, the choice is yours.

答案 1 :(得分:5)

基本上,@simbabque's answer是正确的。当您的FETCH获取参数列表时,0中的$exp->{0}已经被字符串化,因为哈希键始终是字符串。

当然,如果你向每个参数添加0以便不加选择地获取,你将会遇到问题。下面,我使用Scalar::Util::looks_like_number来区分数字和字符串,但是,当然,如果您使用"0"进行尝试,这将无效。这也将转换为0

use strict; use warnings;

package Example::Tie;

use JSON;
use Scalar::Util qw( looks_like_number );

my $json = JSON->new;

sub TIEHASH {
    my $pkg = shift;
    bless { @_ } => $pkg;
}

sub FETCH {
    my $tied = shift;
    $json->encode({key => looks_like_number($_[0]) ? 0 + $_[0] : $_[0]})
}

package Example;

sub new {
    my $pkg = shift;
    my $self = {};
    tie %$self, 'Example::Tie', @_;
    bless $self => $pkg;
}

package main;

my $exp = Example->new;
print "$_\n" for map $exp->{$_}, 0, 'a';