JSON::Create(3)

πŸ“¦
version 0.36 Β· 4 versions
, in fedora 43

okay bestie, welcome to JSON::Create(3) β€” the manual page that's about to be your whole personality πŸ’…

🌸 NAME 🌸

JSON::Create β€” literally just Create JSON. That's the vibe. ✨

πŸ“œ SYNOPSIS πŸ“œ

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

produces output

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

(This example is included as examples/synopsis.pl <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/synopsis.pl> in the distribution.)

πŸ“¦ VERSION πŸ“¦

This doc describes JSON::Create version 0.36, which corresponds to git commit c6e87e42a72ce49bbb1fa92712fcbae22df2b592 <https://github.com/benkasminbullock/json-create/commit/c6e87e42a72ce49bbb1fa92712fcbae22df2b592> at Tue Apr 7 05:50:22 2026 +0900. ✨

πŸ¦‹ DESCRIPTION πŸ¦‹

JSON::Create encodes Perl variables into JSON. The basic routine create_json gives common defaults. The stricter version create_json_strict accepts only unambiguous inputs. For more customization, an object created with new and run with create allows specifying behaviour in more detail.

JSON::Create handles no string encoding except UTF-8. It supports serialization of objects via user-defined callbacks. Its companion module JSON::Parse parses JSON into Perl.

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

✨ FUNCTIONS ✨

create_json

my $json = create_json (\%hash, %args);

This converts a hash reference, array reference, or scalar into JSON. The return value is the output JSON as a string. The arguments available in %args are the same as new and set.

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

create_json_strict

my $json = create_json_strict (\%hash, %args);

This is the same as create_json, except that it rejects ambiguous inputs. See strict for details. It is effectively identical to

my $json = create_json (\%hash, %args, strict => 1);

The strict option can be set in %args without producing an error, but it will be overridden.

This function was added in version 0.20 of the module.

write_json

 write_json ('file.json', \%hash, %options);

Write the contents of %hash (or an array reference or scalar) to the file specified in the first argument. This takes all the same arguments as create_json, new and set.

This function was added in version 0.30 of the module.

πŸ’– 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 create.

create

my $json = $jc->create ($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 <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/fatal-errors.pl> in the distribution.)

This method was added in version 0.10 of the module.

new

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

Create a new JSON::Create object. Use create to generate JSON with it. It is possible to supply arguments with new:

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

All of the same values as set may be used.

set

$json->set (strict => 1, fatal_errors => 1, validate => 1);
  • downgrade_utf8 option β€” This corresponds to downgrade_utf8.
  • escape_slash option β€” This corresponds to escape_slash.
  • fatal_errors option β€” This corresponds to fatal_errors.
  • indent option β€” This corresponds to indent.
  • no_javascript_safe option β€” This corresponds to no_javascript_safe.
  • non_finite_handler option β€” This corresponds to non_finite_handler.
  • object_handler option β€” This corresponds to object_handler.
  • replace_bad_utf8 option β€” This corresponds to replace_bad_utf8.
  • sort option β€” This corresponds to sort.
  • strict option β€” This corresponds to strict.
  • type_handler option β€” This corresponds to type_handler.
  • unicode_escape_all option β€” This corresponds to unicode_escape_all.
  • unicode_upper option β€” This corresponds to unicode_upper.
  • validate option β€” This corresponds to validate.

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

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.

Converting booleans to "true" and "false"

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 <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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 8.65.

The current version of Mojo::JSON (Mojolicious version 8.65) uses "JSON::PP::Boolean" for 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.

cmp

Set a user-defined routine to be used with the sort option.

Sorting the keys of objects in a user-defined order will reduce the performance of the module. The built-in variables $a and $b are not available to your sort routine, so you need to get the arguments yourself.

Sorting object elements case-independently

use utf8;
use FindBin '$Bin';
use JSON::Create;
my $jc = JSON::Create->new (sort => 1, indent => 1);
$jc->cmp (\&uccmp);
my %emojis = (
    lifeforms => {
        Kingkong => '🦍',
        goat => '🐐',
        elephant => '🐘',
        Grape => 'πŸ‡',
        Watermelon => 'πŸ‰',
        melon => '🍈',
        # What if life exists based on another element? πŸ––
        siliconbased => '❄',
    },
);
print $jc->run (\%emojis);

