++ed by:

1 non-PAUSE user.

Ben Bullock

NAME

JSON::Create - fast, minimal, UTF-8-only serialization of data to JSON

SYNOPSIS

    use JSON::Create 'create_json';
    my %hash = (a => 'b', c => 'd');
    print create_json (\%hash);
    

produces output

    {"a":"b","c":"d"}

(This example is included as examples/synopsis.pl in the distribution.)

VERSION

This document describes JSON::Create version 0.24, corresponding to git commit c2c5e58942d527b579d7073ea8870767383da2ae at Tue Jun 13 17:00:40 2017 +0900.

DESCRIPTION

JSON::Create encodes Perl variables into the JSON format. It offers "create_json", which uses common defaults, "create_json_strict", which accepts only unambiguous inputs, and a customizable object created with "new" and run with "run" where the user can specify a variety of behaviours.

JSON::Create aims to provide fast and minimal serialization of data types. It handles no string encoding except UTF-8. The module supports serialization of objects and other non-data types via user-defined callbacks.

Errors in processing result in a warning and an undefined return value. This behaviour can be altered with the method "fatal_errors".

This module documentation is generated from machine readable code. All of the example programs in this documentation may be found in the directory examples/ in the distribution. All of the example outputs displayed were generated by running the example programs.

This module is a companion to JSON::Parse for parsing JSON into Perl.

FUNCTIONS

create_json

    my $json = create_json (\%hash);

This converts a hash reference, array reference, or scalar into JSON. The return value is the output JSON.

Details of the conversion of each type are given in "CONVERSIONS".

create_json_strict

    my $json = create_json_strict (\%hash);

The same as "create_json", except that it rejects ambiguous inputs. See "strict" for details.

This function was added in version 0.20 of the module.

CONVERSIONS

This section details what conversions are applied to the various inputs to produce outputs.

Hashes

JSON::Create turns associative arrays into JSON objects. The keys are written into JSON as strings, with any control characters escaped. The order of the keys is as they are supplied by Perl.

    use JSON::Create 'create_json';
    my %example = (
        x => 1,
        y => 2,
        z => 3,
    );
    print create_json (\%example);

produces output

    {"y":2,"x":1,"z":3}

(This example is included as examples/hash.pl in the distribution.)

Nested hashes are recursively followed:

    use JSON::Create 'create_json';
    my %example = (
        x => {
            y => 2,
            z => 3,
        },
        a => {
            b => 4,
            c => 5,
        },
    );
    print create_json (\%example);

produces output

    {"a":{"c":5,"b":4},"x":{"y":2,"z":3}}

(This example is included as examples/nested-hash.pl in the distribution.)

Arrays

Arrays are converted to JSON arrays. The order of the array is identical to the Perl one.

    use JSON::Create 'create_json';
    my @array = (1, 2, 2.5, qw/mocha dusty milky/, qw/Tico Rocky Pinky/);
    print create_json (\@array);

produces output

    [1,2,2.5,"mocha","dusty","milky","Tico","Rocky","Pinky"]

(This example is included as examples/array.pl in the distribution.)

Nested arrays are recursively followed:

    use JSON::Create 'create_json';
    my @array = ([1, 2, 2.5], [qw/mocha dusty milky/], [qw/Tico Rocky Pinky/]);
    print create_json (\@array);

produces output

    [[1,2,2.5],["mocha","dusty","milky"],["Tico","Rocky","Pinky"]]

(This example is included as examples/nested-array.pl in the distribution.)

Nested hashes and arrays are converted similarly:

    use JSON::Create 'create_json';
    my $nested = {
        numbers => [1, 2, 2.5, 99.99],
        cats => [qw/mocha dusty milky/],
        dogs => [qw/Tico Rocky Pinky/],
        fruit => {
            thai => 'pineapple',
            japan => 'persimmon',
            australia => 'orange',
        },
    };
    print create_json ($nested);

produces output

    {"cats":["mocha","dusty","milky"],"dogs":["Tico","Rocky","Pinky"],"numbers":[1,2,2.5,99.99],"fruit":{"thai":"pineapple","australia":"orange","japan":"persimmon"}}

(This example is included as examples/nested.pl in the distribution.)

Scalars

Non-reference Perl scalars are converted to JSON strings or numbers, depending on what Perl thinks they contain.

Strings

As far as possible, strings are written as they are to the JSON.

JSON is in Unicode, so all output must be checked for Unicode validity. Further, this module insists on the UTF-8 encoding. (See "Input strings must be UTF-8".) Invalid UTF-8 within input strings produces the error "Invalid UTF-8" and the undefined value is returned. This behaviour can be altered with the method "replace_bad_utf8". (For full details of the corner cases, see "UNICODE HANDLING".)

Some whitespace and control characters must be also escaped for the output to be valid JSON. (See "RFC 7159".)

In addition to this, "create_json_strict" or the "strict" option reject inputs containing non-ASCII bytes (bytes with values of from 128 to 255) which are not marked as character strings.

Control characters and whitespace

To form valid JSON, bytes of value less than 0x20 in a Perl string must be converted into JSON escapes, either the whitespace escapes \b (backspace) \r, \t, \n, and \f, or the form \u0001 for other control characters. Further, the backslash must be written as \\ and double quotes must be written as \".

This example demonstrates some of the necessary escaping:

    use JSON::Create 'create_json';
    # An example string containing various things.
    my $weirdstring = {weird => "\t\r\n\x00 " . '"' . '\\' . '/' };
    print create_json ($weirdstring);
    

produces output

    {"weird":"\t\r\n\u0000 \"\\/"}

(This example is included as examples/weirdstring.pl in the distribution.)

U+2028 and U+2029 (JavaScript clashes)

    my $out = create_json (["\x{2028}"]);
    # $out = '["\u2028"]'

Although it is not required by the JSON standard, JSON::Create by default escapes Unicode code points U+2028 and U+2029 as \u2028 and \u2029 for JavaScript compatibility. This behaviour can be altered with the method "no_javascript_safe".

