Expand description
§jsonfix
Repair, extract, and partially parse malformed JSON — the JSON that language models emit, that logs contain, and that hand-written files slowly turn into.
Language models wrap JSON in prose and markdown fences, use single quotes,
leave trailing commas, and get cut off mid-string when a stream ends. Most
JSON parsers reject all of that. jsonfix accepts it, fixes it, and hands
back valid JSON — with zero dependencies, no unsafe, and no std.
§The five verbs
| Function | Use it for |
|---|---|
repair | turn broken JSON into a valid String |
extract | pull the JSON value out of prose or a fenced block |
parse / parse_partial | get a Value tree, complete or still streaming |
StreamRepairer | repair a document that arrives chunk by chunk |
validate | strict check with byte-precise errors |
use jsonfix::{extract, parse, repair, repair_extract};
let reply = "Sure! Here you go:\n```json\n{name: 'Ada', age: 36,}\n```";
// A chat reply has prose around the JSON: extract first, then repair.
assert_eq!(extract(reply), Some("{name: 'Ada', age: 36,}"));
assert_eq!(repair_extract(reply).unwrap(), r#"{"name": "Ada", "age": 36}"#);
// Without prose the same functions work on the bare document.
assert_eq!(repair("{name: 'Ada'}").unwrap(), r#"{"name": "Ada"}"#);
assert_eq!(parse("{age: 36}").unwrap().get("age").and_then(|v| v.as_i64()), Some(36));§Partial values while a stream is still being written
Allow decides what may be kept from a value that is not finished yet.
This is what a chat UI needs while the model is still writing.
use jsonfix::{parse_partial, Allow, Options};
let opts = Options::partial(Allow::OBJ | Allow::STR);
let value = parse_partial(r#"{"answer": "Hel"#, opts).unwrap();
assert_eq!(value.to_json_string(), r#"{"answer": "Hel"}"#);§Numbers keep their exact text
A number is never routed through f64, so 64-bit IDs, timestamps, and long
decimals come back byte for byte:
use jsonfix::repair;
assert_eq!(
repair("{id: 1234567890123456789, price: 1.10}").unwrap(),
r#"{"id": 1234567890123456789, "price": 1.10}"#
);§Choosing how much gets repaired
Every repair pass has its own flag in Repairs, and Options::strict
turns the crate into a plain validator:
use jsonfix::{validate, Options, Repairs, repair_with};
// Fences and comments only: a bare word stays an error.
let opts = Options::all().with_repairs(Repairs::FENCES | Repairs::COMMENTS);
assert_eq!(repair_with("```json\n{\"a\": 1} // done\n```", opts).unwrap(), r#"{"a": 1}"#);
assert!(repair_with("{a: 1}", opts).is_err());
assert!(validate(r#"{"a": 1}"#).is_ok());
assert!(validate("{a: 1}").is_err());§What gets repaired
- markdown fences, surrounding prose, and JSONP wrappers (
cb({...})) //and/* */comments, trailing commas, and missing commas- single quotes and typographic quotes (
“...”,‘...’) - unquoted keys and values,
True/False/None,undefined NaN/Infinity(kept as the strings"NaN"/"Infinity"), leading zeros (kept as a string),.5,2.,2e, and a lone-- missing escapes,
\x41escapes, HTML entities ("),"a" + "b" NumberLong(2)/ISODate("...")wrappers and[1, 2, ...]ellipses- truncated documents: open brackets and strings are closed
- several top-level values (NDJSON) become one array
The behaviour follows the widely used JavaScript jsonrepair for the cases
it defines, expressed here as a tolerant lexer plus a lenient parser.
§No standard library required
The crate is #![no_std] (it needs alloc only for the values it builds)
and forbids unsafe, which matters when the input comes from an untrusted
model or a hostile log line.
Structs§
- Allow
- Which incomplete constructs a partial parse is allowed to keep.
- Delta
- What changed between two consecutive repaired outputs.
- Error
- A repair or parse failure, carrying the byte offset where it was detected.
- Number
- A JSON number kept as its original (normalized) text.
- Options
- How
repair_withand friends behave. - Repairs
- Which repair passes run. Construct one with
|or start from a preset. - Stream
Repairer - Repairs a document that is revealed one chunk at a time.
Enums§
- Deserialize
Error serde_json - Why
deserializefailed: repair failure or a type mismatch. - Error
Kind - What went wrong while repairing or parsing.
- Value
- A repaired, parsed JSON value.
- Write
Error std - Why
repair_to_writerfailed: the repair itself, or the sink.
Constants§
- MAX_
NESTING_ DEPTH - Maximum container/group nesting depth accepted by the parser.
Functions§
- deserialize
serde_json - Repairs
inputand deserializes it intoT(requires theserde_jsonfeature). - deserialize_
with serde_json - Like
deserialize, with explicitOptions. - extract
- Returns the first JSON value found in
input, trimmed of surrounding prose. - extract_
all - Returns every top-level value in
input, in order (NDJSON and log lines). - extract_
partial - Returns everything from the first JSON value to the end of the input.
- from_
value serde - Deserializes any serde data model from a
Value, consuming the tree. - loads
serde_json - Repairs
inputand returns aserde_json::Valuein one call. - loads_
with serde_json - Like
loads, with explicitOptions. - parse
- Parses
inputinto aValue, repairing anything repairable. - parse_
partial - Like
parse_with, named for the partial-parsing use case. - parse_
with - Like
parse, with explicitOptions. - repair
- Repairs
inputand returns canonical, valid JSON. - repair_
bytes - Byte-oriented
repair: repairsinputbytes and returns the repaired document as bytes. - repair_
bytes_ into - Like
repair_bytes_with, appending to a caller-provided byte buffer. - repair_
bytes_ with - Like
repair_bytes, with explicitOptions. - repair_
extract - Extracts the first JSON value from
input(prose, fences, logs) and repairs it. - repair_
into - Like
repair_with, appending to a caller-provided buffer. - repair_
to_ writer std - Repairs
inputand writes canonical JSON tosink(requires thestdfeature). - repair_
with - Like
repair, with explicitOptions. - validate
- Validates that
inputis valid JSON and returns it as aValue.