RFC 8259 Scope¶
This page states precisely what cl-json-kit accepts, what it rejects, and
what is explicitly out of scope.
Conformance results¶
Conformance is measured, not asserted. The whole parsing corpus of
JSONTestSuite — the reference suite
behind Parsing JSON is a
Minefield — is vendored into
t/rfc8259-conformance-test.lisp and runs on every CI build, so these numbers
are a regression gate rather than a one-off measurement:
| Corpus class | Requirement | Cases | Result |
|---|---|---|---|
y_ |
every conforming parser must accept | 95 | 95 accepted |
n_ |
every conforming parser must reject | 176 + 12 | all rejected |
i_ |
implementation-defined | 22 + 13 | each choice pinned by a test |
No case crashes, hangs, or signals anything other than json-parse-error; the
two pathological cases the corpus ships as 100 KB and 250 KB files of repeated
punctuation are rejected by a depth bound rather than by exhausting the control
stack.
The counts split because 25 cases (the + 12 and + 13 above) carry input that
is not well-formed UTF-8 — lone continuation bytes, overlong sequences, UTF-16
with and without a BOM, ISO-8859-1. This library's API consumes Common Lisp
characters rather than octets (see Characters, not
octets below), so those inputs are rejected by the
external-format layer before the reader sees them, and the vendored tests
exclude them by name rather than claim credit for them.
The implementation-defined cases are where parsers legitimately disagree —
deeply nested arrays, numbers outside double-float range, lone surrogates in
\uXXXX escapes. Each is listed in the test file together with this library's
choice, so a silent change in any of them fails the build.
What the reader accepts¶
The reader accepts one complete RFC 8259 JSON text:
- objects, arrays, strings, numbers,
true,false, andnull; - only JSON-defined whitespace;
- string escapes, including UTF-16 surrogate-pair combination for characters outside the Basic Multilingual Plane.
What the reader rejects¶
- unescaped control characters in strings;
- malformed escapes;
- lone (unpaired) surrogates;
- invalid number forms;
- a leading UTF-8 byte-order mark (
U+FEFF) — it is not JSON-defined whitespace, so it is rejected like any other unexpected character; strip it yourself first (see the FAQ and Recipes); - trailing data after the value (for
parse;parse-prefixandread-jsonintentionally stop at the value boundary).
Numbers¶
The built-in decoder maps ordinary non-integer numbers to double-float. When
float conversion overflows or underflows, it falls back to an exact integer or
ratio instead of rejecting an otherwise valid JSON number; for example, 1e400
remains an exact integer. max-exact-exponent bounds the absolute decimal
scale used by that fallback, preventing an untrusted exponent from requesting an
enormous expt computation.
Supply a number-decoder when the application
needs its own decimal type or a uniform number representation, and a
number-encoder to control formatting on output.
Characters, not octets¶
The API operates on Common Lisp characters, not octets. Encoding validity
and UTF-8 decoding errors therefore belong to the stream / external-format
layer — configure your stream before calling read-json or write-json.
The writer emits non-ASCII Lisp characters directly and escapes the required JSON characters; it does not promise ASCII-only output.
Non-goals¶
The following are explicitly out of scope:
- RFC 8785 canonicalization (canonical number formatting, byte-for-byte cross-implementation output);
- multiple-value stream framing beyond what
parse-prefix/read-jsonprovide; - comments;
- trailing commas;
- JSON5 and other extensions;
- arbitrary-precision decimal preservation without a custom
number-decoder/number-encoder; - an octet / UTF-8 API.
:sort-keys t therefore yields deterministic member ordering, not RFC 8785
canonical JSON. See Writing JSON.