This escaping is necessary for JavaScript because of a clash between the JSON standard and the JavaScript (ECMAScript) standard. The characters U+2028 ("LINE SEPARATOR" in the Unicode standard) and U+2029 ("PARAGRAPH SEPARATOR" in the Unicode standard) are valid within JSON, as defined by "RFC 7159", but invalid within JavaScript strings, as defined by the ECMA standard (See ECMA Standard ECMA-262, "ECMAScript Language Specification", 3rd Edition, section 7.3 "Line Terminators").

Other escapes

The forward slash, /, known as "solidus" in the JSON specification, does not have to be escaped, and JSON::Create's default is not to escape it. This behaviour can be altered with the method "escape_slash".

Other Unicode values are not escaped. This behaviour can be altered with the method "unicode_escape_all".

Integers

Integers are printed in the obvious way. Note that what you input to Perl as an integer, Perl may interpret to be a floating point number, if it has a very large absolute value, in which case this module will print it out like a floating point number.

If the contents of the scalar appear to be both an integer and a string to Perl (if it is a PVIV in the Perl XS terminology), it is printed using Perl's string representation (the PV rather than the IV is used). Usually no quotes are added, but if the string representation (the PV) starts with a zero, and is not a floating-point number, it is also printed with quotes, as in "01", rather than being printed as 1 or 01 (an error in JSON). This works around an unusual case where strings consisting of numbers, such as Huffman codes (see Compress::Huffman) are recognized as integers by Perl. This behaviour was added in version 0.22 of the module.

Floating point numbers

Finite floating point numbers are printed using printf formatting via "%g", like

    printf ("%g", $number);

This behaviour can be altered with the method "set_fformat"

JSON does not allow NaN or infinity as bare values. From page 6 of "RFC 7159":

    Numeric values that cannot be represented in the grammar below (such as Infinity and NaN) are not permitted.

"create_json" converts NaN (not a number) values to "nan" (the letters nan surrounded by double quotes), and positive and negative infinity to "inf" and "-inf" respectively.

"create_json_strict" disallows non-finite numbers. If a non-finite number appears within its input, it prints a warning "Non-finite number in input" and returns the undefined value:

    use JSON::Create 'create_json_strict';
    print create_json_strict (9**9**9);
    

produces output

    Non-finite number in input at /usr/home/ben/projects/json-create/examples/strict-non-finite.pl line 5.
    Use of uninitialized value in print at /usr/home/ben/projects/json-create/examples/strict-non-finite.pl line 5.

(This example is included as examples/strict-non-finite.pl in the distribution.)

A JSON::Create object created with "new" converts in the same way as "create_json". This behaviour can be altered with the method "non_finite_handler". If "strict" is specified, non-finite numbers are passed to "non_finite_handler" if it is set, and if not, it prints a warning "Non-finite number in input" and returns the undefined value.

The undefined value

Undefined values in the input are mapped to the JSON literal "null".

    use JSON::Create 'create_json';
    print create_json ({a => undef, b => [undef, undef]}), "\n";
    

produces output

    {"a":null,"b":[null,null]}

(This example is included as examples/undef.pl in the distribution.)

Booleans

Booleans (true and false) from input via JSON::Parse version 0.37 or later will be turned into the outputs true and false:

    use JSON::Parse '0.38', 'parse_json';
    use JSON::Create 'create_json';
    my $in = parse_json ('[true,false,"boo"]');
    print create_json ($in);

produces output

    [true,false,"boo"]

(This example is included as examples/json-parse-bool.pl in the distribution.)

Other kinds of object can be converted to booleans using the method "bool" (see below).

Other types

JSON::Create is meant to provide serialization of data types. It does not provide built-in serialization of Perl objects and other non-data types, such as code references or regular expressions, beyond a few basic defaults. How to handle non-data types is left completely up to users. These basic defaults, and how to set up more extensive handling, are described in the following subsections.

Scalar references

"create_json" dereferences scalar references, then treats them as scalars in the way described in "Scalars".

"create_json_strict" rejects scalar references. Input containing a scalar reference causes a warning "Input's type cannot be serialized to JSON" and the undefined value to be returned.

A JSON::Create object created with "new" behaves as "create_json" unless the "strict" option is specified. If the strict option is specified, scalar references are passed through to "type_handler" if it is set, otherwise input containing a scalar reference causes a warning "Input's type cannot be serialized to JSON" and the undefined value to be returned.

Objects

"create_json" handles Perl objects as if non-object types. In other words, unless you specify object handling, it breaks encapsulation.

"create_json_strict" rejects input containing Perl objects. If the input contains an object (a blessed reference), a warning "Object cannot be serialized to JSON" is printed and the undefined value returned.

A JSON::Create object created with "new" can handle specified types of object with method "obj", or with a general object handler supplied via "obj_handler". By default it behaves like "create_json". If "strict" is chosen, it rejects input containing Perl objects unless the user sets a handler for them with "obj" or "obj_handler".

Code, regexes, and other references

A code or other reference (regexes, globs, etc.) in the input of "create_json" or "create_json_strict" prints a warning "Input's type cannot be serialized to JSON" and causes the entire return value to be the undefined value. This behaviour can be altered with the method "type_handler".

METHODS

If you need to alter the format of the output from the defaults of "create_json" or "create_json_strict", create an object with "new" and then set preferences on that object before producing output with "run".

new

    my $jc = JSON::Create->new ();

Create a new "JSON::Create" object. Use "run" to generate JSON with it.

run

    my $json = $jc->run ($input);

This does exactly the same thing as "create_json", unless the output format associated with $jc has been altered using "Methods for formatting the output". The return value is the output JSON.

fatal_errors

    $jc->fatal_errors (1);

If this is called with a true value, errors in the input are upgraded from warnings to fatal errors.

    use JSON::Create;
    my $jc = JSON::Create->new ();
    $jc->fatal_errors (1);
    my $invalid_utf8 = "\x{99}\x{ff}\x{88}";
    eval {
        $jc->run ($invalid_utf8);
    };
    if ($@) {
        print "Fatal error: $@\n";
    }

produces output

    Fatal error: Invalid UTF-8 at /usr/home/ben/projects/json-create/examples/fatal-errors.pl line 9.
    

(This example is included as examples/fatal-errors.pl in the distribution.)

