Conformance matrix
How 27 parser runs treat the same files in JSON5, JSONC, JWCC (HuJSON), JSON Lines and strict JSON: 18 other parsers, plus jsonvet in each dialect and the duplicate-key variants. Each dialect's table lists the parsers that claim that dialect. Run on 2026-11-10; a full run took 108 s. How it works and how to rerun it: conformance/README.md.
Cells: ✓ the parser agrees with the expected verdict, ✗ it disagrees, ‼ it crashed or
took longer than 10 s. Where the spec allows both verdicts (expected: either), the cell
shows what the parser did: A accepted, R rejected. Duplicate keys count as valid in the
syntax tables (jsonvet runs with --ignore duplicate-key); they have their own table.
Expected verdicts come from the spec where it decides the case (marked †, with the section under the table), from the json5-tests file extension, from conformance/extra/, or else from jsonvet's reviewed corpus snapshot. Where jsonvet disagrees with a spec, its column shows ✗ like any other parser.
JSON5
Spec: https://spec.json5.org/. 171 files, 166 scored.
| parser | agrees | disagrees | crash or timeout |
|---|---|---|---|
js-json5 |
166 (100%) | 0 | 0 |
py-json5 |
165 (99%) | 1 | 2 |
py-pyjson5 |
165 (99%) | 1 | 0 |
go-json5 |
151 (91%) | 15 | 0 |
rs-json5 |
166 (100%) | 0 | 0 |
rs-serde-json5 |
156 (94%) | 10 | 0 |
jsonvet-json5 |
166 (100%) | 0 | 0 |
Files where at least one parser disagrees (26):
Files where every parser agrees (145)
Spec notes (†):
- corpus/json5/bom.json: JSON5 1.0.0 §8: U+FEFF is white space.
- corpus/json5/combining-mark-in-key.json: JSON5 1.0.0 §3 and ECMAScript 5.1 §7.6: a combining mark may continue a key.
- corpus/json5/escape-letter.json: JSON5 1.0.0 §5.1: an unknown escape keeps the letter and drops the backslash.
- corpus/json5/escaped-newline-in-string.json: JSON5 1.0.0 §5.1: a backslash before a line break continues it.
- corpus/json5/hex-and-friends.json: JSON5 1.0.0 §6: hex, bare decimal points, plus signs, Infinity, NaN.
- corpus/json5/letter-u0e33-starts-key.json: ECMAScript 5.1 §7.6: U+0E33 is a letter in every Unicode version.
- corpus/json5/line-comment-ends-at-u2028-clean.json: JSON5 1.0.0 §7: a line comment ends at a LineTerminator, and U+2028 is one.
- corpus/json5/negative-hex.json: JSON5 1.0.0 §6: hex numbers may be signed.
- corpus/json5/raw-u2028-in-string.json: JSON5 1.0.0 §5.2: U+2028 may appear raw in a string (the grammar in §9.1 says otherwise).
- corpus/json5/top-level-scalar.json: JSON5 1.0.0 §2: a JSON5 text is any value.
- corpus/json5/combining-mark-starts-key-escaped.json: ECMAScript 5.1 §7.6: an escape in a key must still be a character allowed there.
- corpus/json5/combining-mark-starts-key.json: JSON5 1.0.0 §3 and ECMAScript 5.1 §7.6: a combining mark cannot start a key.
- corpus/json5/double-comma-array.json: JSON5 1.0.0 §4: a single comma may follow the final element.
- corpus/json5/empty.json: JSON5 1.0.0 §9.2: a JSON5 text is exactly one value.
- corpus/json5/escape-nine.json: JSON5 1.0.0 §9.1: escapes of the digits 1 to 9 are not in the grammar.
- corpus/json5/key-with-dash.json: JSON5 1.0.0 §3: an unquoted key must be an IdentifierName.
- corpus/json5/line-comment-ends-at-u2028.json: JSON5 1.0.0 §7: the comment ends at U+2028, so x is left over.
- corpus/json5/lone-comma-array.json: JSON5 1.0.0 §4: a single comma may follow the final element.
- corpus/json5/lone-comma-object.json: JSON5 1.0.0 §3: a single comma may follow the final member.
- corpus/json5/number-led-key.json: JSON5 1.0.0 §3: an unquoted key must be an IdentifierName.
- corpus/json5/punctuation-u00b7-in-key.json: ECMAScript 5.1 §7.6: U+00B7 is punctuation, not allowed in a key (ECMAScript 2015 later allowed it).
- corpus/json5/raw-newline-in-string.json: JSON5 1.0.0 §5: a string cannot hold a raw line break.
- corpus/json5/unclosed-comment-own-star-slash.json: JSON5 1.0.0 §7: the */ cannot reuse the opening *.
- corpus/json5/unclosed-comment.json: JSON5 1.0.0 §7: a block comment ends with */.
- corpus/json5/whitespace-only.json: JSON5 1.0.0 §9.2: a JSON5 text is exactly one value.
- corpus/json5/depth-10000.json: JSON5 1.0.0 §10: a parser may limit the depth of nesting.
- corpus/json5/depth-10001.json: JSON5 1.0.0 §10: a parser may limit the depth of nesting.
- corpus/json5/integer-5000-digits.json: JSON5 1.0.0 §10: a parser may limit the range of numbers.
- corpus/json5/mark-u1885-starts-key.json: ECMAScript 5.1 §7.6 allows any Unicode version from 3.0: U+1885 was a letter until Unicode 9.0, a mark since.
- corpus/json5/not-utf8.json: JSON5 1.0.0 §9.1 works on Unicode characters and names no byte encoding.
Notable disagreements:
- corpus/json5/bom.json: Go
titanous/json5rejects a leading byte order mark, which §8 lists as white space. - corpus/json5/escape-letter.json: Go
titanous/json5rejects\a, which §5.1 says is a plaina. - corpus/json5/hex-and-friends.json: Go
titanous/json5rejects-NaN, and Rustserde_json5rejects+Infinity; §6 allows a sign on both. - corpus/json5/letter-u0e33-starts-key.json: Go
titanous/json5takes only ASCII letters, digits,_and$in unquoted keys, with no escapes, so it also rejects the keys in other rows that hold accents, combining marks or\uescapes. - corpus/json5/negative-hex.json: Rust
serde_json5rejects a sign on a hex number,-0x10and+0x1Falike. - corpus/json5/raw-u2028-in-string.json: Rust
serde_json5rejects a raw U+2028 in a string. §5.2 allows it but the grammar in §9.1 does not, so the spec shares the blame. - corpus/json5/combining-mark-starts-key-escaped.json: Python
json5,pyjson5and Rustserde_json5accept an escaped combining mark at the start of a key; ECMAScript 5.1 §7.6 does not allow one there. - corpus/json5/line-comment-ends-at-u2028.json: Go
titanous/json5does not end a line comment at U+2028, so the strayxafter it disappears into the comment. - corpus/json5/punctuation-u00b7-in-key.json: Rust
serde_json5accepts a middle dot (U+00B7) in a key. ECMAScript 2015 allows it; ECMAScript 5.1, which JSON5 cites, does not. - corpus/json5/unclosed-comment.json: Go
titanous/json5accepts a block comment that never closes at the end of the file (also unclosed-comment-own-star-slash.json and json5-tests unterminated-block-comment.txt). - corpus/json5/depth-10000.json: Python
json5raises RecursionError. Rustserde_json5overflowed its stack in the 2026-10-14 run and parsed the file in the 2026-10-19 run, with the same version, compiler and 8 MB stack, so its verdict here is not stable.pyjson5rejects the file at its own nesting limit, which §10 allows.
JSONC
Spec: https://jsonc.org/. 11 files, 9 scored.
| parser | agrees | disagrees | crash or timeout |
|---|---|---|---|
js-jsonc-parser |
9 (100%) | 0 | 0 |
js-strip-json-comments |
8 (89%) | 1 | 0 |
go-tidwall-jsonc |
8 (89%) | 1 | 0 |
rs-jsonc-parser |
7 (78%) | 2 | 0 |
jsonvet-jsonc |
9 (100%) | 0 | 0 |
Files where at least one parser disagrees (3):
| file | expected | js-jsonc-parser |
js-strip-json-comments |
go-tidwall-jsonc |
rs-jsonc-parser |
jsonvet-jsonc |
|---|---|---|---|---|---|---|
| extra/jsonc/lone-cr-ends-comment.jsonc | valid † | ✓ | ✗ | ✗ | ✓ | ✓ |
| extra/jsonc/comment-only.jsonc | invalid † | ✓ | ✓ | ✓ | ✗ | ✓ |
| extra/jsonc/json5-syntax.jsonc | invalid † | ✓ | ✓ | ✓ | ✗ | ✓ |
Files where every parser agrees (8)
| file | expected | js-jsonc-parser |
js-strip-json-comments |
go-tidwall-jsonc |
rs-jsonc-parser |
jsonvet-jsonc |
|---|---|---|---|---|---|---|
| extra/jsonc/line-comment-at-end.jsonc | valid † | ✓ | ✓ | ✓ | ✓ | ✓ |
| corpus/jsonc/comments.json | valid | ✓ | ✓ | ✓ | ✓ | ✓ |
| extra/jsonc/block-comment-unclosed.jsonc | invalid † | ✓ | ✓ | ✓ | ✓ | ✓ |
| extra/jsonc/hash-comment.jsonc | invalid † | ✓ | ✓ | ✓ | ✓ | ✓ |
| extra/jsonc/nested-block-comment.jsonc | invalid † | ✓ | ✓ | ✓ | ✓ | ✓ |
| corpus/jsonc/line-comment-not-ended-by-u2028.json | invalid † | ✓ | ✓ | ✓ | ✓ | ✓ |
| extra/jsonc/trailing-comma-array.jsonc | either † | R | R | A | A | R |
| corpus/jsonc/trailing-comma.json | either † | R | R | A | A | R |
Spec notes (†):
- extra/jsonc/line-comment-at-end.jsonc: Syntax, single-line comments: a line comment may run to the end of the file.
- extra/jsonc/lone-cr-ends-comment.jsonc: Conventions: a lone CR is a line ending, so it ends a line comment.
- extra/jsonc/block-comment-unclosed.jsonc: Syntax, multi-line comments: a block comment ends with */.
- extra/jsonc/comment-only.jsonc: Comments sit where JSON allows whitespace; JSON needs a value.
- extra/jsonc/hash-comment.jsonc: Appendix B: comments that start with # are not supported.
- extra/jsonc/json5-syntax.jsonc: Beyond JSON, JSONC adds only comments (and optional trailing commas): no unquoted keys, single quotes or hex.
- extra/jsonc/nested-block-comment.jsonc: Syntax, multi-line comments: they cannot nest, so the first */ ends the comment.
- corpus/jsonc/line-comment-not-ended-by-u2028.json: JSONC, Conventions: a line ending is LF, CRLF or CR, so U+2028 does not end the comment.
- extra/jsonc/trailing-comma-array.jsonc: Syntax, trailing commas: parsers MAY support them.
- corpus/jsonc/trailing-comma.json: JSONC, Trailing commas: parsers MAY support them.
Notable disagreements:
- extra/jsonc/lone-cr-ends-comment.jsonc:
strip-json-commentsand Gotidwall/jsoncend a line comment only at LF, so a comment ended by a lone CR swallows the rest of the file. - extra/jsonc/comment-only.jsonc: The Rust
jsonc-parserreturns no value and no error for a file holding only a comment; the driver counts that as an accept. - extra/jsonc/json5-syntax.jsonc: The Rust
jsonc-parseraccepts unquoted keys, single quotes and hex by default; it is lenient by design.
JWCC (HuJSON)
Spec: https://nigeltao.github.io/blog/2021/json-with-commas-comments.html. 11 files, 10 scored.
| parser | agrees | disagrees | crash or timeout |
|---|---|---|---|
go-hujson |
9 (90%) | 1 | 0 |
jsonvet-jwcc |
10 (100%) | 0 | 0 |
Files where at least one parser disagrees (1):
| file | expected | go-hujson |
jsonvet-jwcc |
|---|---|---|---|
| extra/jwcc/line-comment-at-end.json | valid † | ✗ | ✓ |
Files where every parser agrees (10)
| file | expected | go-hujson |
jsonvet-jwcc |
|---|---|---|---|
| extra/jwcc/trailing-comma-array.json | valid † | ✓ | ✓ |
| corpus/jwcc/trailing-comma-and-comment.json | valid † | ✓ | ✓ |
| extra/jwcc/cr-does-not-end-comment.json | invalid † | ✓ | ✓ |
| extra/jwcc/hash-comment.json | invalid † | ✓ | ✓ |
| extra/jwcc/hex-number.json | invalid † | ✓ | ✓ |
| extra/jwcc/lone-comma-object.json | invalid † | ✓ | ✓ |
| extra/jwcc/two-trailing-commas.json | invalid † | ✓ | ✓ |
| extra/jwcc/unquoted-key.json | invalid † | ✓ | ✓ |
| corpus/jwcc/single-quotes.json | invalid † | ✓ | ✓ |
| extra/jwcc/u2028-in-line-comment.json | either † | R | A |
Spec notes (†):
- extra/jwcc/line-comment-at-end.json: Update of 2021-07-20: a final line comment needs no newline.
- extra/jwcc/trailing-comma-array.json: One optional comma after the final element of an array.
- corpus/jwcc/trailing-comma-and-comment.json: JWCC: one comma after the final element, and comments.
- extra/jwcc/cr-does-not-end-comment.json: Line comments end at \n; \r bytes are irrelevant, so ] is inside the comment.
- extra/jwcc/hash-comment.json: # comments are not valid JavaScript, so not JWCC.
- extra/jwcc/hex-number.json: JWCC adds only comments and trailing commas to JSON.
- extra/jwcc/lone-comma-object.json: The comma follows a final element; an empty object has none.
- extra/jwcc/two-trailing-commas.json: Only one comma may follow the final element.
- extra/jwcc/unquoted-key.json: Unquoted strings are not part of JWCC.
- corpus/jwcc/single-quotes.json: JWCC adds only comments and trailing commas; no single quotes.
- extra/jwcc/u2028-in-line-comment.json: Only \n ends a line comment, so b is comment text; but JWCC is meant to be valid JavaScript, where U+2028 ends it.
Notable disagreements:
- extra/jwcc/line-comment-at-end.json:
hujson, the reference implementation, rejects a line comment that ends the file without a newline. The spec allows that since its 2021-07-20 update (reported as tailscale/hujson#56).
JSON Lines
Spec: https://jsonlines.org/. 11 files, 11 scored.
| parser | agrees | disagrees | crash or timeout |
|---|---|---|---|
py-jsonlines |
9 (82%) | 2 | 0 |
go-json-stream |
7 (64%) | 4 | 0 |
rs-serde-json-stream |
7 (64%) | 4 | 0 |
jsonvet-jsonl |
11 (100%) | 0 | 0 |
Files where at least one parser disagrees (6):
| file | expected | py-jsonlines |
go-json-stream |
rs-serde-json-stream |
jsonvet-jsonl |
|---|---|---|---|---|---|
| extra/jsonl/scalars.jsonl | valid † | ✗ | ✓ | ✓ | ✓ |
| extra/jsonl/bom.jsonl | invalid † | ✗ | ✓ | ✓ | ✓ |
| extra/jsonl/trailing-blank-line.jsonl | invalid † | ✓ | ✗ | ✗ | ✓ |
| corpus/jsonl/blank-line.jsonl | invalid † | ✓ | ✗ | ✗ | ✓ |
| corpus/jsonl/two-values-on-a-line.jsonl | invalid † | ✓ | ✗ | ✗ | ✓ |
| corpus/jsonl/value-across-lines.jsonl | invalid † | ✓ | ✗ | ✗ | ✓ |
Files where every parser agrees (5)
| file | expected | py-jsonlines |
go-json-stream |
rs-serde-json-stream |
jsonvet-jsonl |
|---|---|---|---|---|---|
| extra/jsonl/crlf.jsonl | valid † | ✓ | ✓ | ✓ | ✓ |
| extra/jsonl/padded-line.jsonl | valid † | ✓ | ✓ | ✓ | ✓ |
| corpus/jsonl/crlf-duplicate.jsonl | valid † | ✓ | ✓ | ✓ | ✓ |
| corpus/jsonl/no-final-newline.jsonl | valid † | ✓ | ✓ | ✓ | ✓ |
| corpus/jsonl/valid.jsonl | valid | ✓ | ✓ | ✓ | ✓ |
Spec notes (†):
- extra/jsonl/crlf.jsonl: Rule 3: \r\n works because whitespace around a value is ignored.
- extra/jsonl/padded-line.jsonl: Rule 3: whitespace around a value is ignored.
- extra/jsonl/scalars.jsonl: Rule 2: any JSON value is permitted on a line.
- corpus/jsonl/crlf-duplicate.jsonl: JSON Lines rule 3: \r\n works, as white space around a value.
- corpus/jsonl/no-final-newline.jsonl: JSON Lines rule 3: a final line terminator is not required.
- extra/jsonl/bom.jsonl: Rule 1: a byte order mark must NOT be included.
- extra/jsonl/trailing-blank-line.jsonl: Rule 3: a final line terminator must be the last byte of the file.
- corpus/jsonl/blank-line.jsonl: JSON Lines rule 2: a blank line is not a value.
- corpus/jsonl/two-values-on-a-line.jsonl: JSON Lines rule 2: each line is one JSON value.
- corpus/jsonl/value-across-lines.jsonl: JSON Lines rule 2: each line is one JSON value.
Notable disagreements:
- extra/jsonl/scalars.jsonl: Python
jsonlinesrejects anullline unless you passallow_none=True; the spec permits any JSON value. - extra/jsonl/bom.jsonl: Python
jsonlinesaccepts a leading byte order mark, which the spec forbids. - corpus/jsonl/blank-line.jsonl: Go's
json.Decoderand serde_json'sStreamDeserializerread values split by any white space, not by lines. They are how those languages usually read JSON Lines, so blank lines, two values on a line and a value across lines all pass.
Strict JSON (baseline)
Spec: https://www.rfc-editor.org/rfc/rfc8259. 131 files, 129 scored.
| parser | agrees | disagrees | crash or timeout |
|---|---|---|---|
js-json-parse |
129 (100%) | 0 | 0 |
py-json |
124 (96%) | 5 | 0 |
py-jiter |
122 (95%) | 7 | 0 |
rs-serde-json |
127 (98%) | 2 | 0 |
jsonvet-json |
129 (100%) | 0 | 0 |
Files where at least one parser disagrees (7):
| file | expected | js-json-parse |
py-json |
py-jiter |
rs-serde-json |
jsonvet-json |
|---|---|---|---|---|---|---|
| corpus/i-json/lone-surrogate.json | valid † | ✓ | ✓ | ✗ | ✗ | ✓ |
| corpus/json/lone-surrogates-are-distinct-keys.json | valid † | ✓ | ✓ | ✗ | ✗ | ✓ |
| corpus/json/bare-infinity.json | invalid | ✓ | ✗ | ✗ | ✓ | ✓ |
| corpus/json/nan-and-infinity.json | invalid | ✓ | ✗ | ✗ | ✓ | ✓ |
| json5-tests/numbers/infinity.json5 | invalid | ✓ | ✗ | ✗ | ✓ | ✓ |
| json5-tests/numbers/nan.json5 | invalid | ✓ | ✗ | ✗ | ✓ | ✓ |
| json5-tests/numbers/negative-infinity.json5 | invalid | ✓ | ✗ | ✗ | ✓ | ✓ |
Files where every parser agrees (124)
Spec notes (†):
- corpus/i-json/duplicate-key.json: RFC 8259 §4: names SHOULD be unique, but repeats are valid syntax.
- corpus/i-json/lone-surrogate.json: RFC 8259 §8.2: the grammar allows a lone surrogate escape.
- corpus/i-json/unsafe-integer.json: RFC 8259 §6: the grammar has no range limit (I-JSON has one).
- corpus/json/duplicate-key.json: RFC 8259 §4: names SHOULD be unique, but repeats are valid syntax.
- corpus/json/lone-surrogates-are-distinct-keys.json: RFC 8259 §8.2: the grammar allows lone surrogates.
- corpus/json/leading-zero.json: RFC 8259 §6: a number cannot have a leading zero.
- json5-tests/comments/irregular-block-comment.json: json5/json5-tests#4: named .json but has a block comment, so it is not JSON.
- corpus/i-json/bom.json: RFC 8259 §8.1: a parser MAY ignore a byte order mark.
- corpus/json/bom.json: RFC 8259 §8.1: a parser MAY ignore a byte order mark.
Notable disagreements:
- corpus/i-json/lone-surrogate.json: serde_json rejects an escaped lone surrogate when it builds the string, though the RFC 8259 grammar allows one (§8.2).
- corpus/json/nan-and-infinity.json: Python's
jsonaccepts NaN, Infinity and -Infinity by default; RFC 8259 §6 has no such numbers.
Duplicate keys
Every spec here lets a parser keep one of the values, and JSON5 §3 also lets it fail. Only four
parsers in the matrix can report duplicates: Python's json through an object_pairs_hook
that raises, Python json5 with allow_duplicate_keys=False, jiter with
catch_duplicate_keys=True, and jsonvet by default. The
others have no switch for it in their sources, and accepted these files wherever their syntax
allowed. A dash means the parser rejects the file's syntax, so the test says nothing.
Versions
| parser | language | package | version |
|---|---|---|---|
js-json5 |
node | json5 | 2.2.3 |
js-jsonc-parser |
node | jsonc-parser | 3.3.1 |
js-strip-json-comments |
node | strip-json-comments | 5.0.3 |
js-json-parse |
node | - | built in (node v26.12.0) |
py-json |
python | - | built in (python 3.14.8) |
py-json5 |
python | json5 | 0.17.3 |
py-pyjson5 |
python | pyjson5 | 2.0.1 |
py-jsonlines |
python | jsonlines | 4.0.0 |
py-jiter |
python | jiter | 0.17.0 |
py-json-dupes |
python | - | built in (python 3.14.8) |
py-json5-dupes |
python | json5 | 0.17.3 |
py-jiter-dupes |
python | jiter | 0.17.0 |
go-hujson |
go | github.com/tailscale/hujson | v0.0.0-20260727124030-b80ff77dac4f |
go-json5 |
go | github.com/titanous/json5 | v1.0.0 |
go-tidwall-jsonc |
go | github.com/tidwall/jsonc | v0.3.3 |
go-json-stream |
go | - | built in (go go1.27.3) |
rs-serde-json |
rust | serde_json | 1.0.154 |
rs-serde-json-stream |
rust | serde_json | 1.0.154 |
rs-json5 |
rust | json5 | 1.3.2 |
rs-serde-json5 |
rust | serde_json5 | 0.2.1 |
rs-jsonc-parser |
rust | jsonc-parser | 0.34.0 |
jsonvet-json |
jsonvet | jsonvet | 3.4.0 (51501e0) |
jsonvet-json5 |
jsonvet | jsonvet | 3.4.0 (51501e0) |
jsonvet-jsonc |
jsonvet | jsonvet | 3.4.0 (51501e0) |
jsonvet-jwcc |
jsonvet | jsonvet | 3.4.0 (51501e0) |
jsonvet-jsonl |
jsonvet | jsonvet | 3.4.0 (51501e0) |
jsonvet-dupes |
jsonvet | jsonvet | 3.4.0 (51501e0) |
Images: node:26-slim@sha256:e556a144463cc8dc53afe8de6d2fa0db5c132fd39ffa232c17641bd40df2bf11, python:3.14-slim@sha256:fbecd461fc72d21b2143c6eaba25143738079154a6d0ea3da3aec724f50eed4a, golang:1.27@sha256:f24164268fc1e741bbdc0066dbadcf1c8426c5a8489f0729c02c3905a53032fd, rust:1@sha256:767c71592f7d1f3f242292000f8efd4e67b6732363da176772f4d220332e264f. Runtimes: node v26.12.0; python 3.14.8; go go1.27.3; rust 1.99.1; jsonvet 3.4.0 (51501e0).