sub uccmp
{
    my ($a, $b) = @_;
    return uc ($a) cmp uc ($b);
}

produces output

{
        "lifeforms":{
                "elephant":"🐘",
                "goat":"🐐",
                "Grape":"πŸ‡",
                "Kingkong":"🦍",
                "melon":"🍈",
                "siliconbased":"❄",
                "Watermelon":"πŸ‰"
        }
}

(This example is included as examples/cmp.pl <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/cmp.pl> in the distribution.)

This method was added in version 0.29 of the module.

downgrade_utf8

$jc->downgrade_utf8 (1);

If this is set to a true value, the return value of create_json or create 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 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 spec, bestie, they're fancy like that) get escaped with a backslash, so "/" gets turned into "\/". Call it with any false value to make the slash not be escaped β€” that's the default behaviour. πŸŽ€

Escaping slashes

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 <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/escape-slash.pl> in the distribution.)

See also "Other escapes".

This method was added in version 0.07 of the module. πŸ¦‹

indent

$jc->indent (1);

Add whitespace indentation to the output. The formula it applies: add a newline plus indentation after every opening bracket, add the same after every comma, and add the same before every closing bracket. Tabs do all the indenting. How many tabs? However many brackets are currently open, bestie. Simple math, iconic results. πŸ’…

Example of indentation

use JSON::Create;
my %thing = ("it's your thing" => [qw! do what you !, {wanna => 'do'}],
             "I can't tell you" => [{who => 2}, qw! sock it!, 2]);
my $jc = JSON::Create->new ();
$jc->indent (1);
print $jc->run (\%thing);

produces output 🌸

{
    "it's your thing":[
        "do",
        "what",
        "you",
        {
            "wanna":"do"
        }
    ],
    "I can't tell you":[
        {
            "who":2
        },
        "sock",
        "it",
        2
    ]
}

(This example is included as examples/indent.pl <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/indent.pl> in the distribution.)

Reformatting the indentation

If you prefer a different indentation style, you can totally reshape this output to your needs with simple substitutions. For example, s/^(\t+)/ " " x length ($1) /gesm; converts from tabs to two-space indentation. Slay. 🫢

use JSON::Create;
my %thing = ("it's your thing" => [qw! do what you !, {wanna => 'do'}],
             "I can't tell you" => [{who => 2}, qw! sock it!, 2]);
my $jc = JSON::Create->new ();
$jc->indent (1);
my $out = $jc->run (\%thing);
$out =~ s/^(\t+)/ "  " x length ($1) /gesm;
print $out;

produces output ✨

{
  "I can't tell you":[
    {
      "who":2
    },
    "sock",
    "it",
    2
  ],
  "it's your thing":[
    "do",
    "what",
    "you",
    {
      "wanna":"do"
    }
  ]
}

(This example is included as examples/indent-format.pl <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/indent-format.pl> in the distribution.)

This method was added in version 0.27 of the module. πŸ’–

no_javascript_safe

$jc->no_javascript_safe (1);

Call it with a true value and it switches off JavaScript protection in the output JSON. Call it with a false value and the JavaScript protection comes right back on. Toggle city. πŸ”₯

Switching JavaScript-safety on and off

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 <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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 of your very own. The default behaviour of this module is described at "Floating point numbers".

Your routine handler gets the non-finite number as its sole argument, and it returns one argument: the output JSON. That's the whole deal. 🌈

Using null for infinity and NaN

To always use null in place of the default, supply a function like this:

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 <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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 this:

$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, create prints a warning Undefined value from user routine, halts further processing of the input, and returns the undefined value. Not the vibe, bestie, but forewarned is forearmed. 😭

Delete the handler with any false value

To remove the handler, just 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. Back to factory settings, periodt. πŸŽ€

Checking the output JSON

The JSON output by your handler may be checked for validity by switching on validation using "validate". If you don't do 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 create β€” they pass straight 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've supplied. Main character energy for your objects. πŸ‘‘

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. If you want 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 <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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 <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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 <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/closure.pl> in the distribution.)

