5
votes

From what I read, perl doesn't have a real type for integer or string, for him any $variable is scalar.

Recently I had a lot of trouble with an old script that was generating JSON objects required by another process for which values inside JSON must be integers, after some debug I found that because a simple print function:

JSON::encode_json

was generating a string instead an integer, here's my example:

use strict; 
use warnings; 
use JSON;
my $score1 = 998;
my $score2 = 999;
print "score1: ".$score1."\n";
my %hash_object = ( score1 => $score1, score2 => $score2 );
my $json_str = encode_json(\%hash_object);  # This will work now
print "$json_str";

And it outputs:

score1: 998
{"score1":"998","score2":999}

Somehow perl variables have a type or at least this is how JSON::encode_json thinks.

Is there a way to find this type programmatically and why this type is changed when making an operation like the concatenation above?

2
%arr is a terrible name for a variable. First of all, it's a hash, not an array, and secondly the identifier should reflect a variables purpose rather than its nature. The % says that it's a hash, so %hash is superfluous and slightly ridiculous. But sometimes there is little to say, especially about aggregate data structures, and I have often resorted to %data which I think is fine - Borodin
Thank you for suggestions , I do agree with you and made some changes in my example. - Adrian Toma
Only a thought suggestion, not sure if it will work in practice. But could your wrap score1 with int? EX: my %hash_object = ( score1 => int($score1), score2 => $score2 ); - Steven Carlson

2 Answers

8
votes

First, it is somewhat incorrect. Yes, every values is scalar, but scalars have separate numeric and string values. That's all you need to know for this particular question, so I'll leave details out.

What you really need is to take a look at MAPPING / PERL -> JSON / simple scalars section of documentation for JSON module:

JSON::XS and JSON::PP will encode undefined scalars as JSON null values, scalars that have last been used in a string context before encoding as JSON strings, and anything else as number value.

(The docs are actually slightly wrong here. It is not "last been used in string context" it should be "was ever used in string context" - once scalar obtains string value it won't go away until you explicitly write a new number into it.)

You can force the type to be a string by stringifying it:

my $x = 3.1; # some variable containing a number
"$x";        # stringified
$x .= "";    # another, more awkward way to stringify
print $x;    # perl does it for you, too, quite often

Incidentally this comment above is exactly about why your 998 turned to string.

You can force the type to be a number by numifying it:

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.
4
votes

Perl scalars may hold both a string and a scalar value simultaneously. Either of those is valid as a JSON value, so the string value is always used if there is one

This is a rare problem, as most software should accept either a quoted or unquoted representation of a number. Everything in a JSON document is originally a string, and significant data may be lost by requiring the module to apply an arbitrary string → float conversion before it hands you the data

But if you really need to write a numeric value without quotation marks then you can coerce a scalar to behave as a numeric type: just add zero to it.

Likewise, if a scalar variable holds only a numeric variable then you can force Perl to generate and store its equivalent string just by using it as a string. The most obvious method for me is to enclose it in quotation marks

This code appears to do what you want

use strict;
use warnings 'all';
use feature 'say';

use JSON;

my ($score1, $score2) = (998, 999);

print "score1: $score1\n";

my %data = ( score1 => 0+$score1, score2 => 0+$score2 );

say encode_json(\%data);

output

score1: 998
{"score1":998,"score2":999}