NAME

JSON::Create - turn a Perl variable into JSON

SYNOPSIS

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

produces

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

DESCRIPTION

This module produces JSON out of Perl. It offers a simple default function "create_json" which converts Perl into JSON using common defaults, and a customizable object created with "new" where the user can specify a variety of behaviours. For example, Unicode escaping into ASCII via "unicode_escape_all", floating point number formatting via "set_fformat", escaping of slashes (/) with "escape_slash", JSON serialization of objects with "obj", or boolean handling with "bool".

In all cases, errors in processing result in a warning and an undefined return value. This behaviour can be altered with the method "fatal_errors".

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

EXAMPLES

The example programs in this documentation are all found in the directory examples/ in the distribution. All the outputs below are taken from the actual outputs of the example programs.

FUNCTIONS

create_json

my $json = create_json (\%hash);

Convert a Perl hash reference or array reference into JSON. The return value is the output JSON.

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

CONVERSIONS

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

Hashes

Associative arrays are converted to 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);

outputs

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

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);

outputs

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

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);

outputs

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

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);

outputs

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

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);

outputs

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

Scalars

Perl scalars are converted to strings, JSON integers or JSON floats.

Strings

As far as possible, strings are written as-is to the JSON. All output is checked for Unicode validity. Some whitespace and control characters must be escaped for the output to be valid JSON. (See "RFC 7159".)

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 \".

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

outputs

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

U+2028 and U+2029 (JavaScript clashes)

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

Although it is not required by the JSON standard, "create_json" 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.

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"

NaN (not a number) values are converted to "nan" (the letters nan surrounded by double quotes). Positive and negative infinity are converted to "inf" and "-inf" respectively. 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.

The undefined value

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

Booleans

Booleans (true and false) from input via JSON::Parse version 0.37 or later will be turned into true and false. Perl objects can be converted to booleans using the method "bool" (see below).

Other types

Objects

Perl objects (blessed references) are dereferenced and then handed as if non-object types. This behaviour can be altered with the method "obj".

Code, regexes, and other references

A code or other reference (regexes, globs, etc.) in the input prints a warning and causes the entire return value to be the undefined value. This behaviour can be altered with the method "type_handler".

METHODS

To alter the format of the output from the defaults of "create_json", 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. You need to use "run" to generate JSON with this.

run

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

This does exactly the same thing as "create_json", unless you have altered the output format. 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

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

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.

bool

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

The argument is a list of object names. 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";

outputs

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

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.25.

The current version of Mojo::JSON (Mojolicious version 6.25) 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 tests for Mojo::JSON compatibility are currently commented out by default, because different versions of Mojolicious differ a lot in not only function names but also variable names, as seen above. If you want to run these tests, please edit the file t/bool.t in the distribution.

You can also switch on multiple modules using a list:

$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.

type_handler

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

By default, when JSON::Create encounters a type such as a glob or code reference which it doesn't know what to do with, 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 an unhandled type is found in the input. For example, if you want to put the JSON literal null in the output when you find an unhandled type rather than print an error, the above example will do it.

Here is an example showing a few possibilities:

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);

outputs

{"regex":{"value":"(?^:.*)","type":"Regexp"},"code":{"type":"CODE","value":"강남스타일"},"glob":{"type":"GLOB","value":"GLOB(0x28806d80)"}}

The type handler function you supply will be called with one value, the reference it is supposed to handle, and should return one value, a valid JSON string. If you want to have your return value validated, use the "validate" option (see below).

Type handler callbacks are handled in the same way that object callbacks are handled, so please see the discussion at "obj" for how to handle errors and how to deal with routines requiring multiple values.

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";

outputs

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

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.

binmode STDOUT, ":utf8";
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