Throwing an exception in your callback

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 right inside your callback. Your callback, your rules, bestie. πŸ’…

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

and it spits out πŸ’€

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

(yes bestie, this whole thing ships as examples/exception.pl β€” you can find it at https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/exception.pl in the distro.)

If you're the type who wants to handle every single object yourself, no delegating, no sharing β€” there's obj_handler for that. πŸ‘‘

And real talk: if your handler returns the undefined value, create will print a warning that says Undefined value from user routine, halt all further processing of the input, and hand you back the undefined value. Not a vibe, but forewarned is forearmed. ✨

🌈 obj_handler 🌈

$jc->obj_handler (\& my_obj_handler);

You hand it an object handler, and boom β€” every object goes through your routine. Want to replace all objects with 'null'? Do it. Want to die the second an object shows up? Also do it. Your world, your rules. πŸ’…

  • Calling convention β€” your obj_handler routine gets exactly one argument and is expected to give exactly one back: the JSON to output. It gets called in scalar context, so like literally the call looks like this:
$json = &{$jc->{obj_handler}} ($item);

Wanna pass or return multiple values through the obj_handler callback? Use a closure, bestie. There's a whole example waiting for you over in obj.

  • Returning undef halts processing β€” if your handler returns the undefined value, create prints the warning Undefined value from user routine, halts further processing of the input, and returns the undefined value. So like, don't do that unless you mean it. 😭
  • Delete the handler with any false value β€” wanna yeet the handler? Just call the function with no argument:
$jc->obj_handler ();

or with a false one:

$jc->obj_handler (0);

and everything goes back to the default behaviour. So chill. 🧚

  • Checking the output JSON β€” the JSON your handler spits out can be validity-checked if you flip on validation with validate. If you don't do that and your return value happens to have invalid UTF-8 in it, you might run into the error Invalid UTF-8 from user routine. Full lore on that one lives at UTF-8 validation of user-supplied JSON.
  • Exception handling β€” exceptions (die) thrown inside obj_handler callbacks are not caught by create. They get passed straight up to the parent. There's an example over at obj if you want the receipts.

πŸŽ€ Turning various types of object into JSON πŸŽ€

Okay so here's the whole vibe β€” handling a bunch of different object types 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 (indent => 1, sort => 1);
print $jc->run ($monkeys), "\n";
$jc->obj_handler ($obj_handler);
print $jc->run ($monkeys), "\n";
$jc->obj_handler ();
print $jc->run ($monkeys), "\n";

and it produces:

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

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

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

(this one lives in the distro as examples/obj-handler.pl β€” https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/obj-handler.pl if you wanna peek. πŸ¦‹)

✨ Turning everything into JSON ✨

And here's a "try harder" routine that kinda does what the JSON module does β€” it goes looking for methods on all the 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";
        # The call to "create_json" makes sure that the string is
        # valid as a JSON string.
        return create_json ($obj->to_string ());
    }
    else {
        return create_json ($obj);
    }
}

