packetie
packetie

Reputation: 5069

encoding json with no quote around numerical values

I have a perl code snippet

use JSON::XS;
$a = {"john" => "123", "mary" => "456"};
print encode_json($a),"\n";

The output is

{"john":"123","mary":"456"}

Wonder if there is an option to cause encode_json function (from JSON::XS module) to encode it so that the values (123, 456) are not surrounded by double-quote. i.e., like

{"john":123,"mary":456}

Unfortunately I can't change the hash in $a because it's passed to me from another function. Wonder if there is any trick on encode_json().

Thanks!

Upvotes: 2

Views: 1327

Answers (2)

dwarring
dwarring

Reputation: 4883

You probably need to preprocess the data yourself, prior to JSON serialization.

This solution uses Data::Leaf::Walker to traverse an arbitrary structure, converting strings to numbers.

use JSON;
use Data::Leaf::Walker;
use Scalar::Util qw();

my $a = {"john" => "123",
         "mary" => ["456","aa"],
         "fred" => "bb",
         "nested" => {"Q" => undef, "A" => 42},
         };

my $walker = Data::Leaf::Walker->new( $a );

while (my ( $key_path, $value ) = $walker->each ) {
    $walker->store($key_path, $value + 0)
        if Scalar::Util::looks_like_number $value;
};

print  to_json($a);

Output: {"john":123,"nested":{"A":42,"Q":null},"mary":[456,"aa"],"fred":"bb"}

Upvotes: 6

Leeft
Leeft

Reputation: 3837

You shouldn't use JSON::XS directly, just JSON will already load JSON::XS if available.

A scalar in Perl is tagged whether it is a string or a number, and here you're providing strings. Remove the quotes from your numbers, and they should show up unquoted as JSON already does that automatically.

If you're reading strings (from say a database) then you can coerce the strings to numbers like this:

{ john => 0+$john, mary => 0+$mary }


Update, here's a recursive replacement:

#!/usr/bin/env perl
use JSON;
use Modern::Perl;
use Scalar::Util qw( looks_like_number );

my $structure = {
    john => "123",
    mary => 456,
    deeper => {
        lucy => "35zz",
        names => [
            "john",
            "123",
            456,
        ],
    },
};

sub make_numbers_recursively {
    my ( $data ) = shift;

    if ( ref $data eq 'HASH' ) {

        # Replace hash values with recurisvely updated values
        map { $data->{ $_ } = make_numbers_recursively( $data->{ $_ } ) } keys %$data;

    } elsif ( ref $data eq 'ARRAY' ) {

        # Replace each array value with recursively processed result
        map { $_ = make_numbers_recursively( $_ ) } @$data;

    } else {

        $data += 0 if looks_like_number( $data );

    }

    return $data;
}

my $json = JSON->new->pretty;
say $json->encode( $structure );
make_numbers_recursively( $structure );
say $json->encode( $structure );

This outputs:

{
   "mary" : 456,
   "deeper" : {
      "names" : [
         "john",
         "123",
         456
      ],
      "lucy" : "35zz"
   },
   "john" : "123"
}

{
   "mary" : 456,
   "deeper" : {
      "names" : [
         "john",
         123,
         456
      ],
      "lucy" : "35zz"
   },
   "john" : 123
}

Beware that it modifies the structure in-place, so if you need the original data for anything you might want to Clone or Data::Clone it first.

Upvotes: 2

Related Questions