["\u2028"]
["
"]

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

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

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

The function is called "in scalar context", so

use JSON::Create;
my $jc = JSON::Create->new ();
$jc->type_handler (sub {
                       return (1, 2, 3);
                   });
print $jc->run ({ x => *STDOUT }); 

produces

{"x":3}

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

{"cool":false}

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

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

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";

outputs

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

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.

use JSON::Create;
use utf8;
binmode STDOUT, ":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";

outputs

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

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.

unicode_upper

$jc->unicode_upper (1);

Call this with a true value to make Unicode escapes use upper case 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" and "type_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.

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

EXPORTS

The module exports nothing except by request. One function, "create_json", is exported on request.

INSTALLATION

The module uses C internally, so you need a C compiler to install it. There is also an undocumented "pure Perl" module JSON::Create::PP in the distribution, which could be used in a pinch. (This is basically a reference module though.)

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

This module is a companion module to the same author's JSON::Parse. Please see that module's documentation for links to the JSON standard itself, a summary of the ever-growing list of JSON modules on CPAN (under "SEE ALSO") and the reasons why I decided to write these two modules (under "HISTORY").

DIAGNOSTICS

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

Invalid UTF-8

(Warning) Bytes in a Perl string were not valid UTF-8.

JSON::Parse::assert_valid_json failed

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

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. See "type_handler".

BUGS

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

There is currently no way to delete object handlers from a JSON::Create object.

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

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:

Versions used:

CJX	Cpanel::JSON::XS	3.0115
JX	JSON::XS	3.01
JC	JSON::Create	0.09

Comparing hash of ASCII strings...

      Rate  CJX   JX   JC
CJX 40.6/s   --  -2% -33%
JX  41.6/s   2%   -- -31%
JC  60.7/s  49%  46%   --

Comparing hash of integers...

      Rate   JX  CJX   JC
JX  16.2/s   --  -1% -54%
CJX 16.3/s   1%   -- -53%
JC  35.1/s 116% 115%   --

Comparing hash of Unicode strings...

      Rate  CJX   JX   JC
CJX 47.9/s   --  -1% -45%
JX  48.3/s   1%   -- -45%
JC  87.1/s  82%  80%   --

Comparing array of floats...

      Rate  CJX   JX   JC
CJX 14.0/s   --  -1% -60%
JX  14.1/s   1%   -- -59%
JC  34.7/s 147% 146%   --

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.

HISTORY

This module, and "JSON::Parse", and incidentally some other modules I've publicly released, such as "Gzip::Faster", arose partly out of dissatisfaction with the design of the existing CPAN modules. My feeling is that modules should prominently offer a simple interface to catch the most common cases, and then complexity should be added only when it's actually needed. In the case of JSON::Create, "create_json" offers a simple "don't make me think" interface which covers the most common case, and uses what are hopefully sensible defaults, and "new" and the methods like "unicode_escape_all" offer wider choices to the user who actually needs them.

As far as my own use is concerned, the module is currently in use everywhere that I need to generate JSON within Perl. With this and JSON::Parse, I do not use JSON, JSON::XS or any of the other JSON modules on CPAN any more. However, I would suggest caution in switching to this module from the more established JSON solutions on CPAN, since this module is relatively new, and while most parts are implemented, there are some corner-cases and untested code. I'm also changing the design somewhat as I go along without worrying too much about backwards compatibility.

The exact time I've spent making this module is recorded in the file timesheet.txt in the top directory of the distribution. At the time of version 0.10, I've spent 1 day, 6 hours and 41 minutes working on this.

AUTHOR

Ben Bullock, <bkb@cpan.org>

COPYRIGHT & LICENCE

This package and associated files are copyright (C) 2015 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.

TERMINOLOGY

This defines the terminology used in this document.

Convenience function

In this document, a "convenience function" indicates a function which solves some of the problems, some of the time, for some of the people, but which may not be good enough for all envisaged uses. A convenience function is an 80/20 solution, something which solves (about) 80% of the problems with 20% of the effort. Something which does the obvious things, but may not do all the things you might want, a time-saver for the most basic usage cases.

BUGS

In this document, the section BUGS describes possible deficiencies, problems, and workarounds with the module. It's not a guide to bug reporting, or even a list of actual bugs. The name "BUGS" is the traditional name for this sort of section in a Unix manual page.