0

say i have the following inputted JSON object

{
   "field1": 21,
   "field2": "21",
   "field3": "hello"
}

is there any way with decode_json or from_json to know what the original type was (number verses string) of the values? I know Perl generally doesnt care about type, but I have a need to know what the original type was. I also know that perl does keep track of the type when creating a JSON object (so it does distinguish between "21" and 21 when creating a JSON object, so Im hoping there is a way to keep that information when decoding/'from'ming it.

I don't want to base it on the field name, because im trying to write something that will be used somewhat generically, and fieldnames could change.

ikegami
  • 367,544
  • 15
  • 269
  • 518
matt
  • 43
  • 1
  • 8

1 Answers1

4

When using JSON::XS, the type of the value in the scalar matches the type of the value in the document.

$ perl -e'
   use strict;
   use warnings;

   use B        qw( svref_2object SVf_IOK SVf_NOK SVf_POK );
   use JSON::XS qw( decode_json );

   my $data = decode_json(q{[ "4", 4, 4.0, 20000000000000000000 ]});

   for my $i (0..$#$data) {
      my $sv = svref_2object(\( $data->[$i] ));
      my $flags = $sv->FLAGS;
      printf("Scalar %s has %s\n",
         $i,
         join(",",
            $flags & SVf_POK ? "PV" : (),
            $flags & SVf_IOK ? "IV" : (),
            $flags & SVf_NOK ? "NV" : (),
         ),
      );
   }
'
Scalar 0 has PV
Scalar 1 has IV
Scalar 2 has NV
Scalar 3 has PV

As you can see, the fourth scalar is an exception when using JSON::XS. JSON::XS stores very large numbers as strings to avoid loosing precision.

You get similar results with JSON::PP:

Scalar 0 has PV
Scalar 1 has IV
Scalar 2 has NV
Scalar 3 has NV
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • This is obviously an edge case, but JSON numbers that consist of only digits and cannot be represented as numeric in Perl without loss of precision are represented as strings. – ThisSuitIsBlackNot Jan 15 '15 at 16:48
  • 1
    @ThisSuitIsBlackNot, I incorporated your comment into my answer. Note that JSON::PP stored the large number as an NV. – ikegami Jan 15 '15 at 17:00
  • Interesting, I didn't think to test with `JSON::PP`. Looks like the [documentation](https://metacpan.org/pod/JSON::PP#number) needs to be updated since there's no mention of that particular difference. – ThisSuitIsBlackNot Jan 15 '15 at 17:08
  • 1
    @ThisSuitIsBlackNot, Actually, 20000000000000000000 can be stored in an NV without loss. But JSON::PP also stores 20000000000000000001 as an NV. – ikegami Jan 15 '15 at 17:25
  • we ended doing something different, but i accepted your answer. sorry for the long delay, never got back to this question. – matt Jul 23 '15 at 17:26