my $jc = JSON::Create->new (indent => 1, sort => 1, 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";

output:

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

(this one is examples/try-harder.pl β€” https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/try-harder.pl. πŸ“)

Heads up though: this obj_handler overrides whatever you set with bool or obj. Currently it does not warn you about that. See BUGS. And the routine you use to handle objects might be the same one you use to handle types β€” see type_handler. All the callback details live under obj.

This method was added in version 0.13 of the module. πŸŽ‰


πŸ’œ replace_bad_utf8 πŸ’œ

$jc->replace_bad_utf8 (1);

Instead of throwing the warning or error Invalid UTF-8, this swaps invalid UTF-8 in the inputs for the Unicode replacement character U+FFFD. So forgiving. So demure. ✨

Now, subtle-but-important: if you use replace_bad_utf8 on input that contains only strings not marked as character strings, and bad UTF-8 gets found, JSON::Create marks the output as a character string. Otherwise that replacement character is literally just a lil pile of broken bytes. You can change that behaviour with the downgrade_utf8 method.

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. It gets validated, and if the format can't be used you get a warning. The format is also capped at a maximum length to stop buffer overflows happening inside the module. Safety first, bestie. 🫢

Setting the format of floating point numbers

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

output:

[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]

(this one is examples/set-fformat.pl β€” https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/set-fformat.pl. 🌈)

Added in version 0.07 of the module. ⭐


πŸ‘‘ sort πŸ‘‘

$jc->sort (1);

Sorts your hash keys. Default is Perl's plain string sorting. Wanna bring your own ordering? Feed it a cmp and go off. And no β€” this does not touch the order of array elements, only hash keys. Arrays stay exactly where they are, they're not the problem. πŸ’…

JSON with indentation and sorted keys

use utf8;
use JSON::Create;

my %emojis = (
    animals => {
        kingkong => '🦍',
        goat => '🐐',
        elephant => '🐘',
    },
    fruit => {
        grape => 'πŸ‡',
        watermelon => 'πŸ‰',
        melon => '🍈',
    },
    baka => { # Japanese words
        'ば' => 'か',
        'あ' => 'ほ',
        'ま' => 'ぬけ',
    },
);
my $jc = JSON::Create->new ();

my @moons = qw!πŸŒ‘ πŸŒ’ πŸŒ“ πŸŒ” πŸŒ• πŸŒ– πŸŒ— 🌘!;
my $i = 0;
for (@moons) {
    $emojis{moons}{$_} = $i;
    $i++;
}

$jc->sort (1);
$jc->indent (1);
print $jc->run (\%emojis);

output:

{
        "animals":{
                "elephant":"🐘",
                "goat":"🐐",
                "kingkong":"🦍"
        },
        "baka":{
                "あ":"ほ",
                "ば":"か",
                "ま":"ぬけ"
        },
        "fruit":{
                "grape":"πŸ‡",
                "melon":"🍈",
                "watermelon":"πŸ‰"
        },
        "moons":{
                "πŸŒ‘":0,
                "πŸŒ’":1,
                "πŸŒ“":2,
                "πŸŒ”":3,
                "πŸŒ•":4,
                "πŸŒ–":5,
                "πŸŒ—":6,
                "🌘":7
        }
}

(you guessed it, examples/sort.pl β€” https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/sort.pl. πŸŽ€)

Added in version 0.29 of the module. πŸ¦‹


πŸ”₯ type_handler πŸ”₯

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

okay so bestie, by default, when JSON::Create bumps into a variable of a type it just does not know what to do with β€” think a glob, or a code ref β€” it prints a warning and hands you back an undefined value. (More on that in "Code, regexes, and other references".) The method type_handler is the glow-up: it sets up a callback that fires whenever a variable of an unhandled type shows up in the input. So like, if you wanna drop the JSON literal null into the output when you hit a reference to an unhandled type, instead of screaming an error β€” the example up above does exactly that. πŸ’…

  • Calling convention β€” the type_handler routine gets passed a single argument and is expected to return a single argument: the JSON to output. It's called in scalar context. In other words, the call looks like this:
$json = &{$jc->{type_handler}} ($item);

If you wanna pass or return multiple values through the type_handler callback, use a closure. There's an example over in the discussion at "obj".

  • Returning undef halts processing β€” if your handler returns the undefined value, create prints a warning "Undefined value from user routine", halts any further processing of the input, and returns the undefined value. like, full stop, bestie πŸ›‘
  • Delete the handler with any false value β€” to remove the handler, just call the function with no argument,
$jc->type_handler ();

or with a false argument:

$jc->type_handler (0);

The behaviour then reverts to the default. so chill

  • Checking the output JSON β€” the JSON your handler spits out can be checked for validity by switching on validation with "validate". If you don't use it, and your return value happens to contain invalid UTF-8, you might see the error "Invalid UTF-8 from user routine". Peep "UTF-8 validation of user-supplied JSON" for more about this error. πŸ‘€
  • Exception handling β€” exceptions (die) thrown inside type_handler callbacks are not caught by create; they get passed straight through to the parent. See the discussion at "obj" for an example.

Ways to handle types

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,
);
# Let's validate the output of the subroutine below.
my $jc = JSON::Create->new (validate => 1, indent => 1, sort => 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, },
                            indent => 1, sort => 1);
    }
);
print $jc->run (\%crazyhash), "\n";

