NAME

  mb::JSON - JSON encode/decode for multibyte (UTF-8) strings

SYNOPSIS

  use mb::JSON;

  # decode: JSON text -> Perl data
  my $data = mb::JSON::decode('{"name":"Alice","age":30,"active":true}');
  print $data->{name};    # Alice
  print $data->{age};     # 30
  print $data->{active};  # true (mb::JSON::Boolean object)

  # parse: alias for decode() -- both names are interchangeable
  my $data = mb::JSON::parse('{"key":"value"}');

  # encode: Perl data -> JSON text
  my $json = mb::JSON::encode({
      name   => 'Alice',
      active => mb::JSON::true,
      score  => undef,
  });
  # -> {"active":true,"name":"Alice","score":null}

  # stringify: alias for encode() -- both names are interchangeable
  my $json = mb::JSON::stringify({
      name   => 'Alice',
      active => mb::JSON::true,
      score  => undef,
  });
  # -> {"active":true,"name":"Alice","score":null}

  # Boolean values
  my $json = mb::JSON::encode({
      flag => mb::JSON::true,
      done => mb::JSON::false,
  });
  # -> {"done":false,"flag":true}

DESCRIPTION

  mb::JSON is a simple, dependency-free JSON encoder and decoder
  designed for Perl 5.005_03 and later.  It handles UTF-8 multibyte
  strings correctly, making it suitable for environments where standard
  JSON modules requiring Perl 5.8+ are unavailable.

  mb::JSON provides two pairs of symmetric functions:

    decode() / parse()       -- JSON text -> Perl data
    encode() / stringify()   -- Perl data -> JSON text

  Within each pair, both names are aliases and produce identical output.
  parse() mirrors decode(), stringify() mirrors encode().

FUNCTIONS

  mb::JSON::decode($json_text)
      Converts a JSON text string to a Perl data structure.
      If no argument is given, $_ is used.

  mb::JSON::parse($json_text)
      Alias for decode().  Both names are interchangeable.

  mb::JSON::encode($data)
      Converts a Perl data structure to a JSON text string.
      Hash keys are sorted alphabetically for deterministic output.
      UTF-8 multibyte bytes are output as-is (not \uXXXX-escaped).
      If called with no argument at all, $_ is used; encode(undef)
      still returns null.

  mb::JSON::stringify($data)
      Alias for encode().  Both names are interchangeable.
      Mirrors the JSON.stringify() function in JavaScript.

  mb::JSON::true
      Returns the mb::JSON::Boolean object representing JSON true.
      Numifies to 1, stringifies to "true".

  mb::JSON::false
      Returns the mb::JSON::Boolean object representing JSON false.
      Numifies to 0, stringifies to "false".

ENCODING RULES

  undef                -> null
  mb::JSON::true       -> true
  mb::JSON::false      -> false
  number-like scalar   -> number (no quotes)
  other scalar         -> "string" (UTF-8 bytes kept as-is)
  ARRAY reference      -> [...]
  HASH reference       -> {...} (keys sorted alphabetically)

DECODING RULES

  null    -> undef
  true    -> mb::JSON::Boolean (numifies to 1)
  false   -> mb::JSON::Boolean (numifies to 0)
  number  -> Perl number
  string  -> Perl string (\uXXXX converted to UTF-8; UTF-16 surrogate
             pairs combined into 4-byte UTF-8 for code points > U+FFFF)
  object  -> hash reference (a repeated key keeps the last value)
  array   -> array reference

  A leading UTF-8 byte order mark (EF BB BF) is skipped before parsing.

  Whitespace between tokens is space, tab, LF and CR -- exactly the set
  RFC 8259 defines. Form feed and vertical tab are not whitespace. The
  set is spelled out rather than written as perl's \s so that it is the
  same on every supported perl.

CONFIGURATION

  $mb::JSON::MAX_DEPTH   (default 512)
      Maximum nesting depth accepted by decode() and produced by
      encode(). Deeper input or data is rejected with an error rather
      than exhausting the Perl stack. A false value disables the check.
      The limit counts objects and arrays, not values, and both
      directions stop at the same count, so anything decode() accepts
      can be handed straight back to encode().

  $mb::JSON::STRICT      (default 0)
      When false, decode() copies string bytes through unchanged: raw
      control characters are accepted and UTF-8 is not validated.
      When true, decode() rejects a raw control character U+0000-U+001F
      inside a string and rejects a string whose bytes are not
      well-formed UTF-8.

  Both are ordinary package variables, so local() works:

    local $mb::JSON::MAX_DEPTH = 64;
    local $mb::JSON::STRICT    = 1;
    my $data = mb::JSON::decode($untrusted);

