Skip to content

JSON::XS and JSON::PP pass along different contexts #54

@briandfoy

Description

@briandfoy

JSON exports a decode_json that it chooses from either the preferred installed JSON parser or an acceptable one that the user provides. However, decode_json from JSON::XS and JSON::PP act slightly differently.

The first question is how JSON might want to handle this. Reading through previous bug reports, I've seen that JSON might not want to track divergent interfaces.

I'd think that the most user-friendly solution is to make decode_json act the same way despite the backend. On the development side that's a bit hairy.

The problem

In I don't want no 'wantarray', Juho Snellman notes that if decode_json uses a function call as its argument, the backend matters because JSON::XS and JSON::PP use different contexts for their argument list. JSON::XS uses scalar context but JSON::PP uses list context. Unfortunately, he used File::Slurp::read_file and was bit by its context-sensitive return value:

#!/usr/bin/perl
use v5.26;

use JSON::XS ();
use JSON::PP ();

say "JSON::XS " . JSON::XS->VERSION . " ------------";
my $ds_xs = eval { JSON::XS::decode_json read_file() };
say "Error: $@\n" if $@;

say "JSON::PP " . JSON::PP->VERSION . " ------------";
my $ds_pp = eval { JSON::PP::decode_json read_file() };
say "Error: $@\n" if $@;

say "done";

sub read_file {
	my $json = <<~"JSON";
		{
		"camel": "Amelia"
		}
		JSON
	say "Wantarray is <" . wantarray . ">";

	wantarray ? split(/\R/, $json) : $json;
	}

The result shows that JSON::PP only gets the first line of the JSON becuase it was called in list context:

$ perl json.pl
JSON::XS 4.03 ------------
Wantarray is <>
JSON::PP 4.07 ------------
Wantarray is <1>
Error: , or } expected while parsing object/hash, at character offset 1 (before "(end of string)") at json.pl line 12.


done

This shows up on which one JSON loads

#!/usr/bin/perl
use v5.26;

BEGIN {
	$ENV{PERL_JSON_BACKEND} = $ARGV[0] // 'JSON::XS';
	}

use JSON;

say "JSON backend is ", JSON->backend;

my $ds = decode_json read_file();

sub read_file {
	my $json = <<~"JSON";
		{
		"camel": "Amelia"
		}
		JSON
	say "Wantarray is <" . wantarray . ">";

	wantarray ? split(/\R/, $json) : $json;
	}

If the backend is not JSON::PP (and acceptable to JSON), there's no problem:

$ perl json2.pl
JSON backend is JSON::Backend::XS
Wantarray is <>

$ perl json2.pl JSON::PP
JSON backend is JSON::Backend::PP
Wantarray is <1>
, or } expected while parsing object/hash, at character offset 1 (before "(end of string)") at json2.pl line 12.

$ perl json2.pl Cpanel::JSON::XS
JSON backend is JSON::Backend::XS
Wantarray is <>

$ perl json2.pl Foo
The value of environmental variable 'PERL_JSON_BACKEND' is invalid. at json2.pl line 8.
Compilation failed in require at json2.pl line 8.
BEGIN failed--compilation aborted at json2.pl line 8.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions