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 XS::Parse::Keyword, 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 via qw(), these compile to custom ops via
    XS::Parse::Keyword, bypassing normal function dispatch. Keywords are
    lexically scoped. The "-flag" import style installs pre-configured
    closures instead (not compiled as keywords).

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). Croaks if path not found. Use "jhas" to check first, or
        "jgetp" for undef-on-missing behavior.

    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 (ops/sec, higher is better)
                        JSON::XS    JSON::YY     delta
        small  (38B)    6.4M        6.7M         +4%
        medium (11KB)   26.8K       27.3K        +2%
        large  (806KB)  153         234         +53%

  Decode (ops/sec, higher is better)
                        JSON::XS    JSON::YY     delta
        small  (38B)    4.2M        3.5M        -17%
        medium (11KB)   16.9K       14.1K       -16%
        large  (806KB)  249         267          +8%

    Encode is consistently faster, especially on large payloads where
    yyjson's optimized serializer dominates. Decode is slightly slower on
    small/medium payloads due to Perl SV allocation overhead.

  Doc API vs decode-modify-encode cycle
                                Perl        Doc         speedup
        read one value          3.0M/s      3.1M/s      ~equal
        modify + serialize      1.6M/s      2.2M/s      +42%
        read from large doc     14.6K/s     73.7K/s     +405%
        modify large + encode   7.4K/s      47.3K/s     +536%
        clone subtree           15.0K/s     75.2K/s     +400%
        type/length check       14.4K/s     74.6K/s     +418%

    The Doc API avoids full Perl materialization, providing 4-5x speedup for
    surgical operations on medium/large documents.

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