This method was added in version 0.10 of the module.

strict

    $jc->strict (1);

This switches on rejection of ambiguous inputs, which means

  • all non-data types, including objects,

  • strings containing non-ASCII bytes (bytes with values of from 128 to 255) which are not marked as utf8 (character strings),

  • non-finite floating point numbers (NaN or infinite values), and

  • scalar references.

Calling "run" with such inputs results in a return value of undef (the undefined value) and a warning being printed. You can override the behaviour for objects with "obj", "obj_handler", for non-data types and scalar references with "type_handler", and for non-finite numbers with "non_finite_handler".

The rejection of non-ASCII bytes in non-utf8 strings cannot be overridden, so users need to ensure that all input is either ASCII-only or character string-only (utf8).

This method was added in version 0.20 of the module.

Methods for formatting the output

These methods work on the object created with "new" to format the output JSON in a different way from the default when operating "run".

These methods do not affect the behaviour of "create_json" or "create_json_strict".

bool

    $jc->bool ('boolean');
    $jc->bool (qw/boolean JSON::Tiny::_Bool/);

Given a list of names of object types, the JSON::Create object, $jc in the example, will convert objects of these types into the JSON literals true or false depending on whether Perl thinks they're true or false. For example,

    use JSON::Create;
    use boolean;
    my $thing = {'Yes' => true, 'No' => false};
    my $jc = JSON::Create->new ();
    print $jc->run ($thing), "\n";
    $jc->bool ('boolean');
    print $jc->run ($thing), "\n";
    

produces output

    {"Yes":1,"No":0}
    {"Yes":true,"No":false}

(This example is included as examples/boolean.pl in the distribution.)

If you prefer to take over all object handling yourself, there is also "obj_handler", which overrides what is set with bool.

Interoperability

The boolean values of the following Perl modules can interoperate with JSON::Create.

boolean
    $jc->bool ('boolean');
JSON::Tiny
    $jc->bool ('JSON::Tiny::_Bool');

Round trip compatibility is also confirmed for JSON::Tiny version 0.54.

JSON::PP
    $jc->bool ('JSON::PP::Boolean');

Round trip compatibility is also confirmed for JSON::PP version 2.27300.

Types::Serialiser
    $jc->bool ('JSON::PP::Boolean');

Please note the above is not a typo, JSON::PP::Boolean is the correct object type for Types::Serialiser. To confirm this, try

    print ref $Types::Serialiser::false;
Mojo::JSON
    $jc->bool ('JSON::PP::Boolean', 'Mojo::JSON::_Bool');

Round trip compatibility is also confirmed for Mojo::JSON version 6.38.

The current version of Mojo::JSON (Mojolicious version 6.38) actually depends on JSON::PP and uses the type JSON::PP::Boolean for its true and false values. Older versions used their own type, Mojo::JSON::_Bool.

JSON::Create's compatibility tests for Mojo::JSON compatibility are available only in the git repository as xt/mojo-json.t, rather than in the CPAN release, because different versions of Mojolicious differ a lot in not only function names but also variable names, as seen above.

You can handle multiple modules with the same object:

    $jc->bool (qw/boolean JSON::Tiny::_Bool JSON::PP::Boolean/);

The compatibility of the above modules can be confirmed by running the test script t/bool.t in the distribution. However, JSON::Create does not install these modules, so unless you have installed them yourself, the tests will just be skipped.

More modules will be added to this list as time permits.

downgrade_utf8

    $jc->downgrade_utf8 (1);

If this is set to a true value, the return value of "create_json" or "run" is never upgraded to character strings, or utf8. This overrides the default behaviour, which is to upgrade the output to utf8 if any part of the input is utf8, or if the user has requested replacement with "replace_bad_utf8" and there are bad characters in the user's input. See "UNICODE HANDLING" for details. All output of JSON::Create is valid UTF-8, regardless of what this flag is set to. See "Output is valid UTF-8".

This method should be considered experimental.

This method was added in version 0.18 of the module.

escape_slash

    $jc->escape_slash (1);

Call this with a true value to make the slash (known as the "solidus" in the JSON specification) be escaped with a backslash. Call this with any false value to make the slash not be escaped (the default behaviour).

    use JSON::Create;
    my $jc = JSON::Create->new ();
    my $in = {'/dog/' => '/run/'};
    print $jc->run ($in), "\n";
    $jc->escape_slash (1);
    print $jc->run ($in), "\n";
    $jc->escape_slash (0);
    print $jc->run ($in), "\n";

produces output

    {"/dog/":"/run/"}
    {"\/dog\/":"\/run\/"}
    {"/dog/":"/run/"}

(This example is included as examples/escape-slash.pl in the distribution.)

See also "Other escapes".

This method was added in version 0.07 of the module.

no_javascript_safe

    $jc->no_javascript_safe (1);

If called with a true value, this switches off JavaScript protection in the output JSON. If called with a false value, the JavaScript protection is switched on again.

    use JSON::Create;
    my $in = ["\x{2028}"];
    my $jc = JSON::Create->new ();
    print $jc->run ($in), "\n";
    $jc->no_javascript_safe (1);
    print $jc->run ($in), "\n";
    

