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_json" compiled 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_json" for 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::Doc" handle (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 $self for 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. $value can 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 "undef" if 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_path equals $match_value. Returns the matching element as a Doc, or "undef" if not found. my $bob = jfind $doc, "/users", "/name", "Bob"; Patching jpatch $doc, $patch_doc Apply RFC 6902 JSON Patch. $patch_doc must be a Doc containing a patch array. Modifies $doc in-place. jmerge $doc, $patch_doc Apply RFC 7386 JSON Merge Patch. Modifies $doc in-place. Comparison jeq $doc_a, $doc_b Deep equality comparison. Returns boolean. 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 * "canonical" mode 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: 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.