produces output

{
        "code":{
                "type":"CODE",
                "value":"κ°•λ‚¨μŠ€νƒ€μΌ"
        },
        "glob":{
                "type":"GLOB",
                "value":"GLOB(0x209a82a0)"
        },
        "regex":{
                "type":"Regexp",
                "value":"(?^:.*)"
        }
}

(This example is included as examples/type-handler.pl <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/type-handler.pl> in the distribution.) πŸŽ€

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

use JSON::Create;
my $jc = JSON::Create->new ();
$jc->strict (1);
print "Before: ", $jc->run (\1), "\n";
$jc->type_handler (sub {
                       my ($thing) = @_;
                       if (ref $thing eq 'SCALAR') {
                           return $$thing;
                       }
                   });
print "After: ", $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.
Before:
After: 1

(This example is included as examples/type-handler-scalar.pl <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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. πŸ¦‹

Escape all Unicode

use JSON::Create;
use utf8;
my $jc = JSON::Create->new ();
my $in = '血ブâ1ↂΟͺ';
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

"血ブâ1ↂΟͺ"
"\u8d64\u30d6\u00f6\uff21\u2182\u03ea"
"\u8D64\u30D6\u00F6\uFF21\u2182\u03EA"
"血ブâ1ↂΟͺ"

(This example is included as examples/escape-all.pl <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/escape-all.pl> in the distribution.)

Output is always UTF-8

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 = '血ブâ1ↂΟͺ';
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

"血ブâ1ↂΟͺ"
"\u8d64\u30d6\u00f6\uff21\u2182\u03ea"
"\u8D64\u30D6\u00F6\uFF21\u2182\u03EA"
"血ブâ1ↂΟͺ"

(This example is included as examples/escape-all-no-utf8.pl <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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. There's an 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.


✨ CONVERSIONS ✨

This section details what conversions are applied to the various inputs to produce outputs. the receipts, basically 🧾

Hashes

JSON::Create turns associative arrays into JSON objects. The keys are written into JSON as strings, with 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 <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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

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

(This example is included as examples/nested-hash.pl <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/nested-hash.pl> in the distribution.)


Arrays

Arrays are converted to JSON arrays. The order of elements of the array is left unchanged. no shuffling, we respect the lineup πŸ’…

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 <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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 <https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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, sort => 1, indent => 1);

produces output

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

iconic. no notes. πŸ¦‹

(This example is included as examples/nested.pl β€” https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/nested.pl β€” in the distribution.)

πŸ“ Scalars

Non-reference Perl scalars get turned into JSON strings or numbers, depending on what Perl thinks they're holding. That's it, that's the vibe check. ✨

πŸ’– Strings

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

JSON is Unicode, so all output gets checked for Unicode validity. Further, this module insists on UTF-8. (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. You can change that behaviour 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 8259".) It's giving "required by the spec", bestie. 😭

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. Periodt. πŸ’…

πŸŽ€ 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 "\"". Not me having to escape everything β€” but that's literally the rules. πŸ™ˆ

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 β€” https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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 8259", 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"). Two standards beefing, and you're caught in the middle. πŸ’€

⭐ 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 usual way. Perl may interpret an integer with a very large absolute value to be a floating point number, and this module will print it out as such. See also "Context-dependent variables" for the handling of variables with both string and integer values.

πŸ’œ 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 8259":

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. iconic behaviour honestly πŸ’…

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/blib/lib/JSON/Create.pm line 200.
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 β€” https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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 β€” https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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 β€” https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/json-parse-bool.pl β€” in the distribution.)

Other kinds of object can be converted to booleans using the method bool (see below). πŸ”₯

🌈 Context-dependent variables

A context-dependent variable is a variable which may contain a string and a numerical value. Usually the string value is just a representation of the number, but it may not be. The behaviour in the case of a context-dependent variable is as follows. πŸ‘€

