A basic JSON parser
Beastie can output JSON in some cases, and for this or other reasons, it's sometimes useful to be able to parse JSON to Scheme s-expressions.
The goal here is to parse valid JSON correctly, as described in RFC 8259. The parser is strict, and will not try to recover from syntax errors. We pass all of the y_ and n_ tests at this test suite. We implement no extensions.
The only parsing wrinkle is that, since JSON arrays are mapped to s7 hash-tables with symbol keys, the JSON object {"": 0} (ie, with a zero-length key) would map to a hash-table entry with a zero-length symbol name. s7 manages to cope with that, but it's unexpected and inconvenient, so this is special-cased to a key of '_ instead. This choice also incidentally means that any duplicate keys in the JSON will be resolved into a single key in the hash-table, in an unspecified way.
We assume the input is UTF-8; this is required by the current RFC, but was not historically true of JSON.
Index:
(json-write! sexp) : write the sexp as JSON, to the (current-output-port).
Mapping to output:
#f, #t, and () are written as JSON false, true, and null, respectively.Because multiple Scheme structures are written to the small range of JSON types, it's not possible in general to round-trip structures via JSON.
(parse-json-file filename [:on-error func]) : parse the contents of a file containing JSON, and return as a sexp; if the filename is #f, then parse stdin.
On any parsing error, the function calls the on-error procedure, passing it an error message. The default procedure prints the message as a warning and returns #f. Since false is a legitimate JSON input, this also would produce #f as output: if it is necessary to distinguish an error from a successful parse of false, then a non-default on-error procedure can do so.
Numbers are reported as floats, strings as ustring?, lists as lists, objects as hash-tables, and false, true and null as #f, #t and () respectively.
See RFC 8259. This is a strict parser, and will object to any malformed input. We don't recognise any extensions, such as comments in the file.
(parse-json-string str [:on-error func]) : similar to parse-json-file.