bc3-au has asked for the wisdom of the Perl Monks concerning the following question:

Due to the work I'm doing processing returned API data, I need bools decoded using JSON::decode to be numeric, rather than JSON::PP::Boolean.

CPAN docs are great, just use $json->boolean_values(0,1);

Unfortunately that method doesn't seem to be getting exported:

#!perl -l
use 5.012; # strict, // use warnings; use JSON 4.0; use Data::Dumper; print "JSON::VERSION = $JSON::VERSION\n"; my $json = JSON->new; $json->boolean_values(0,1); test@BruceDEV:~$ perl Perl-Testing/JSON-boolean_values.pl JSON::VERSION = 4.02 Can't locate object method "boolean_values" via package "JSON" at Perl +-Testing/JSON-boolean_values.pl line 11.
I'm using Debian packages for the libraries we have infrastructure teams maintaining our systems & having them update via CPAN causes issues However the modules do look correct.
Debian GNU/Linux 10 ii libjson-perl 4.02000-1 + all module for manipulating JSON-formatted d +ata ii libjson-xs-perl 3.040-1+b1 + amd64 module for manipulating JSON-formatted d +ata (C/XS-accelerated)
Looking at the contents of /usr/share/perl5/JSON.pm I see this, so it looks like it's definitely the right module, although I'm a little suspicious as a number of the methods in the docs, including boolean_values don't seem to be exported.
package JSON; use strict; use Carp (); use Exporter; BEGIN { @JSON::ISA = 'Exporter' } @JSON::EXPORT = qw(from_json to_json jsonToObj objToJson encode_json d +ecode_json); BEGIN { $JSON::VERSION = '4.02'; $JSON::DEBUG = 0 unless (defined $JSON::DEBUG); $JSON::DEBUG = $ENV{ PERL_JSON_DEBUG } if exists $ENV{ PERL_JSON +_DEBUG }; } [...] =head2 boolean_values (since version 4.0) $json->boolean_values([$false, $true]) ($false, $true) = $json->get_boolean_values By default, JSON booleans will be decoded as overloaded C<$JSON::false> and C<$JSON::true> objects.
Any pointers ?, I *really* don't want to move to using CPAN directly on these devices, given how they're maintained.

Replies are listed 'Best First'.
Re: Issues using boolean_values in JSON v4.02
by kikuchiyo (Hermit) on Jun 14, 2023 at 09:32 UTC

    Based on the list of installed Debian packages you've posted:

    Debian GNU/Linux 10 ii libjson-perl 4.02000-1 all m +odule for manipulating JSON-formatted data ii libjson-xs-perl 3.040-1+b1 amd64 m +odule for manipulating JSON-formatted data (C/XS-accelerated)

    It's likely that the problem is that the JSON::XS module is installed, so JSON wants to use it, but it is too old (3.040), so it doesn't have the boolean_values method.

    The best solution would be upgrading JSON::XS to 4.0 or newer, but this may be problematic if there is no Debian package available for your OS version.

    Alternatively, you could force JSON to use the JSON::PP backend, which should support the boolean_values method, at the expense of being slower.

    By the way, you could probably ditch the JSON module altogether and use either JSON::PP or JSON::XS explicitly, because it's only a convoluted and slow wrapper with little to no useful new functionality.

      It's not slow. Because it uses inheritance, it's just as fast as not using it (other than the one time per process setup cost).

      The reason I avoid it is because it defaults to JSON::PP. Your program could be orders of magnitude slower than required without any feedback that this is the case, just because you forgot to install the faster back end.

      And also because it uses JSON::XS instead of Cpanel::JSON::XS. The author of JSON::XS is very contrary, and insists on keeping JSON::XS thread-unsafe, yet most Perl builds are threaded builds. I don't need this drama associated with that author.

      (JSON will work with Cpanel::JSON::XS, but you have to explicitly tell it to.)

Re: Issues using boolean_values in JSON v4.02
by hv (Prior) on Jun 14, 2023 at 02:35 UTC

    Update: As ikegami++ points out, the below won't work: as mentioned in the documentation, the different backends have incompatible object formats, so you can't mix-and-match. Without updating any modules, then, the only way to get at this functionality is to use JSON::PP as the backend (eg by setting the PERL_JSON_BACKEND environment variable), which will be at the cost of the speed benefit of the XS backend.

    It should be possible to create a shim module that checks which of the available backends has a boolean_values function, and sets the backend accordingly. But if the code is deployed only in a single place that probably has little value.

    I would expect the approach in my original answer below to work for classes that have a single master object and delegate to your choice of backends for the functionality - this is how I wrongly thought the JSON class worked.

    Looking at the code for JSON-4.02, there is no mention of boolean_values, so I suspect you will need to call the JSON::PP function explicitly. This should work:

    JSON::PP::boolean_values($json, 0, 1);

    Alternatively, in your code you could inject the desired pass-through function into the JSON package:

    { package JSON { sub boolean_values { JSON::PP::boolean_values(@_); } }; }

    .. or create your own application-specific JSON wrapper:

    package Our::JSON; use base qw{ JSON }; sub boolean_values { JSON::PP::boolean_values(@_); } 1;

    For extra points, inject the function in a BEGIN block only if !JSON->can('boolean_values'), so it automatically turns itself off when a JSON release catches up - that doesn't seem to have happened yet as of v4.10.

    I'd recommend reporting this as an issue against the JSON package, it's possible I'm missing some good reason why this isn't already implemented - I suspect it involves no more than adding it to the @PublicMethods list, with corresponding docs and tests.

      Your fix can't possibly work.

      JSON inherits from either JSON::PP, JSON::XS or Cpanel::JSON::XS

      If it's inheriting from JSON::PP, then your fix does nothing at all. That doesn't help.

      If it's inheriting from JSON::XS, your fix would cause a JSON::XS object to be passed to JSON::PP. That won't work.

      If it's inheriting from Cpanel::JSON::XS, your fix would cause a Cpanel::JSON::XS object to be passed to JSON::PP. That won't work.

        Ah, I think you're right; I guess I didn't read enough of the code - I see now that packages such as JSON::Backend::PP fiddle with @JSON::ISA (which seems like exactly the sort of action-at-a-distance we usually recommend against).