4

有没有办法让encode_json方法不在数字周围加上引号?

例如,我在 CentOS 6.3 上使用 perl 5.10(在 Win 7 上也使用 ActiveState perl 5.16),它把引号放在不应该的地方:

# perl -MJSON -e 'print encode_json({a => [split(",", "1.2,30")]});'
{"a":["1.2","30"]}

也就是说,好的,它在上面的代码中将“1.2”和“30”视为字符串,但我的问题是:

我的 perl 脚本使用 Google Charts解析 CSV 文件并生成 HTML 文件,后者因在数字值周围加上引号而感到困惑(尽管我告诉他们该列的类型是“数字”)。

作为一种解决方法,我遍历我的数据结构并将每个数字替换为,sprintf "%f", $val但这会导致在每个数字之后显示太多空值,这使得图表正确,但看起来很难看:

# perl -e 'printf "%f", "30"'
30.000000

在此处输入图像描述

更新:

是的,乍一看,添加零或乘以一似乎有效:

# perl -MJSON -e 'print encode_json({a => [map {1 * $_} split(",", "1.2,30")]});'
{"a":[1.2,30]}

但在我的真实脚本中,它仍然不适用于浮点数

当使用 Dumper 模块时,您也可以在 CLI 中看到我仍然遇到的问题:

# perl -MData::Dumper -e 'print Dumper({a => [map {1.0 * $_} split(",", "1.2,30")]});'
$VAR1 = {
          'a' => [
                   '1.2', # <= THIS IS MY PROBLEM AND CONFUSES GOOGLE CHARTS
                   30
                 ]
        };
4

6 回答 6

14

您的问题是,尽管您正确地将其转换为数字,但在您调用 encode_json 之前它会被转换回字符串。这是因为您在调试语句中调用 Data::Dumper。如果您注释掉对 Data::Dumper 的调用,您会发现 encode_json 输出正确。

例如,此示例显示了在对象上调用 Dumper 之前和之后的 JSON 对象:

$ perl -MData::Dumper -MJSON -e '
my $obj = {a => [map { $_ - 0 } split(",", "1.2,30")]};
print "JSON before: ",encode_json($obj),"\n";
print "Dumper: ",Dumper($obj);
print "JSON after: ",encode_json($obj),"\n";
'
JSON before: {"a":[1.2,30]}
Dumper: $VAR1 = {
          'a' => [
                   '1.2',
                   30
                 ]
        };
JSON after: {"a":["1.2",30]}

如您所见,Dumper 实际上会修改您要转储的对象,从而影响您随后的 encode_json 调用。

于 2013-05-31T19:14:47.470 回答
11

您已经有了答案,但我想指出在 Perl 中执行此操作的惯用方式。来自Modern Perl 的第 10 章

要确保 Perl 将值视为数字,请添加零:

my $numeric_value = 0 + $value;

要确保 Perl 将值视为布尔值,请对其进行双重否定:

my $boolean_value = !! $value;

要确保 Perl 将值视为字符串,请将其与空字符串连接:

my $string_value = '' . $value;
于 2013-05-31T19:55:18.263 回答
6

它们以字符串形式出现,因为您从split.

可能有更好的方法来做到这一点,但将它们乘以 1 似乎可行:

perl -MJSON -e 'print encode_json({a => [map { $_ * 1  } split(",", "1.2,30")]});'
于 2013-05-31T14:39:48.047 回答
3

Perl 在内部跟踪标量数据类型。您可以通过在算术表达式中使用标量来强制将其类型转换为数字。例如:

my $scalar = "3.14"; # $scalar is a string
$scalar *= 1;        # Now $scalar is a number

另一方面,如果您的问题是 的输出中有太多零sprintf,您可以通过将精度更改为其他值来解决此问题,例如 2 位:

sprintf "%.2f", $val
于 2013-05-31T14:40:53.537 回答
1

我有一个类似的案例。我的 perl 代码(以复杂的方式)生成一个复杂的数据结构,然后用 JSON::to_json 对其进行序列化并将其传递给 javascript。数据结构中有很多不同深度的数字,javascript会对它们进行算术运算。

javascript 中的“+”用作数字的加法和字符串的连接,因此在将数字转换为 json 时不要将数字放在引号中至关重要。另一方面,数据非常复杂,所以我需要简单而通用的方法来强制数字是任意数组/哈希/哈希数组等中的数字。

所以,我最终得到了这样的功能:

use Scalar::Util qw(looks_like_number);
sub force_numbers
{  
    if (ref $_[0] eq ""){
        if ( looks_like_number($_[0]) ){
            $_[0] += 0;
        }   
    } elsif ( ref $_[0] eq 'ARRAY' ){
        force_numbers($_) for @{$_[0]};
    } elsif ( ref $_[0] eq 'HASH' ) {
        force_numbers($_) for values %{$_[0]};
    }   

    return $_[0];
}   

现在我可以在将数据转换为 json 之前应用它:

print to_json(force_numbers($data));
于 2013-09-06T22:17:35.693 回答
0

已经给出了正确的解决方案。

如果由于任何原因您觉得难以采用,这里有一个更简单的建议,感谢Data::Dump它为我们完成了所有艰苦的工作:

perl -MJSON -MData::Dump=pp -le 'print encode_json eval pp { a => [split /,/, "1.2,30,b"] }'

这使:

{"a":[1.2,30,"b"]}

如果您的效率限制允许承担pp/eval往返,它应该几乎透明地解决您的问题。

它还发现您在测试中获得的引用真实值仅归因于Data::Dumper(有问题的)选择,正如其他人已经说过的那样。

于 2013-06-02T00:08:19.327 回答