Skip to main content

Crate jsonfix

Crate jsonfix 

Source
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

FunctionUse it for
repairturn broken JSON into a valid String
extractpull the JSON value out of prose or a fenced block
parse / parse_partialget a Value tree, complete or still streaming
StreamRepairerrepair a document that arrives chunk by chunk
validatestrict 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, \x41 escapes, 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_with and friends behave.
Repairs
Which repair passes run. Construct one with | or start from a preset.
StreamRepairer
Repairs a document that is revealed one chunk at a time.

Enums§

DeserializeErrorserde_json
Why deserialize failed: repair failure or a type mismatch.
ErrorKind
What went wrong while repairing or parsing.
Value
A repaired, parsed JSON value.
WriteErrorstd
Why repair_to_writer failed: the repair itself, or the sink.

Constants§

MAX_NESTING_DEPTH
Maximum container/group nesting depth accepted by the parser.

Functions§

deserializeserde_json
Repairs input and deserializes it into T (requires the serde_json feature).
deserialize_withserde_json
Like deserialize, with explicit Options.
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_valueserde
Deserializes any serde data model from a Value, consuming the tree.
loadsserde_json
Repairs input and returns a serde_json::Value in one call.
loads_withserde_json
Like loads, with explicit Options.
parse
Parses input into a Value, repairing anything repairable.
parse_partial
Like parse_with, named for the partial-parsing use case.
parse_with
Like parse, with explicit Options.
repair
Repairs input and returns canonical, valid JSON.
repair_bytes
Byte-oriented repair: repairs input bytes 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 explicit Options.
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_writerstd
Repairs input and writes canonical JSON to sink (requires the std feature).
repair_with
Like repair, with explicit Options.
validate
Validates that input is valid JSON and returns it as a Value.