NAME

Module::Generic::JSON - A thin and reliable wrapper around JSON

SYNOPSIS

use Module::Generic::JSON;
my $j = Module::Generic::JSON->new(
    utf8         => 1,
    pretty       => 1,
    canonical    => 1,
    relaxed      => 1,
    allow_nonref => 1,
) || die( Module::Generic::JSON->error );
$j->encode( $some_ref ) || die( $j->error );

Or

my $j = Module::Generic::JSON->new;
$j->utf8->pretty->canonical->relaxed->allow_nonref->encode( $some_ref ) ||
    die( $j->error );

Or, even simpler:

use Module::Generic::JSON qw( new_json );
my $j = new_json(
    utf8         => 1,
    pretty       => 1,
    canonical    => 1,
    relaxed      => 1,
    allow_nonref => 1,
) || die( Module::Generic::JSON->error );
$j->encode( $some_ref ) || die( $j->error );

VERSION

v0.2.2

DESCRIPTION

This is a thin and reliable wrapper around the otherwise excellent JSON class. Its added value is:

  • Allow the setting of all the JSON properties upon object instantiation

    As mentioned in the synopsis, you can do:

    my $j = Module::Generic::JSON->new(
        utf8         => 1,
        pretty       => 1,
        canonical    => 1,
        relaxed      => 1,
        allow_nonref => 1,
    ) || die( Module::Generic::JSON->error );

    instead of:

    my $j = Module::Generic::JSON->new;
    $j = $j->utf8->pretty->canonical->relaxed->allow_nonref;
  • No fatal exception that would kill your process inadvertently.

    This is important in a web application where you do not want some module killing your process, but rather you want the exception to be handled gracefully.

    Thus, instead of having to do:

    local $@;
    my $ref = eval{ $j->decode( $payload ) };
    if( $@ )
    {
        # Like returning a 500 or maybe 400 HTTP error
        bailout_gracefully( $@ );
    }

    you can simply do:

    my $ref = $j->decode( $payload ) || bailout_gracefully( $j->error );
  • Upon error, it returns an exception object

  • All methods calls are passed through to JSON, and any exception is caught, and handled properly for you.

For class functions too, you can execute them safely and catch error, if any, by calling Module::Generic::JSON->error, so for example:

decode_json( $some_data ) || die( Module::Generic::JSON->error );

CONSTRUCTOR

new

This takes an hash or hash reference of options and returns a new Module::Generic::JSON object. The options must be supported by JSON. On error, sets an error object and returns undef in scalar context or an empty list in list context:

my $j = Module::Generic::JSON->new( utf8 => 1, pretty => 1 ) ||
    die( Module::Generic::JSON->error );

METHODS

See the documentation for the module JSON for more information, but below are the known methods supported by JSON

allow_blessed

allow_nonref

allow_tags

allow_unknown

ascii

backend

boolean

boolean_values

canonical

convert_blessed

decode

Decodes a JSON string and returns the resulting Perl data structure. On error, sets an error object and returns undef in scalar context or an empty list in list context:

my $data = $j->decode( '{"a":1}' ) || die( $j->error );

decode_prefix

encode

Encodes a Perl data structure into a JSON string. On error, sets an error object and returns undef in scalar context or an empty list in list context:

my $json_str = $j->encode( { a => 1 } ) || die( $j->error );

filter_json_object

filter_json_single_key_object

indent

is_pp

is_xs

latin1

max_depth

max_size

pretty

property

relaxed

space_after

space_before

utf8

CLASS FUNCTIONS

decode_json

Decodes a JSON string and returns the resulting Perl data structure. On error, sets an error object and returns undef in scalar context or an empty list in list context:

my $data = decode_json( '{"a":1}' ) || die( Module::Generic::JSON->error );

encode_json

Encodes a Perl data structure into a JSON string. On error, sets an error object and returns undef in scalar context or an empty list in list context:

my $json_str = encode_json( { a => 1 } ) || die( Module::Generic::JSON->error );

from_json

Decodes a JSON string with optional configuration options. Takes a JSON string and an optional hash reference of options (passed to "new"). On error, sets an error object and returns undef in scalar context or an empty list in list context:

my $data = from_json( '{"a":1}', { utf8 => 1 } ) || die( Module::Generic::JSON->error );

to_json

Encodes a Perl data structure with optional configuration options. Takes a Perl data structure and an optional hash reference of options (passed to "new"). On error, sets an error object and returns undef in scalar context or an empty list in list context:

my $json_str = to_json( { a => 1 }, { pretty => 1 } ) || die( Module::Generic::JSON->error );

SERIALISATION

Module::Generic::JSON inherits serialisation methods from Module::Generic. The following subroutines are implemented: FREEZE, THAW, STORABLE_freeze, and STORABLE_thaw. See Module::Generic for details.

THREAD-SAFETY

Module::Generic::JSON is thread-safe for all operations, as it operates on per-object state and avoids the thread-safety issues present in the underlying JSON module.

Key considerations for thread-safety:

  • Shared Variables

    There are no shared variables that are modified at runtime in Module::Generic::JSON. The global $DEBUG variable (inherited from Module::Generic) is typically set before threads are created, and it is the user's responsibility to ensure thread-safety if modified at runtime:

    use threads;
    local $Module::Generic::JSON::DEBUG = 0; # Set before threads
    my @threads = map
    {
        threads->create(sub
        {
            my $json = Module::Generic::JSON->new( utf8 => 1 );
            $json->encode( { a => 1 } ); # Thread-safe
        });
    } 1..5;
    $_->join for( @threads );

    Note that the JSON module uses a global $JSON variable for functions like encode_json and decode_json, which can lead to thread-safety issues if modified at runtime. Module::Generic::JSON avoids this by creating a new object instance for each call to "decode_json" and "encode_json", ensuring thread isolation.

  • Object State

    The underlying JSON object is stored per-object, ensuring thread isolation:

    use threads;
    my @threads = map
    {
        threads->create(sub
        {
            my $json = Module::Generic::JSON->new( utf8 => 1 );
            $json->encode( { tid => threads->tid } ); # Thread-safe
        });
    } 1..5;
    $_->join for( @threads );
  • Class Functions

    Class functions like "decode_json" and "encode_json" create a new object per call, ensuring thread isolation:

    use threads;
    my @threads = map
    {
        threads->create(sub
        {
            my $data = decode_json( '{"a":1}' ); # Thread-safe
        });
    } 1..5;
    $_->join for( @threads );
  • External Libraries

    The underlying JSON module (both JSON::XS and JSON::PP) is thread-safe for object methods, as it operates on per-object state. However, its class functions (e.g., encode_json, decode_json) are not thread-safe due to the use of a global $JSON variable. Module::Generic::JSON mitigates this by always creating a new instance for such calls.

  • Serialisation

    Serialisation methods ("FREEZE", "THAW") operate on per-object state, making them thread-safe.

For debugging in threaded environments (depending on your Operating System):

ls -l /proc/$$/fd  # List open file descriptors

AUTHOR

Jacques Deguest <jack@deguest.jp>

SEE ALSO

JSON, Module::Generic::Exception

COPYRIGHT & LICENSE

Copyright(c) 2025 DEGUEST Pte. Ltd.

All rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.