Skip to content

Releases: nerima-lisp/cl-json-kit

v1.0.0

Choose a tag to compare

@takeokunn takeokunn released this 25 Jul 12:49
v1.0.0
62a3164

The first stable release.

The public API does not change here. What changes is that it is now a
promise: from this release on, the exported surface of the json-kit
package and its documented behavior follow Semantic
Versioning
, as stated in the Compatibility
Promise
.

Both halves of that promise are enforced by the test suite rather than by
intent.

Measured RFC 8259 conformance

The whole parsing corpus of JSONTestSuite
is vendored into t/rfc8259-conformance-test.lisp and runs on every build:

Corpus class Requirement Cases Result
y_ must accept 95 95 accepted
n_ must reject 188 188 rejected
i_ implementation-defined 35 each choice pinned by a test

Nothing crashes, hangs, or signals anything other than json-parse-error, and
the two cases the corpus ships as 100 KB / 250 KB files of repeated punctuation
are rejected by a depth bound rather than by exhausting the control stack. The
25 cases whose input is not well-formed UTF-8 are excluded by name: this API
consumes characters rather than octets, so they exercise the external-format
layer rather than the reader.

A pinned public API

t/public-api-test.lisp asserts the exact export list, a docstring on every
exported symbol, and the package name and absence of nicknames. A symbol cannot
enter or leave the public surface without failing a spec, which forces the
change to be classified against the compatibility promise.

That docstring assertion found 11 exported symbols with none — both condition
classes and nine slot readers, which define-condition and defstruct have
nowhere to document. documentation now answers at the REPL for the whole
public surface.

Fixed

Float serialization depended on ambient state rather than on the value.
Common Lisp's printer appends a type marker (d/f/s/l) whenever a float's
format differs from *read-default-float-format*, and the writer rewrote that
marker to e. The same double therefore serialized as 3.14e0 in a default
image and as 3.14 in one whose default had been set to double-float, with
single-floats coming out the other way round. Both forms are valid JSON that
read back identically, so nothing was incorrect — what was wrong is that
stringify was not a function of its argument. It now is: 3.14d0 always
serializes as 3.14, 1.0d308 always as 1.0e308.

The existing round-trip spec could not catch this, because it bound
*read-default-float-format* to the value's own type before reading the text
back — exactly the compensation that hid the asymmetry.

Also in this release

  • nix flake check now builds the documentation, which builds with
    mkdocs --strict. A broken link or a page missing from the nav fails a pull
    request instead of failing the publish workflow after a merge to main.
  • The README is an entry point rather than a second copy of the reference
    documentation, which had grown to 466 lines of content duplicated from the
    documentation site.

Full detail in the changelog.

v0.2.0

Choose a tag to compare

@takeokunn takeokunn released this 24 Jul 09:06
v0.2.0
425a6b8

Internal modernization and a behavior-preserving performance pass over the 0.1.0 public surface — no API or observable behavior changes.

Changed

  • Split the monolithic reader/writer into per-concern files; shared types/constants in data.lisp; control flow consolidated into macro files.
  • Faster hot paths: allocation-free plain-integer parsing (fixnum with bignum fallback), duplicate-table-free default object path, per-array (not per-element) error-path consing, fixnum \uXXXX decoding, run-flushed string serialization.
  • Bumped the cl-weave test dependency to v0.10.0; hardened CI timeouts.

Added

  • Reproducible benchmark harness under benchmark/ comparing against Jzon, Jonathan, JSOWN, and Yason.

See CHANGELOG.md for details.

v0.1.0

Choose a tag to compare

@takeokunn takeokunn released this 23 Jul 15:33
v0.1.0
53c7b91

First release of cl-json-kit — a dependency-free, SBCL-only JSON reader and writer for Common Lisp strings and character streams.

Highlights

  • Reading: parse (whole string), parse-prefix (one value + exclusive end position), and read-json (one value from a stream without over-consuming following input).
  • Parser options: shape (:object-type, :array-type, :key-type), duplicate-key policy, custom null/false/true values, and :key-decoder / :number-decoder / :object-hook / :array-hook callbacks.
  • Resource bounds for untrusted input: :max-depth, :max-input-length, :max-string-length, :max-number-length, :max-array-elements, :max-object-members, :timeout-seconds.
  • Distinct sentinels +json-null+ / +json-false+ (with json-null-p / json-false-p) keep JSON null and false distinguishable from Lisp nil and from each other.
  • UTF-16 surrogate pairs in \uXXXX escapes decode into a single character (e.g. emoji).
  • Rich errors: json-parse-error carries position, line, column, path, expected, context, and text; json-serialization-error carries message and path.
  • Writing: stringify (to string) and write-json (to stream) dispatch purely by Lisp type (hash-table → object, vector/list → array), never guessing an object/array shape from a cons list's structure. Options include :pretty, :indent, :sort-keys, :null-value / :false-value, :number-encoder, and output bounds.
  • Ordered objects: make-json-object / json-object-p / json-object-members preserve member order and duplicate keys, plus alist->json-object / json-object->alist.

Full details in CHANGELOG.md.