produces output

    ["\u2028"]
    ["
"]

(This example is included as examples/js-safe.pl in the distribution.)

See also "U+2028 and U+2029 (JavaScript clashes)".

non_finite_handler

   $jc->non_finite_handler (\& handler);

This overrides the default behaviour for handling non-finite floating point numbers, in other words NaN (not a number) and negative or positive infinity, with a user-defined routine. The default behaviour of this module is described at "Floating point numbers".

The routine handler is supplied with the non-finite number as its sole argument, and returns one argument, the output JSON. For example, to always use null in place of the default, supply a function like the following:

    use JSON::Create;
    my $bread = { 'curry' => -sin(9**9**9) };
    my $jcnfh = JSON::Create->new ();
    print $jcnfh->run ($bread), "\n";
    $jcnfh->non_finite_handler(sub { return 'null'; });
    print $jcnfh->run ($bread), "\n";

produces output

    {"curry":"nan"}
    {"curry":null}

(This example is included as examples/non-finite-handler.pl in the distribution.)

  • Calling convention

    The non_finite_handler routine is passed a single argument and is expected to return a single argument, the JSON to output. It is called in scalar context. In other words, the call looks like the following:

         $json = &{$jc->{non_finite_handler}} ($item);

    To pass or return multiple values via the non_finite_handler callback, use a closure. See the discussion at "obj" for an example.

  • Returning undef halts processing

    If your handler returns the undefined value, "run" prints a warning "Undefined value from user routine", halts further processing of the input, and returns the undefined value.

  • Delete the handler with any false value

    To remove the handler, simply call the function without an argument,

        $jc->non_finite_handler ();

    or with a false argument:

        $jc->non_finite_handler (0);

    The behaviour then reverts to the default.

  • Checking the output JSON

    The JSON output by your handler may be checked for validity by switching on validation using "validate". If you do not use this, and your return value happens to contain invalid UTF-8, you may see the error "Invalid UTF-8 from user routine". Please see "UTF-8 validation of user-supplied JSON" for more about this error.

  • Exception handling

    Exceptions (die) thrown within non_finite_handler callbacks are not caught by "run" but passed through to the parent. Please see the discussion at "obj" for an example.

This method was added in version 0.17 of the module.

obj

    $jc->obj ('Zilog::Z80' => sub { my ($obj) = @_; print "\"Z80\""; });

Register JSON generators for Perl objects. When JSON::Create finds an object with a registered type, it will call the method you have supplied.

The argument to obj is a hash. The keys are object names, and the corresponding values are code references to the JSON serializer for that object:

    $jc->obj (
        'My::Object' => \& object_to_json,
    );

The output is passed through to the output string unaltered. To have your JSON output checked for validity, use the "validate" option.

The function is called with the object reference as its only argument, as if called like this:

    my $user_json = $my_object->object_to_json ();

The return value of the function, object_to_json in the above example, must be a single value, a string containing the object's JSON encoding.

    use JSON::Create;
    my $jc = JSON::Create->new ();
    package Zilog::Z80;
    sub new { return bless { memory => '64 kbytes' }; }
    sub to_json {
        my ($self) = @_;
        return '"I can address as many as '.$self->{memory}.' of memory"';
    }
    1;
    package main;
    my $zilog = Zilog::Z80->new ();
    my %stuff = (zilog => $zilog);
    print $jc->run (\%stuff), "\n";
    # Set up our object's method for printing JSON.
    $jc->obj (
        'Zilog::Z80' => \& Zilog::Z80::to_json,
    );
    print $jc->run (\%stuff), "\n";

produces output

    {"zilog":{"memory":"64 kbytes"}}
    {"zilog":"I can address as many as 64 kbytes of memory"}

(This example is included as examples/zilog.pl in the distribution.)

The function is called "in scalar context", so

    use JSON::Create;
    my $jc = JSON::Create->new ();
    $jc->validate (1);
    $jc->type_handler (sub {
                           return ('"a"', '"b"', '"c"');
                       });
    print $jc->run ({ x => *STDOUT }); 
    

produces output

    {"x":"c"}

(This example is included as examples/too-many-values.pl in the distribution.)

If you need to pass or return more than a single argument, use a closure:

    use JSON::Create;
    package My::Cool::Object;
    sub new { return bless {}; }
    sub serialize { return ('true', 'false'); };
    1;
    package main;
    my $object = My::Cool::Object->new ();
    my $jc = JSON::Create->new ();
    my ($arg1, $arg2);
    $jc->obj (
        'My::Cool::Object' => sub {
            my ($obj) = @_;
            my ($value1, $value2) = My::Cool::Object::serialize ($obj, $arg1, $arg2);
            return $value2;
        },
    );
    print $jc->run ({cool => $object});

produces output

    {"cool":false}

(This example is included as examples/closure.pl in the distribution.)

Exceptions (fatal errors) are not caught by JSON::Create, so if you want to halt the execution of JSON::Create, you can throw an exception within your callback.

    use JSON::Create;
    package Funky::Monkey::Baby; sub new {return bless {};} 1;
    package main;
    my $jc = JSON::Create->new ();
    $jc->obj (
        'Funky::Monkey::Baby' => sub {
            die "There is no such thing as a funky monkey baby";
        },
    );
    eval {
        $jc->run ({fmb => Funky::Monkey::Baby->new ()});
    };
    if ($@) {
        print "$@\n";
    }

produces output

    There is no such thing as a funky monkey baby at /usr/home/ben/projects/json-create/examples/exception.pl line 10.
    

(This example is included as examples/exception.pl in the distribution.)

If you prefer to take over all object handling yourself, there is also "obj_handler".

If your handler returns the undefined value, "run" prints a warning "Undefined value from user routine", halts further processing of the input, and returns the undefined value.

obj_handler

    $jc->obj_handler (\& my_obj_handler);

Supply an object handler. If you supply this, all objects will be handled by your handler. For example, you can replace all objects with 'null' or die if an object is found.

  • Calling convention

    The obj_handler routine is passed a single argument and is expected to return a single argument, the JSON to output. It is called in scalar context. In other words, the call looks like the following:

         $json = &{$jc->{obj_handler}} ($item);

    To pass or return multiple values via the obj_handler callback, use a closure. See the discussion at "obj" for an example.

  • Returning undef halts processing

    If your handler returns the undefined value, "run" prints a warning "Undefined value from user routine", halts further processing of the input, and returns the undefined value.

  • Delete the handler with any false value

    To remove the handler, simply call the function without an argument,

        $jc->obj_handler ();

    or with a false argument:

        $jc->obj_handler (0);

    The behaviour then reverts to the default.

  • Checking the output JSON

    The JSON output by your handler may be checked for validity by switching on validation using "validate". If you do not use this, and your return value happens to contain invalid UTF-8, you may see the error "Invalid UTF-8 from user routine". Please see "UTF-8 validation of user-supplied JSON" for more about this error.

  • Exception handling

    Exceptions (die) thrown within obj_handler callbacks are not caught by "run" but passed through to the parent. Please see the discussion at "obj" for an example.

Here is an example of handling various types of object with a user-supplied handler:

    use utf8;
    use FindBin '$Bin';
    use JSON::Create;
    package Monkey::Shines;
    sub new { return bless {}; }
    1;
    package Monkey::Shines::Bool;
    sub true { my $monkey = 1; return bless \$monkey; }
    sub false { my $monkey = 0; return bless \$monkey; }
    1;
    package main;
    my $monkeys = {
        CuriousGeorge => Monkey::Shines->new (),
        KingKong => Monkey::Shines::Bool->true (),
        FunkyKong => Monkey::Shines::Bool->false (),
        PeterTork => "Monkees",
    };
    my $obj_handler = sub {
        my ($obj) = @_;
        if (ref ($obj) =~ /bool/i) {
            return $$obj ? 'true' : 'false';
        }
        else {
            return 'null';
        }
    };
    my $jc = JSON::Create->new ();
    print $jc->run ($monkeys), "\n";
    $jc->obj_handler ($obj_handler);
    print $jc->run ($monkeys), "\n";
    $jc->obj_handler ();
    print $jc->run ($monkeys), "\n";

produces output

    {"CuriousGeorge":{},"PeterTork":"Monkees","KingKong":1,"FunkyKong":0}
    {"CuriousGeorge":null,"PeterTork":"Monkees","KingKong":true,"FunkyKong":false}
    {"CuriousGeorge":{},"PeterTork":"Monkees","KingKong":1,"FunkyKong":0}

(This example is included as examples/obj-handler.pl in the distribution.)

Here is an example of a "try harder" routine which does something like the JSON module does, by looking for methods on all objects:

    use utf8;
    use FindBin '$Bin';
    use JSON::Create 'create_json';
    use Mojo::URL;
    use Path::Tiny;
    
    sub try_harder
    {
        my ($obj) = @_;
        my $type = ref $obj;
        if ($obj->can ('TO_JSON')) {
            print "Jsonifying $type with 'TO_JSON'.\n";
            return create_json ($obj->TO_JSON ());
        }
        elsif ($obj->can ('to_string')) {
            print "Stringifying $type with 'to_string'.\n";
            return create_json ($obj->to_string ());
        }
        else {
            return create_json ($obj);
        }
    }
    
    my $jc = JSON::Create->new ();
    $jc->validate (1);
    $jc->obj_handler (\& try_harder);
    print $jc->run ({
        url => Mojo::URL->new('http://sri:foo@example.com:3000/foo?foo=bar#23'),
        path => path ('/home/ben/software/install/bin/perl'),
    }), "\n";
    
    

produces output

    Stringifying Mojo::URL with 'to_string'.
    Jsonifying Path::Tiny with 'TO_JSON'.
    {"url":"http://example.com:3000/foo?foo=bar#23","path":"/home/ben/software/install/bin/perl"}

(This example is included as examples/try-harder.pl in the distribution.)

This obj_handler overrides whatever you have set with "bool" or "obj". Currently, it does not print a warning about this. See "BUGS". The routine you use to handle objects may be the same as the routine you use to handle types. See "type_handler". For more details about the callbacks, see "obj".

This method was added in version 0.13 of the module.

replace_bad_utf8

    $jc->replace_bad_utf8 (1);

Replace invalid UTF-8 in the inputs with the Unicode replacement character U+FFFD, rather than produce the warning or error "Invalid UTF-8".

If replace_bad_utf8 is used on input containing only strings not marked as character strings, and bad UTF-8 is found, JSON::Create marks the output as a character string. Otherwise the replacement character itself is just a series of broken bytes. This behaviour can be altered with the method "downgrade_utf8".

This method was added in version 0.12 of the module.

set_fformat

    $jc->set_fformat ('%e');

This sets the printf-style format string used to print floating point numbers. This is validated and a warning printed if the format cannot be used. The format is also restricted to a maximum length to prevent buffer overflows within the module.

    use JSON::Create;
    my $jc = JSON::Create->new ();
    my @array = (1000000000.0,3.141592653589793238462643383279502884197169399375105820974944592307816406,0.000000001);
    print $jc->run (\@array), "\n";
    $jc->set_fformat ('%.3f');
    print $jc->run (\@array), "\n";
    $jc->set_fformat ('%E');
    print $jc->run (\@array), "\n";
    $jc->set_fformat ();
    print $jc->run (\@array), "\n";

produces output

    [1e+09,3.14159,1e-09]
    %.3f
    [1000000000.000,3.142,0.000]
    %E
    [1.000000E+09,3.141593E+00,1.000000E-09]
    [1e+09,3.14159,1e-09]

(This example is included as examples/set-fformat.pl in the distribution.)

This method was added in version 0.07 of the module.

type_handler

    $jc->type_handler (sub {return 'null'});

By default, when JSON::Create encounters a variable of a type which it doesn't know what to do with, such as a glob or code reference, it prints a warning and returns an undefined value. See "Code, regexes, and other references". The method type_handler sets up a callback which is called when a variable of an unhandled type is found in the input. For example, to put the JSON literal null in the output when a reference to a variable of an unhandled type is encountered, rather than print an error, the above example will do it.

  • Calling convention

    The type_handler routine is passed a single argument and is expected to return a single argument, the JSON to output. It is called in scalar context. In other words, the call looks like the following:

         $json = &{$jc->{type_handler}} ($item);

    To pass or return multiple values via the type_handler callback, use a closure. See the discussion at "obj" for an example.

  • Returning undef halts processing

    If your handler returns the undefined value, "run" prints a warning "Undefined value from user routine", halts further processing of the input, and returns the undefined value.

  • Delete the handler with any false value

    To remove the handler, simply call the function without an argument,

        $jc->type_handler ();

    or with a false argument:

        $jc->type_handler (0);

    The behaviour then reverts to the default.

  • Checking the output JSON

    The JSON output by your handler may be checked for validity by switching on validation using "validate". If you do not use this, and your return value happens to contain invalid UTF-8, you may see the error "Invalid UTF-8 from user routine". Please see "UTF-8 validation of user-supplied JSON" for more about this error.

  • Exception handling

    Exceptions (die) thrown within type_handler callbacks are not caught by "run" but passed through to the parent. Please see the discussion at "obj" for an example.

The following example shows a few possibilities for handling types:

    use utf8;
    use FindBin '$Bin';
    use JSON::Create 'create_json';
    my %crazyhash = (
        'code' => sub { return "강남스타일"; },
        'regex' => qr/.*/,
        'glob' => *STDOUT,
    );
    my $jc = JSON::Create->new ();
    # Let's validate the output of the subroutine below.
    $jc->validate (1);
    # Try this one weird old trick to convert your Perl type.
    $jc->type_handler (
        sub {
            my ($thing) = @_;
            my $value;
            my $type = ref ($thing);
            if ($type eq 'CODE') {
                $value = &$thing;
            }
            else {
                $value = "$thing";
            }
            return create_json ({ type => $type, value => $value, });
        }
    );
    print $jc->run (\%crazyhash);

produces output

    Wide character in print at /usr/home/ben/projects/json-create/examples/type-handler.pl line 30.
    {"code":{"value":"강남스타일","type":"CODE"},"glob":{"type":"GLOB","value":"GLOB(0x28806d80)"},"regex":{"type":"Regexp","value":"(?^:.*)"}}

(This example is included as examples/type-handler.pl in the distribution.)

If the "strict" option is chosen, this method is also passed scalar references.

    use JSON::Create;
    my $jc = JSON::Create->new ();
    $jc->strict (1);
    print $jc->run (\1), "\n";
    $jc->type_handler (sub {
                           my ($thing) = @_;
                           if (ref $thing eq 'SCALAR') {
                               return $$thing;
                           }
                       });
    print $jc->run (\1), "\n";
    

produces output

    Input's type cannot be serialized to JSON at /usr/home/ben/projects/json-create/examples/type-handler-scalar.pl line 7.
    Use of uninitialized value in print at /usr/home/ben/projects/json-create/examples/type-handler-scalar.pl line 7.
    
    1

(This example is included as examples/type-handler-scalar.pl in the distribution.)

This method was added in version 0.10 of the module.

unicode_escape_all

    $jc->unicode_escape_all (1);

Call this with a true value to make all Unicode characters be escaped into the \u3000 format. A false value switches that off again.

For example,

    use JSON::Create;
    use utf8;
    my $jc = JSON::Create->new ();
    my $in = '赤ブöAↂϪ';
    print $jc->run ($in), "\n";
    $jc->unicode_escape_all (1);
    print $jc->run ($in), "\n";
    $jc->unicode_upper (1);
    print $jc->run ($in), "\n";
    $jc->unicode_escape_all (0);
    print $jc->run ($in), "\n";

produces output

    "赤ブöAↂϪ"
    "\u8d64\u30d6\u00f6\uff21\u2182\u03ea"
    "\u8D64\u30D6\u00F6\uFF21\u2182\u03EA"
    "赤ブöAↂϪ"

(This example is included as examples/escape-all.pl in the distribution.)

Note that JSON::Create contains its own UTF-8 validation, and this escaping is applied regardless of whether Perl marks the bytes as "utf8" or not:

    use JSON::Create;
    no utf8;
    my $jc = JSON::Create->new ();
    my $in = '赤ブöAↂϪ';
    print $jc->run ($in), "\n";
    $jc->unicode_escape_all (1);
    print $jc->run ($in), "\n";
    $jc->unicode_upper (1);
    print $jc->run ($in), "\n";
    $jc->unicode_escape_all (0);
    print $jc->run ($in), "\n";

produces output

    "赤ブöAↂϪ"
    "\u8d64\u30d6\u00f6\uff21\u2182\u03ea"
    "\u8D64\u30D6\u00F6\uFF21\u2182\u03EA"
    "赤ブöAↂϪ"

(This example is included as examples/escape-all-no-utf8.pl in the distribution.)

See also "Input strings must be UTF-8".

unicode_upper

    $jc->unicode_upper (1);

Call this with a true value to make Unicode escapes use upper case letters in the hexadecimal. See the example under "unicode_escape_all".

validate

    $jc->validate (1);

If this is called with a true value, JSON::Create validates the user-generated JSON given by the callbacks registered with "obj", "type_handler", "obj_handler" and "non_finite_handler". The validation is done via the routine assert_valid_json of JSON::Parse, so that module must be installed, otherwise the call to validate will fail. This also validates that the return value contains only valid UTF-8.

If JSON::Parse is installed, and the JSON fails to validate, a warning will be produced containing the invalid JSON string and the error produced by assert_valid_json, and the return value will be undefined.

This method was added in version 0.07 of the module.

EXPORTS

The module exports nothing except by request. Two functions, "create_json" and "create_json_strict", are exported on request. There is also an export tag all if you require both functions:

    use JSON::Create ':all';

INSTALLATION

The module uses C internally, so you need a C compiler to install it. If the compiled library cannot be loaded, there is also a backup "pure Perl" module JSON::Create::PP in the distribution.

UNICODE HANDLING

This section details JSON::Create's handling of Unicode within strings. This involves the distinction between two things with confusingly similar names, Perl character strings, utf8, and the Unicode encoding UTF-8.

UTF-8 only

JSON::Create only consumes and produces the UTF-8 encoding of Unicode. If you need a different encoding, please use the Encode module to encode the output.

Input strings must be UTF-8

All strings within the input must be UTF-8 encoded. This does not mean that the strings must be Perl character strings (Perl's utf8), it means that input strings must be valid UTF-8. Input strings can be either Perl character strings or bytes, but in either case the bytes of the string must be valid UTF-8.

To illustrate this, examine the following example:

    use JSON::Create 'create_json';
    use utf8;
    $| = 1;
    print create_json ('赤ブöAↂϪ'), "\n";
    no utf8;
    binmode STDOUT, ":raw";
    print create_json ('赤ブöAↂϪ'), "\n";
    print create_json ("\x99\xff\x10"), "\n";

produces output

    "赤ブöAↂϪ"
    "赤ブöAↂϪ"
    Invalid UTF-8 at /usr/home/ben/projects/json-create/examples/valid-chars.pl line 12.
    Use of uninitialized value in print at /usr/home/ben/projects/json-create/examples/valid-chars.pl line 12.
    

(This example is included as examples/valid-chars.pl in the distribution.)

The point here is that the UTF-8 validation is carried out regardless of whether Perl thinks that the input string is "utf8". The string in the third call to "create_json" is not marked as utf8 by Perl but still fails as invalid UTF-8.

JSON::Create's insistence on UTF-8 within input strings is related to "Unicode upgrades are not done according to Perl conventions".

Output is valid UTF-8

All of the output of either the function "create_json" or the method "run" is valid UTF-8. This does not mean that output strings are marked as Perl character strings (utf8), it means that the output has been validated as UTF-8.

There is one exception to this. In the case of user-generated JSON returned by "obj", "obj_handler", "type_handler" and "non_finite_handler", the parts of the output consisting of return values from user routines may be non-UTF-8-compliant if the user has not switched on validation with "validate", and there are no character strings (utf8) anywhere in the input. However, if there are any Perl character strings (utf8) anywhere in the input, and the user has not chosen "downgrade_utf8", JSON::Create validates the entire output as UTF-8, as described in "UTF-8 validation of user-supplied JSON".

Unicode upgrades are not done according to Perl conventions

If a single string anywhere in the input is a Perl character string, in other words marked as utf8, the entire output string is marked as a Perl character string, utf8, without altering the non-utf8 bytes. This is at odds with Perl conventions. Perl says that non-utf8 strings actually consist of characters with symbols from 0 to 255 which "coincidentally" fit into one byte, and utf8 strings actually consist of characters with values from 0 to 0x10FFFF, not bytes, and when combining the two, it is illegal to treat either of these as bytes, but instead they must both be treated as numbers. To illustrate this, here is the behaviour of JSON contrasted with JSON::Create:

    use utf8;
    use FindBin '$Bin';
    use JSON;
    use JSON::Create 'create_json';
    no utf8;
    my $x = 'かきくけこ';
    use utf8;
    my $y = 'さしすせそ';
    my $v = {x => $x, y => $y};
    print to_json ($v), "\n";
    print create_json ($v), "\n";

produces output

    {"x":"かきくけこ","y":"さしすせそ"}
    {"x":"かきくけこ","y":"さしすせそ"}

(This example is included as examples/json-unicode.pl in the distribution.)

The Perl convention is that if a non-utf8 string and a utf8 string are combined, they should be combined as JSON does it, by treating each byte of the non-utf8 string as if it is a single Unicode code point, and writing equivalent UTF-8 bytes for that code point into the output. JSON::Create does a different thing, which is to insist that all input strings must be valid UTF-8, and after validating them, it combines them with the utf8 strings without altering their contents. This break with the Perl convention is by design.

This example illustrates what happens with non-UTF-8 bytes:

    use JSON;
    use JSON::Create 'create_json';
    use Gzip::Faster;
    $|=1;
    # Generate some random garbage bytes
    my $x = gzip ('かきくけこ');
    use utf8;
    my $y = 'さしすせそ';
    my $v = {x => $x, y => $y};
    print to_json ($v), "\n";
    print create_json ($v), "\n";

produces output

    {"y":"さしすせそ","x":"\u001f‹\b\u0000\u0000\u0000\u0000\u0000\u0000\u0003{ÜØý¸±÷qcÿãƉ\u001b'\u0003\u0000£ ³\u0012\u000f\u0000\u0000\u0000"}
    Invalid UTF-8 at /usr/home/ben/projects/json-create/examples/json-unicode-gzip-bytes.pl line 15.
    Use of uninitialized value in print at /usr/home/ben/projects/json-create/examples/json-unicode-gzip-bytes.pl line 15.
    

(This example is included as examples/json-unicode-gzip-bytes.pl in the distribution.)

Using replace_bad_utf8 may cause a utf8 upgrade

Please see the discussion under "replace_bad_utf8".

UTF-8 validation of user-supplied JSON

If you supply JSON via a user routine such as "obj_handler", and you choose not to validate your output with "validate", and the input contains a character string (utf8), and you do not choose "downgrade_utf8", the entire output string has to be validated as UTF-8, to prevent a loophole where a string containing non-UTF-8 compliant bytes could get upgraded to a character string (utf8).

In this case, if invalid UTF-8 is detected, the diagnostic "Invalid UTF-8 from user routine" is printed, and the undefined value returned. Since the check is applied to the final output JSON, there is no information about which routine was at fault, so to get a more specific diagnosis, please switch on "validate".

Here is an example of how this may occur:

    use JSON::Create;
    my $jc = JSON::Create->new ();
    # This type handler returns a non-UTF-8 string.
    $jc->type_handler (sub {return '"'. pack ("CCC", 0x99, 0x10, 0x0) . '"';});
    use utf8;
    # sub {1} triggers the type handler for a code reference, and the ぶー
    # contains a "utf8" flag, so this combination sets off the problem.
    print $jc->run ({a => sub {1}, b => 'ぶー'});

produces output

    Invalid UTF-8 from user routine at /usr/home/ben/projects/json-create/examples/user-bad-utf8.pl line 11.
    Use of uninitialized value in print at /usr/home/ben/projects/json-create/examples/user-bad-utf8.pl line 11.

(This example is included as examples/user-bad-utf8.pl in the distribution.)

DIAGNOSTICS

All diagnostics are warnings by default. This behaviour can be altered with the method "fatal_errors".

Input's type cannot be serialized to JSON

(Warning) A reference type such as a code reference, regexp, or glob was found in the user's input. For a discussion, see "Code, regexes, and other references". For how to overcome this, see "type_handler".

Invalid UTF-8

(Warning) Bytes in a Perl string were not valid UTF-8. This behaviour can be altered with the method "replace_bad_utf8".

Invalid UTF-8 from user routine

(Warning) A return value from a user routine was not valid UTF-8. See "UTF-8 validation of user-supplied JSON".

This diagnostic and the corresponding validation of user-supplied JSON was added in version 0.19 of the module.

JSON::Parse::assert_valid_json failed

(Warning) The user requested validation with "validate" and this failed.

Non-ASCII byte in non-utf8 string

(Warning) The user tried to encode a string containing a non-ASCII byte in a non-utf8 string. This diagnostic occurs with either "create_json_strict" or "strict".

This diagnostic was added in version 0.20 of the module together with "create_json_strict" and the "strict" method.

Non-finite number in input

(Warning) A number which cannot be represented as a floating point number was found in the input. See "Floating point numbers".

This diagnostic was added in version 0.20 of the module together with "create_json_strict" and the "strict" method.

Object cannot be serialized to JSON

(Warning) An object in the input could not be serialized to JSON. See "Objects" for a discussion.

This diagnostic was added in version 0.20 of the module together with "create_json_strict" and the "strict" method.

Undefined value from user routine

(Warning) An undefined value was returned by a user routine set with either "obj", "obj_handler", "type_handler" or "non_finite_handler".

PERFORMANCE

This module is fairly new on the scene, so speed is not a key issue until the module is demonstrably producing correct outputs.

However, due to user interest, there is a benchmarking script in bench/bench.pl which compares the performance of the module with JSON::XS and Cpanel::JSON::XS. Outputs look like this, where the "improve" column is the improvement in speed of the fastest module compared to the slowest:

    Versions used:
    +-----+------------------+---------+
    | CJX | Cpanel::JSON::XS | 3.0211  |
    +-----+------------------+---------+
    | JX  | JSON::XS         | 3.01    |
    +-----+------------------+---------+
    | JC  | JSON::Create     | 0.19_01 |
    +-----+------------------+---------+
    Comparing hash of ASCII strings...
    
    Repetitions: 1000 x 200 = 200000
    +--------+--------+------------+---------+
    | Module | 1/min  | min        | improve |
    +--------+--------+------------+---------+
    | CJX    | 399838 | 0.00250101 | 1       |
    | JC     | 659689 | 0.00151587 | 1.64989 |
    | JX     | 420524 | 0.00237799 | 1.05173 |
    +--------+--------+------------+---------+
    
    Comparing hash of integers...
    
    Repetitions: 1000 x 200 = 200000
    +--------+--------+------------+---------+
    | Module | 1/min  | min        | improve |
    +--------+--------+------------+---------+
    | CJX    | 170210 | 0.00587511 | 1       |
    | JC     | 361921 | 0.00276303 | 2.12633 |
    | JX     | 172477 | 0.00579786 | 1.01332 |
    +--------+--------+------------+---------+
    
    Comparing hash of Unicode strings...
    
    Repetitions: 1000 x 200 = 200000
    +--------+--------+------------+---------+
    | Module | 1/min  | min        | improve |
    +--------+--------+------------+---------+
    | CJX    | 521096 | 0.00191903 | 1       |
    | JC     | 971804 | 0.00102901 | 1.86492 |
    | JX     | 524419 | 0.00190687 | 1.00638 |
    +--------+--------+------------+---------+
    
    Comparing array of floats...
    
    Repetitions: 1000 x 200 = 200000
    +--------+--------+------------+---------+
    | Module | 1/min  | min        | improve |
    +--------+--------+------------+---------+
    | CJX    | 146286 | 0.00683594 | 1.00338 |
    | JC     | 369022 | 0.00270987 | 2.53115 |
    | JX     | 145792 | 0.00685906 | 1       |
    +--------+--------+------------+---------+
    
    Comparing array of ASCII strings...
    
    Repetitions: 1000 x 200 = 200000
    +--------+--------+------------+---------+
    | Module | 1/min  | min        | improve |
    +--------+--------+------------+---------+
    | CJX    | 319785 | 0.0031271  | 1       |
    | JC     | 476193 | 0.00209999 | 1.4891  |
    | JX     | 333888 | 0.00299501 | 1.0441  |
    +--------+--------+------------+---------+

This only compares a few simple cases in which the output is demonstrably correct. Also, at least on my computer, the numbers seem to vary wildly from one test to another. The benchmarking script also contains commented out code to test against JSON::DWIW if you are interested.

Please note that the numbers for the floating point results are at the moment unfairly skewed in favour of JSON::Create, because whereas JSON::XS prints out the full available precision of the number (which is about equivalent to printing with a format %.15g), JSON::Create prints only six digits of precision, creating a false impression of greater speed. Work on this is currently in progress.

BUGS

There is currently no facility to add whitespace to the output JSON.

There is currently no way to delete object handlers set via "obj" from a JSON::Create object.

There are a few remaining undecided issues around the default object serialization.

No warning is printed when the user uses clashing methods like "bool" and "obj_handler".

Features added since version 0.16 of the module are not yet implemented in JSON::Create::PP.

There is a bug in JSON::Create::PP's handling of non-integer numbers.

The floating point printing loses precision on round trips.

HISTORY

This module is currently under development and may undergo backwards-incompatible changes.

"set_fformat" was added in version 0.07.

"validate" was added in version 0.07.

"fatal_errors" was added in version 0.10.

"replace_bad_utf8" was added in version 0.12.

"obj_handler" was added in version 0.13. This version also added loading of the Pure-Perl version of the module, JSON::Create::PP, if the loading of JSON::Create failed.

"non_finite_handler" was added in version 0.17.

"downgrade_utf8" was added in version 0.18.

The "Invalid UTF-8 from user routine" diagnostic was added in version 0.19.

The "create_json_strict" function and "strict" methods and associated diagnostics were added in version 0.20.

SEE ALSO

RFC 7159

JSON is specified in RFC 7159 "The application/json Media Type for JavaScript Object Notation (JSON)".

json.org

http://json.org is the website for JSON, authored by Douglas Crockford.

JSON::Parse

JSON::Create is a companion module to the same author's JSON::Parse. Please see that module's documentation for a summary of JSON modules on CPAN (under "SEE ALSO").

JSON::Create::PP

This is a backup module for JSON::Create in pure Perl.

Hazy::Cosmic::Jive

This module contains the in-development floating point handling for JSON::Create.

AUTHOR

Ben Bullock, <bkb@cpan.org>

COPYRIGHT & LICENCE

This package and associated files are copyright (C) 2015-2017 Ben Bullock.

You can use, copy, modify and redistribute this package and associated files under the Perl Artistic Licence or the GNU General Public Licence.