If the variable contains a valid numerical value, the numerical value is used in the output JSON, rather than the string value. However, some modules, like charinfo in Unicode::UCD, return context-dependent hash values which have a non-number-like string under the key script with values such as "Latin" or "Common" but no valid numerical value, despite being marked as context-dependent variables. In such cases, the string value is used. (In terms of XS, if the scalar is marked as "SVt_PVIV" or "SVt_PVNV", the scalar is tested with "SvIOK" or "SvNOK" respectively, and if this is true the numerical value is used, if false the string value is used.) ngl that's a whole saga

Up to version 0.25, this module wrongly assumed that the string part of a context-dependent variable would always be a valid representation of a number, and the string was added to the output without quote marks. In version 0.26, checking was done to see if the string actually was a number. In version 0.27 this approach was abandoned and the numerical value was given precedence. In version 0.28 this was again altered for the sake of unusual cases like the script key returned by charinfo in Unicode::UCD, where the scalar is marked as having a numerical value but does not in fact contain a valid numerical value. The glow-up was real. ✨

🧚 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. bestie that's so unserious of it 😭

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. not the whole return value catching strays πŸ’€


✨ 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';

ask and you shall receive, that's the energy 🫢


🌸 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. two paths, both slay πŸ’–

🌈 UNICODE HANDLING 🌈

okay bestie this section is lowkey the ✨lore✨ of how JSON::Create deals with Unicode inside strings. and the whole thing hinges on two things with deeply confusingly similar names: Perl character strings, which we call "utf8", and the actual Unicode encoding "UTF-8". yeah. one is a Perl flag, one is a byte format. they are NOT the same and the entire section is basically about that πŸ’€

πŸ¦‹ UTF-8 only

JSON::Create only consumes and produces the UTF-8 encoding of Unicode. that's it. that's the whole menu. if you need a different encoding, pls go use the Encode module to encode the output yourself, bestie 🫢

πŸŽ€ Input strings must be UTF-8

All strings within the input must be UTF-8 encoded. and listen β€” this does not mean the strings must be Perl character strings (Perl's "utf8"). it means the input strings must be valid UTF-8. input strings can be either Perl character strings or bytes, but either way, the bytes of the string must be valid UTF-8. no exceptions, no vibes-based exceptions, periodt βœ‹

to illustrate, look at this example:

use JSON::Create 'create_json';
use utf8;
$| = 1;
print create_json ('血ブâ1ↂΟͺ'), "\n";
no utf8;
binmode STDOUT, ":raw";
print create_json ('血ブâ1ↂΟͺ'), "\n";
print create_json ("\x99\xff\x10"), "\n";

and it produces output:

"血ブâ1ↂΟͺ"
"血ブâ1ↂΟͺ"
Invalid UTF-8 at /usr/home/ben/projects/json-create/blib/lib/JSON/Create.pm line 192.
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 β€” https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/valid-chars.pl in the distribution.)

the point here, and this is the whole point: the UTF-8 validation is carried out regardless of whether Perl thinks the input string is "utf8". that third call to create_json? Perl did not mark that string as utf8, and it still fails as invalid UTF-8. it's giving equal-opportunity validation πŸ’…

and this insistence on UTF-8 within input strings is related to the whole "Unicode upgrades are not done according to Perl conventions" thing below πŸ‘‡

⭐ Output is valid UTF-8

All of the output of either the function create_json or the method create is valid UTF-8. again β€” this does not mean the output strings are marked as Perl character strings ("utf8"). it means the output has been validated as UTF-8. validation β‰  marking, bestie, they're different things and the distinction is literally the whole vibe of this page.

now. there is one exception to this. for 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, then JSON::Create validates the entire output as UTF-8, as described in "UTF-8 validation of user-supplied JSON". the loophole gets closed, babes πŸšͺπŸ”’

πŸ’œ Unicode upgrades are not done according to Perl conventions

here's the tea β˜• if a single string anywhere in the input is a Perl character string β€” in other words, marked as "utf8" β€” then the entire output string is marked as a Perl character string, "utf8", without altering the non-"utf8" bytes.

