cl-json-kit¶
cl-json-kit is a dependency-free JSON reader and writer for Common Lisp.
It provides string and character-stream APIs, explicit object/array mappings,
opaque values for JSON null and false, bounded resource use, and structured
diagnostics.
The core reader and writer use portable Common Lisp. On SBCL,
:timeout-seconds additionally uses sb-ext:with-timeout; other
implementations accept the option but parse without a wall-clock timeout.
Start with Installation and Quick Start, then move on to Reading JSON and Writing JSON for the full API surface.
-
Get started
Install with Nix or ASDF and parse your first document in minutes.
-
Learn the API
Reading, writing, conversion helpers, error handling, and limits.
-
Reference
Every exported symbol, its options, and the RFC 8259 scope.
-
Stable API
What 1.0 promises, what it deliberately does not, and how to report a break.
-
Benchmarks
Reproducible SBCL harnesses with full provenance in the output.
Why another JSON library?¶
The central rule is that JSON shape is never guessed from Lisp contents:
- A JSON object is a hash table or an alist, selected with
:object-type. - A JSON array is a vector or a list, selected with
:array-type. - When writing, hash tables are objects and vectors/lists are arrays.
- An alist becomes an object only after explicit conversion with
alist->json-object.
This avoids silently treating an array of pairs as an object — a class of ambiguity bug that has affected sibling JSON libraries. The reader also handles UTF-16 surrogate-pair escapes, rejects unpaired surrogates, reports an error path and source location, and applies configurable bounds before untrusted input can grow without limit.
Key features¶
- Explicit shape control. You decide whether objects become hash tables or alists and whether arrays become vectors or lists — the library never infers intent from a cons list's structure.
- Distinct
nullandfalse.+json-null+and+json-false+are opaque sentinels, kept distinguishable from Lispniland from each other. - Correct Unicode.
\uXXXXescapes decode UTF-16 surrogate pairs into a single non-BMP character (for example, emoji); lone surrogates are rejected. - Bounded by default. Every reader and writer entry point enforces finite size and depth limits suited to untrusted input.
- Structured diagnostics. Failures signal typed conditions carrying position, line, column, path, and a bounded snippet.
- No runtime dependencies. The runtime system depends on nothing beyond the
Common Lisp standard; only the test system uses
cl-weave. - Measured conformance. The full JSONTestSuite parsing corpus is vendored into the test suite and runs on every build — all 95 must-accept cases accepted, all 188 must-reject cases rejected, and every implementation-defined answer pinned. See RFC 8259 Scope.
- A stable API. From 1.0.0 on, the exported surface follows Semantic Versioning, and the export list itself is asserted by the test suite so it cannot change by accident.
At a glance¶
(defparameter *document*
(json-kit:parse
"{\"name\":\"Ada\",\"active\":false,\"note\":null,\"tags\":[\"a\",\"b\"]}"))
(gethash "name" *document*) ; => "Ada", T
(json-kit:json-false-p (gethash "active" *document*)) ; => T
(json-kit:json-null-p (gethash "note" *document*)) ; => T
(json-kit:parse "[1,2,3]" :array-type :list) ; => (1 2 3)
(json-kit:parse "\"\\ud83d\\ude00\"") ; => "😀"
(let ((table (make-hash-table :test #'equal)))
(setf (gethash "name" table) "Ada")
(json-kit:stringify table)) ; => "{\"name\":\"Ada\"}"
Lisp ↔ JSON mapping¶
| JSON | Default reader result | Writer input |
|---|---|---|
| object | hash table with string keys | hash table with string keys |
| array | vector | vector or proper list |
| string | string | string |
| integer | integer | integer |
| non-integer number | implementation float | finite float or exact-decimal ratio |
true |
t |
t |
false |
+json-false+ |
+json-false+ |
null |
+json-null+ |
+json-null+ |
See Data Model and Mapping for the full rules, including the
treatment of nil, ratios, and unsupported values.
Nix workflow¶
The flake.nix
at the repository root packages cl-json-kit as a Nix flake:
nix build— builds the ASDF system.nix flake check— runs the test suite as a reproducible derivation.nix develop— a devShell with SBCL and the competitor JSON libraries used by the benchmarks.nix build .#docs— builds this documentation site with MkDocs (Material).
License¶
MIT. See LICENSE.