BOOLEAN VALUES

  Perl has no native boolean type. mb::JSON provides two singleton
  objects for unambiguous boolean encoding:

    mb::JSON::true   -- stringifies as "true",  numifies as 1
    mb::JSON::false  -- stringifies as "false", numifies as 0

  A plain 1 or 0 encodes as a JSON number, not a boolean:

    encode({ count => 1 })             -> '{"count":1}'
    encode({ flag => mb::JSON::true }) -> '{"flag":true}'

INCLUDED DOCUMENTATION

  The doc/ directory contains JSON cheat sheets in 21 languages
  for use as quick reference materials.

INSTALLATION

  To install this module type the following:

    perl Makefile.PL
    make
    make test
    make install

  Or using pmake.bat (Windows):

    pmake.bat test
    pmake.bat install

COMPATIBILITY

  This module works with Perl 5.005_03 and later.

  WHY PERL 5.005_03 SPECIFICATION?

  This module adheres to the Perl 5.005_03 specification--not because we
  use the old interpreter, but because this specification represents the
  simple, original Perl programming model that makes programming enjoyable.

  THE STRENGTH OF MODERN TIMES

  Some people think the strength of modern times is the ability to use
  modern technology. That thinking is insufficient. The strength of modern
  times is the ability to use ALL technology up to the present day.

  By adhering to the Perl 5.005_03 specification, we gain access to the
  entire history of Perl--from 5.005_03 to 5.42 and beyond--rather than
  limiting ourselves to only the latest versions.

  Key reasons:

  - Simplicity: Original Perl approach keeps programming "raku" (easy/fun)
  - JPerl: Final version of JPerl (Japanese Perl)
  - Universal: Runs on ALL Perl versions (5.005_03 through 5.42+)
  - Philosophy: Programming should be enjoyable (Camel Book readers know!)

  Perl 5.6+ introduced character encoding complexity that made programming
  harder. By following the 5.005_03 specification, we maintain the joy of
  Perl programming.

TARGET USE CASES

  - Processing JSON data with UTF-8 multibyte strings
  - Environments where JSON::PP or JSON::XS cannot be installed
  - Legacy Perl environments (5.005_03 and later)
  - Lightweight scripts and utilities with no external dependencies

LIMITATIONS

  - Numbers are not distinguished from strings if the value matches the
    number pattern, so the string "30" is encoded as the number 30.
    Leading-zero strings such as "007" stay strings.
  - Numbers pass through Perl's own numeric conversion, so 1.0 and 1e2
    are re-encoded as 1 and 100, and an integer wider than the
    platform's floating point mantissa loses precision.
  - A number literal outside the range of this perl's floating point
    type is rejected by decode()/parse() rather than silently converted:
    it would otherwise become Inf at one end, or 0 after losing every
    significant digit it carried at the other. Where the range ends
    depends on how perl was built -- an ordinary double runs out around
    1e308, so 1e400 and 1e-400 are rejected there, while a perl built
    -Duselongdouble or -Dusequadmath accepts both. A literal that is
    genuinely zero, including 0e-400, is unaffected on every build.
  - References other than ARRAY and HASH are stringified rather than
    raising an error. A blessed hash or array reference is stringified
    too, because ref() returns the class name.
  - Inf and NaN have no JSON representation and are rejected by
    encode()/stringify(). They are recognized by how perl prints them,
    so a string spelling exactly "Inf", "-Inf", "Infinity", "NaN" or one
    of the "1.#INF" forms is rejected too. Longer words such as "Info"
    and "nano" are unaffected.
  - By default the decoder does not validate UTF-8 and accepts raw
    control characters inside strings. Set $mb::JSON::STRICT to enable
    those checks.

AUTHOR

  INABA Hitoshi <ina.cpan@gmail.com>

COPYRIGHT AND LICENSE

  Copyright (c) 2021, 2022, 2026 INABA Hitoshi <ina.cpan@gmail.com>

  This software is free software; you can redistribute it and/or modify
  it under the same terms as Perl itself.