and yeah, 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 you combine the two, it's illegal to treat either of them as bytes β€” instead they must both be treated as numbers. that's the Perl way.

to illustrate, here's JSON's behaviour contrasted with JSON::Create's:

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

which produces output:

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

(this example is included as examples/json-unicode.pl β€” https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/json-unicode.pl in the distribution.)

so the Perl convention is: if a non-utf8 string and a utf8 string get combined, they should be combined the way JSON does it β€” by treating each byte of the non-utf8 string as if it's a single Unicode code point, and writing equivalent UTF-8 bytes for that code point into the output. JSON::Create does a different thing: it insists 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. it's not a bug, bestie, it's a lifestyle choice πŸ’…

and here's an example that 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";

which produces output:

{"x":"\u001f\b\u0000\u0000\u0000\u0000\u0000\u0000\u0003{ÜØý¸±÷qcÿãÆ\u001b'\u0003\u0000Β£ Β³\u0012\u000f\u0000\u0000\u0000","y":"さしすせそ"}
Invalid UTF-8 at /usr/home/ben/projects/json-create/blib/lib/JSON/Create.pm line 192.
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 β€” https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/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. that's it, that's the section, go read the other section πŸŽ€

🧚 UTF-8 validation of user-supplied JSON

okay so this one is important. 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 β€” then the entire output string has to be validated as UTF-8. why? to prevent a loophole where a string containing non-UTF-8 compliant bytes could get upgraded to a character string ("utf8"). we're not letting that sneak through, no ma'am 🚫

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

here's 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 => 'ぢー'});

which 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 β€” https://fastapi.metacpan.org/source/BKB/JSON-Create-0.36/examples/user-bad-utf8.pl in the distribution.)


πŸ”₯ DIAGNOSTICS πŸ”₯

all diagnostics are warnings by default. you can change that behaviour with the method fatal_errors. so if you want them to be, like, fatal, you absolutely can. main character energy, your call πŸ‘‘

πŸ’€ 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. slay your way out of it πŸ’…

😭 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. yeah. it failed. that's the whole story.

🌈 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 πŸ”₯

okay so bestie, you wanna know if this thing is fast? there's literally a benchmarking script sitting in bench/bench.pl and it does a whole side-by-side with JSON::XS and Cpanel::JSON::XS. the outputs look like this, and the improve column is showing you how much faster the fastest module was compared to the slowest one:

Versions used:
+-----+------------------+---------+
| CJX | Cpanel::JSON::XS | 4.25    |
+-----+------------------+---------+
| JX  | JSON::XS         | 4.03    |
+-----+------------------+---------+
| JC  | JSON::Create     | 0.29_02 |
+-----+------------------+---------+
Comparing hash of ASCII strings...

Repetitions: 1000 x 200 = 200000
+--------+--------+------------+---------+
| Module | 1/min  | min        | improve |
+--------+--------+------------+---------+
| CJX    | 380539 | 0.00262785 | 1       |
| JC     | 755322 | 0.00132394 | 1.98487 |
| JX     | 672056 | 0.00148797 | 1.76606 |
+--------+--------+------------+---------+

Comparing hash of integers...

Repetitions: 1000 x 200 = 200000
+--------+--------+------------+---------+
| Module | 1/min  | min        | improve |
+--------+--------+------------+---------+
| CJX    | 157604 | 0.00634503 | 1       |
| JC     | 388038 | 0.00257707 | 2.46211 |
| JX     | 199245 | 0.00501895 | 1.26422 |
+--------+--------+------------+---------+

Comparing hash of Unicode strings...

Repetitions: 1000 x 200 = 200000
+--------+-------------+-------------+---------+
| Module | 1/min       | min         | improve |
+--------+-------------+-------------+---------+
| CJX    | 513127      | 0.00194883  | 1       |
| JC     | 1.04831e+06 | 0.000953913 | 2.04299 |
| JX     | 808463      | 0.00123692  | 1.57556 |
+--------+-------------+-------------+---------+

Comparing array of floats...

