NAME
JSON::YY - Fast JSON encoder/decoder with document manipulation API, backed by yyjson
SYNOPSIS
# functional API (fastest for simple encode/decode)
use JSON::YY qw(encode_json decode_json);
my $json = encode_json { foo => 1, bar => [1, 2, 3] };
my $data = decode_json '{"foo":1}';
# OO API (configurable)
my $coder = JSON::YY->new(utf8 => 1, pretty => 1);
my $json = $coder->encode($data);
my $data = $coder->decode($json);
# zero-copy readonly decode (fastest for read-only access)
use JSON::YY qw(decode_json_ro);
my $data = decode_json_ro $json; # readonly, zero-copy strings
# Doc API (manipulate JSON without full Perl materialization)
use JSON::YY ':doc';
my $doc = jdoc '{"users":[{"name":"Alice","age":30}]}';
jset $doc, "/users/0/age", 31;
my $name = jgetp $doc, "/users/0/name"; # "Alice"
print jencode $doc, ""; # serialize
DESCRIPTION
JSON::YY is a JSON module backed by yyjson 0.12.0, a high-performance JSON library written in ANSI C. It provides three API layers:
- Functional/Keyword API -
encode_json/decode_jsoncompiled as custom Perl ops via the keyword plugin, eliminating function call overhead. - OO API - JSON::XS-compatible interface with chaining setters.
- Doc API - Operate directly on yyjson's mutable document tree using path-based keywords. Avoids full Perl materialization for surgical JSON edits.
FUNCTIONAL API
use JSON::YY qw(encode_json decode_json decode_json_ro);
- encode_json $perl_value
-
Encode a Perl value to a UTF-8 JSON string. Equivalent to
JSON::YY->new->utf8->encode($value)but faster (no object overhead). - decode_json $json_string
-
Decode a UTF-8 JSON string to a Perl value.
- decode_json_ro $json_string
-
Decode to a deeply readonly structure with zero-copy strings. String SVs point directly into yyjson's parsed buffer. Faster than
decode_jsonfor medium/large documents. Modification attempts croak.
When imported, these names are registered as Perl keywords that compile to custom ops, bypassing the normal function dispatch.
OO API
my $coder = JSON::YY->new(utf8 => 1, pretty => 1);
my $coder = JSON::YY->new->utf8->pretty; # chaining style
- new(%options)
-
Create a new encoder/decoder. Options:
utf8,pretty,allow_nonref,allow_unknown,allow_blessed,convert_blessed,max_depth. - encode($perl_value)
-
Encode to JSON string.
- decode($json_string)
-
Decode from JSON string.
- decode_doc($json_string)
-
Decode to a
JSON::YY::Dochandle (mutable document, no Perl materialization). Can then use Doc API keywords on the result. - utf8, pretty, allow_nonref, allow_unknown, allow_blessed, convert_blessed
-
Boolean setters, return
$selffor chaining. - max_depth($n)
-
Set maximum nesting depth (default 512).
DOC API
use JSON::YY ':doc';
The Doc API operates on yyjson's internal mutable document tree, using JSON Pointer (RFC 6901) paths for addressing. All keywords compile to custom ops for maximum performance.
Document creation
- jdoc $json_string
-
Parse JSON into a mutable document handle (
JSON::YY::Doc). - jfrom $perl_value
-
Create a document from a Perl value (hash, array, scalar).
Value constructors
Create typed JSON values for use with jset:
- jstr $value - JSON string (ensures string type, e.g.
jstr "007") - jnum $value - JSON number
- jbool $value - JSON true/false
- jnull - JSON null
- jarr - empty JSON array
- jobj - empty JSON object
Path operations
All path arguments use JSON Pointer syntax: /key/0/nested. Use "" for root. Use /arr/- to append to an array.
- jget $doc, $path
-
Get a subtree reference (returns a Doc that shares the parent's tree).
- jgetp $doc, $path
-
Get value materialized to Perl (string, number, hashref, arrayref, etc.). Alias:
jdecode. - jset $doc, $path, $value
-
Set value at path.
$valuecan be a scalar (auto-typed), Perl ref (recursively converted), or another Doc (deep-copied). Returns$doc. - jdel $doc, $path
-
Delete value at path. Returns the removed subtree as an independent Doc, or
undefif path not found. - jhas $doc, $path
-
Check if path exists. Returns boolean.
- jclone $doc, $path
-
Deep copy subtree into a new independent document.
Serialization
- jencode $doc, $path
-
Serialize document or subtree to compact JSON bytes.
- jpp $doc, $path
-
Serialize to pretty-printed JSON (indented with 4 spaces).
- jraw $doc, $path, $json_fragment
-
Insert a raw JSON string at path without Perl roundtrip. The fragment is parsed by yyjson and inserted directly into the document tree.
Inspection
- jtype $doc, $path
-
Returns type string:
"object","array","string","number","boolean","null". - jlen $doc, $path
-
Array length, object key count, or string byte length.
- jkeys $doc, $path
-
Object keys as a list of strings.
- jvals $doc, $path
-
Object values as a list of Doc handles.
Iteration
Pull-style iterators for arrays and objects:
my $it = jiter $doc, "/users";
while (defined(my $elem = jnext $it)) {
my $name = jgetp $elem, "/name";
my $key = jkey $it; # for objects: current key
}
- jiter $doc, $path - create iterator
- jnext $iter - advance, returns Doc or undef
- jkey $iter - current key (objects only)
File I/O
- jread $filename
-
Read a JSON file and return a Doc handle.
- jwrite $doc, $filename
-
Write a Doc to a file (pretty-printed).
Path enumeration
- jpaths $doc, $path
-
Enumerate all leaf paths under the given path. Returns a list of JSON Pointer strings. Keys containing
~or/are escaped per RFC 6901.
Search
- jfind $doc, $array_path, $key_path, $match_value
-
Find the first element in an array where the value at
$key_pathequals$match_value. Returns the matching element as a Doc, orundefif not found.my $bob = jfind $doc, "/users", "/name", "Bob";
Patching
- jpatch $doc, $patch_doc
-
Apply RFC 6902 JSON Patch.
$patch_docmust be a Doc containing a patch array. Modifies$docin-place. - jmerge $doc, $patch_doc
-
Apply RFC 7386 JSON Merge Patch. Modifies
$docin-place.
Comparison
Type predicates
All return boolean. Return false for missing paths.
- jis_obj $doc, $path
- jis_arr $doc, $path
- jis_str $doc, $path
- jis_num $doc, $path
- jis_int $doc, $path
- jis_real $doc, $path
- jis_bool $doc, $path
- jis_null $doc, $path
Overloading
JSON::YY::Doc objects support:
"$doc" # stringify to JSON
if ($doc) # always true
$a eq $b # deep equality
$a ne $b # deep inequality
IMPORT FLAGS
use JSON::YY -utf8, -pretty;
Imports encode_json/decode_json with the specified flags pre-configured.
JSON POINTER (RFC 6901)
Paths use JSON Pointer syntax:
"" root value
/key object key
/0 array index 0
/a/b/0/c nested path
/arr/- append to array (jset/jraw only)
/k~0ey key containing ~ (escaped as ~0)
/k~1ey key containing / (escaped as ~1)
EXAMPLES
# surgical edit of large document
use JSON::YY ':doc';
my $doc = jdoc $large_json;
jset $doc, "/config/timeout", 30;
my $json = jencode $doc, "";
# extract fields without full decode
my $doc = jdoc $api_response;
my $status = jgetp $doc, "/status";
my $count = jlen $doc, "/data/items";
# type-safe value insertion
jset $doc, "/active", jbool 1; # true, not 1
jset $doc, "/id", jstr "007"; # "007", not 7
# iterate without materializing
my $it = jiter $doc, "/users";
while (defined(my $u = jnext $it)) {
say jgetp $u, "/name" if jis_str $u, "/name";
}
# apply RFC 6902 patch
my $patch = jdoc '[{"op":"replace","path":"/v","value":2}]';
jpatch $doc, $patch;
# apply RFC 7386 merge patch
jmerge $doc, jdoc '{"debug":null,"version":"2.0"}';
# OO decode directly to Doc
my $coder = JSON::YY->new(utf8 => 1);
my $doc = $coder->decode_doc($json);
# insert raw JSON without Perl roundtrip
jraw $doc, "/blob", '[1,2,{"nested":true}]';
# deep compare
say "equal" if jeq $doc_a, $doc_b;
say "equal" if $doc_a eq $doc_b; # overloaded
PERFORMANCE
Encode vs JSON::XS (higher is better):
small (38B): YY 16-27% faster
medium (11KB): YY 8-12% faster
large (806KB): YY 50-170% faster
Decode vs JSON::XS:
small: YY ~10% slower (SV allocation overhead)
medium: decode_json_ro 25% faster
large: decode_json_ro ~16% faster
Doc API vs Perl decode-modify-encode:
read single value: Doc ~equal
modify + serialize: Doc 30% faster (small), 540% faster (medium)
read from large: Doc 380% faster (no materialization)
LIMITATIONS
canonicalmode is accepted but not yet implemented (yyjson has no sorted-key writer).NaN and Infinity values cannot be encoded (croaks).
COOKBOOK
Read config, modify, write back
use JSON::YY ':doc';
my $config = jread "config.json";
jset $config, "/database/host", "newhost";
jwrite $config, "config.json";
Extract fields from large API response
my $doc = jdoc $response_body;
my $status = jgetp $doc, "/status";
my $count = jlen $doc, "/data/items";
my $first = jgetp $doc, "/data/items/0/name";
Find user by name in array
my $user = jfind $doc, "/users", "/name", "Alice";
say jgetp $user, "/email" if defined $user;
Build document from scratch
my $doc = jfrom {};
jset $doc, "/name", "My App";
jset $doc, "/version", jnum 1;
jset $doc, "/features", jarr;
jset $doc, "/features/-", "auth";
jset $doc, "/features/-", "logging";
jset $doc, "/debug", jbool 0;
jwrite $doc, "output.json";
Apply incremental updates (merge patch)
my $doc = jread "state.json";
jmerge $doc, jdoc $incoming_patch_json;
jwrite $doc, "state.json";
Debug: show all paths
my @paths = jpaths $doc, "";
say "$_ = ", jencode $doc, $_ for @paths;
Type-safe assertions
die "expected array" unless jis_arr $doc, "/items";
die "expected string" unless jis_str $doc, "/name";
Compare two documents
die "configs differ" if $prod ne $staging; # overloaded
# or explicitly:
die "differ" unless jeq $prod, $staging;
CHEATSHEET
# --- Import ---
use JSON::YY qw(encode_json decode_json); # functional
use JSON::YY ':doc'; # Doc API keywords
# --- Encode/Decode ---
encode_json $data decode_json $json
$coder->encode($data) $coder->decode($json)
decode_json_ro $json # zero-copy readonly
# --- Doc lifecycle ---
jdoc $json # parse JSON string -> Doc
jfrom $perl_data # Perl data -> Doc
jread $file # read JSON file -> Doc
jwrite $doc, $file # Doc -> write JSON file
jencode $doc, $path # Doc -> JSON string
jpp $doc, $path # Doc -> pretty JSON string
jgetp $doc, $path # Doc -> Perl value
$coder->decode_doc($json) # OO: JSON -> Doc
# --- Read ---
jget $doc, $path # -> Doc subtree ref (shared)
jgetp $doc, $path # -> Perl value (materialized)
jhas $doc, $path # -> bool
jfind $doc, $arr, $k, $v # -> Doc (first match) or undef
# --- Write ---
jset $doc, $path, $val # set (scalar/ref/Doc)
jdel $doc, $path # delete -> Doc (removed)
jraw $doc, $path, $json # insert raw JSON fragment
# --- Copy ---
jclone $doc, $path # deep copy -> independent Doc
# --- Inspect ---
jtype $doc, $path # "object"|"array"|"string"|...
jlen $doc, $path # array/object/string length
jkeys $doc, $path # object keys (list)
jvals $doc, $path # object values (list of Doc)
jpaths $doc, $path # all leaf paths (list)
# --- Type predicates ---
jis_obj jis_arr jis_str jis_num jis_int jis_real jis_bool jis_null
# --- Value constructors ---
jstr $v jnum $v jbool $v jnull jarr jobj
# --- Iterate ---
my $it = jiter $doc, $path;
while (defined(my $v = jnext $it)) { jkey $it; ... }
# --- Patch ---
jpatch $doc, $patch # RFC 6902
jmerge $doc, $patch # RFC 7386
# --- Compare ---
jeq $a, $b # deep equality
$a eq $b # overloaded
"$doc" # overloaded stringify
# --- Path syntax (JSON Pointer RFC 6901) ---
"" root /key object key
/0 array[0] /arr/- append to array
/k~0ey key with ~ /k~1ey key with /
SEE ALSO
JSON::XS, Cpanel::JSON::XS, JSON::PP
yyjson: https://github.com/ibireme/yyjson
AUTHOR
vividsnow
LICENSE
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
yyjson is included under the MIT License.