http://qs321.pair.com?node_id=11118555

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

use strict; use warnings; my @json = jsonsolute('JSON::MaybeXS', 'encode', 'decode'); print $json[0], "\n"; print $json[1]->{key}, "\n"; @json = jsonsolute(); sub jsonsolute { my ($jsonpackage, $jsonefunction, $jsondfunction) = @_; $jsonpackage ||= 'JSON::MaybeXS'; $jsonefunction ||= 'encode_json'; $jsondfunction ||= 'decode_json'; eval "require $jsonpackage"; if ($@) { print "package $jsonpackage missing\n"; } else { my $json = $jsonpackage->new(pretty => 1, canonical => 1); my $encoded = $json->$jsonefunction({key => 'value'}); my $decoded = $json->$jsondfunction('{"key":"value"}'); return $encoded, $decoded; } }
results in:
{ "key" : "value" } value encountered object 'Cpanel::JSON::XS=SCALAR(0x564228375740)', but neit +her allow_blessed, convert_blessed nor allow_tags settings are enable +d (or TO_JSON/FREEZE method missing) at ...(my $encoded)

Replies are listed 'Best First'.
Re: incorrect methods yield success
by haukex (Archbishop) on Jun 26, 2020 at 09:39 UTC
    incorrect methods yield success

    Sorry, that's not really much to go on, the behavior looks correct to me. $jsonpackage->new creates a new object (in this case of type Cpanel::JSON::XS). encode is a method, but encode_json is not, but in the second call of jsonsolute, $json->$jsonefunction calls encode_json as a method, meaning that the parameters that the function encode_json sees are actually ($json, {key => 'value'}), and it doesn't know how to handle the object $json, which is of type Cpanel::JSON::XS, so it dies.

    use warnings; use strict; use Data::Dump; dd jsonsolute('JSON::MaybeXS', 'encode', 'decode'); dd jsonsolute(); print "Done\n"; sub jsonsolute { my ($jsonpackage, $jsonefunction, $jsondfunction) = @_; $jsonpackage ||= 'JSON::MaybeXS'; $jsonefunction ||= 'encode_json'; $jsondfunction ||= 'decode_json'; dd $jsonpackage, $jsonefunction, $jsondfunction; if ( !defined eval "require $jsonpackage; 1" ) { die "package $jsonpackage missing\n"; } else { my $json = $jsonpackage->new(pretty => 1, canonical => 1); dd $json; my $encoded = $json->$jsonefunction({key => 'value'}); my $decoded = $json->$jsondfunction('{"key":"value"}'); dd $encoded, $decoded; return $encoded, $decoded; } } __END__ ("JSON::MaybeXS", "encode", "decode") bless(do{\(my $o = "")}, "Cpanel::JSON::XS") ("{\n \"key\" : \"value\"\n}\n", { key => "value" }) ("{\n \"key\" : \"value\"\n}\n", { key => "value" }) ("JSON::MaybeXS", "encode_json", "decode_json") bless(do{\(my $o = "")}, "Cpanel::JSON::XS") encountered type (HASH(0x52aeb6d595a8), 0x80801) was specified for 'Cp +anel::JSON::XS=SCALAR(0x52aeb6e99850)' at 11118555.pl line 20.
      reading the JSON::MaybeXS docs there could be some confusion as to the section "which works equivalently to the above (and in the usual tradition will accept a hashref instead of a hash, should you so desire).", specifically an optimistic interpretation of "the above"
        there could be some confusion ... specifically an optimistic interpretation of "the above"

        You appear to be referring to this:

        CONSTRUCTOR

        new

        With JSON::PP, JSON::XS and Cpanel::JSON::XS you are required to call mutators to set options, such as:

        my $json = $class->new->utf8(1)->pretty(1);

        Since this is a trifle irritating and noticeably un-perlish, we also offer:

        my $json = JSON::MaybeXS->new(utf8 => 1, pretty => 1);

        which works equivalently to the above (and in the usual tradition will accept a hashref instead of a hash, should you so desire).

        The resulting object is blessed into the underlying backend, which offers (at least) the methods encode and decode.

        The line of code the quoted text refers to doesn't show a use of encode or decode, nowehere in the documentation does it show the use of encode_json or decode_json as a method, and the line immediately following the quoted text specifically refers to the methods encode and decode. In addition, this module is simply a frontend for other modules such as Cpanel::JSON::XS ("The new() method in this module is technically a factory, not a constructor, because the objects it returns will NOT be blessed into the JSON::MaybeXS class."), whose documentation differentiates between the functional interface and the OO interface even more clearly. Taken in context, "above" IMHO pretty clearly refers to the line of code immediately above the sentence, not to something four subsections above it. (You are of course still free to submit a documentation patch.)

Re: incorrect methods yield success
by tobyink (Canon) on Jun 27, 2020 at 07:06 UTC

    encode_json and decode_json are intended to be called as functions, not methods. Use encode and decode instead.

Re: incorrect methods yield success
by perlfan (Vicar) on Jun 27, 2020 at 13:24 UTC
    Do not forget, most of the JSON modules will look for and utilize the TO_JSON method implemented by any references' packages that are contained in the main reference you are attempting to serialize. It comes in handy and I wish I knew about this long ago. Note your error at the end mentions TO_JSON/FREEZE (didn't realize FREEZE was also considered).

    To be clear, this means that in you must implement a TO_JSON yourself in the package of the reference contained in the overall reference you are serializing. hth.

    Conversely, if package references or instances are getting unexpectedly serialized correctly, then look for a TO_JSON in the module .pm(s) themselves.