Repetitions: 1000 x 200 = 200000
+--------+--------+------------+---------+
| Module | 1/min  | min        | improve |
+--------+--------+------------+---------+
| CJX    | 136707 | 0.00731492 | 1       |
| JC     | 342001 | 0.00292397 | 2.50171 |
| JX     | 156961 | 0.00637102 | 1.14816 |
+--------+--------+------------+---------+

Comparing array of ASCII strings...

Repetitions: 1000 x 200 = 200000
+--------+--------+------------+---------+
| Module | 1/min  | min        | improve |
+--------+--------+------------+---------+
| CJX    | 279881 | 0.00357294 | 1       |
| JC     | 547059 | 0.00182796 | 1.95461 |
| JX     | 487766 | 0.00205016 | 1.74276 |
+--------+--------+------------+---------+

she ate and left absolutely no crumbs, periodt πŸ’…


πŸ’€ BUGS πŸ’€

ngl this is the part where we're honest about the flaws. it's giving "we're working on it".

  • right now there is genuinely no way to delete object handlers that you set through obj from a JSON::Create object. πŸ™ˆ
  • there are a few remaining undecided issues floating around about the default object serialization.
  • no warning gets printed when you use clashing methods like bool and obj_handler. not a peep. silence. πŸ’€
  • there is a bug in JSON::Create::PP's handling of non-integer numbers.
  • the floating point printing loses precision on round trips. 😭

πŸ“œ HISTORY πŸ“œ

storytime bestie, here's the whole glow-up timeline:

  • set_fformat got added in version 0.07.
  • validate got added in version 0.07.
  • fatal_errors got added in version 0.10.
  • replace_bad_utf8 got added in version 0.12.
  • obj_handler got added in version 0.13. this version also added loading of the Pure-Perl version of the module, JSON::Create::PP, in case loading JSON::Create failed.
  • non_finite_handler got added in version 0.17.
  • downgrade_utf8 got added in version 0.18.
  • the Invalid UTF-8 from user routine diagnostic got added in version 0.19.
  • the create_json_strict function plus the strict methods and their associated diagnostics got added in version 0.20.
  • indentation got added for objects with indent in version 0.27.
  • version 0.28 changed up how context-dependent variables are handled β€” it now uses the numerical part as a JSON number if it's valid, and the string part as a JSON string if it isn't a valid number. go read "Context-dependent variables" for the whole breakdown.
  • arguments to create_json and create_json_strict, and the methods set, sort and cmp, got added in version 0.29.
  • version 0.30 rewrote set and the related routines in XS, made the module faster, and added write_json. glow up fr fr ✨
  • in version 0.31, the method run got renamed to create.

πŸŽ€ Old names πŸŽ€

  • run β€” the method run was renamed create in version 0.31. the old name is not deprecated and will keep working indefinitely, so don't panic, your old code is safe. 🫢

πŸ‘€ SEE ALSO πŸ‘€

  • JSON::Create::PP β€” this is the backup module for JSON::Create, written in pure Perl.
  • RFC 8259 β€” RFC 8259 "The JavaScript Object Notation (JSON) Data Interchange Format" <https://www.ietf.org/rfc/rfc8259.txt>
  • "SEE ALSO" in JSON::Parse β€” pls go check out "SEE ALSO" in JSON::Parse for the info on JSON specifications and a whole list of other CPAN modules for JSON.

🧚 Installing from the github repository 🧚

experimentally, it's possible to install this module straight from Github. heads up though: you'll also need to install Template and possibly a few other modules too.

🌸 Blog posts 🌸

there are some blog posts about the JSON::Create internals that Perl XS programmers will be into, and they're right here:


πŸ‘‘ AUTHOR πŸ‘‘

Ben Bullock, <benkasminbullock@gmail.com>


this package and its associated files are copyright (C) 2015-2026 Ben Bullock.

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

the sorting code that cmp uses comes from the FreeBSD source code and is copyright by the Regents of the University of California.


and that's a wrap on the whole page bestie!! πŸŽ€ this was JSON::Create(3), from perl v5.42.2, dated 2026-04-07 β€” go forth and serialize responsibly, ily, mwah πŸ’…βœ¨πŸ¦‹

πŸ“¦ where this page comes from