如何避免使用Perl JSON模块在数字周围加引号?

时间:2013-05-31 14:33:25

标签: json perl google-visualization

有没有办法让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脚本解析CSV文件并生成HTML文件using Google Charts,后者因数字值的引号而感到困惑(尽管我告诉他们列的类型为“数字”)。

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

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

enter image description here

更新

是的,乍看之下添加零或乘以一个似乎可以正常工作

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

但在my real script中,仍不适用于浮点数

您也可以在使用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
                 ]
        };

6 个答案:

答案 0 :(得分:9)

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

e.g。此示例显示在对象上调用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调用。

答案 1 :(得分:9)

你已经有了这方面的答案,但我想指出在Perl中这样做的惯用方法。来自chapter 10 of Modern Perl

  

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

my $numeric_value = 0 + $value;
     

为确保Perl将值视为boolean,请将其否定:

my $boolean_value = !! $value;
     

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

my $string_value = '' . $value;

答案 2 :(得分:6)

它们以字符串形式出现,因为你从split中获取字符串。

可能有更好的方法,但将它们乘以1似乎可行:

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

答案 3 :(得分:3)

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

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

另一方面,如果您的问题在sprintf的输出中有太多的零,您可以通过将精度更改为其他内容来修复此问题,例如2位数:

sprintf "%.2f", $val

答案 4 :(得分: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));

答案 5 :(得分: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(可疑)选择,正如其他人已经说过的那样。