Releases: nerima-lisp/cl-json-kit
Release list
v1.0.0
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 checknow 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 tomain.- 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
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
\uXXXXdecoding, run-flushed string serialization. - Bumped the
cl-weavetest 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
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), andread-json(one value from a stream without over-consuming following input). - Parser options: shape (
:object-type,:array-type,:key-type), duplicate-key policy, customnull/false/truevalues, and:key-decoder/:number-decoder/:object-hook/:array-hookcallbacks. - 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+(withjson-null-p/json-false-p) keep JSONnullandfalsedistinguishable from Lispniland from each other. - UTF-16 surrogate pairs in
\uXXXXescapes decode into a single character (e.g. emoji). - Rich errors:
json-parse-errorcarriesposition,line,column,path,expected,context, andtext;json-serialization-errorcarriesmessageandpath. - Writing:
stringify(to string) andwrite-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-memberspreserve member order and duplicate keys, plusalist->json-object/json-object->alist.
Full details in CHANGELOG.md.