///|
pub suberror JsonImportError {
InvalidJsonDocument(String)
} derive(Debug)
///|
pub extend JsonImportError with @debug.Debug::{to_repr}
///|
/// Decode the ordered [{key,value}] representation emitted by to_json.
pub fn document_from_json(
value : Json,
options? : ParseOptions = ParseOptions::default(),
) -> Document raise JsonImportError {
if options.max_depth < 0 ||
options.max_depth > 256 ||
options.max_entries < 0 ||
options.max_token_chars < 0 {
raise InvalidJsonDocument("Invalid resource limits")
}
let count = Ref(0)
{ entries: import_entries(value, options, 0, count), }
}
///|
pub fn parse_json_document(
text : String,
options? : ParseOptions = ParseOptions::default(),
) -> Document raise JsonImportError {
if options.max_input_chars < 0 || text.char_length() > options.max_input_chars {
raise InvalidJsonDocument("JSON input exceeds max_input_chars")
}
let value = @json.parse(text[:]) catch {
_ => raise InvalidJsonDocument("Invalid JSON syntax or excessive nesting")
}
document_from_json(value, options~)
}
///|
fn import_entries(
value : Json,
options : ParseOptions,
depth : Int,
count : Ref[Int],
) -> Array[Entry] raise JsonImportError {
if depth > options.max_depth {
raise InvalidJsonDocument("JSON document exceeds max_depth")
}
guard value is Array(items) else {
raise InvalidJsonDocument("An object must be an ordered entry array")
}
let entries : Array[Entry] = []
for item in items {
if count.val >= options.max_entries {
raise InvalidJsonDocument("JSON document exceeds max_entries")
}
count.val = count.val + 1
guard item is Object(fields) else {
raise InvalidJsonDocument("Each entry must be an object")
}
for key, _ in fields {
if key != "key" && key != "value" {
raise InvalidJsonDocument("Unknown entry field: " + key)
}
}
guard fields.get("key") is Some(String(key)) else {
raise InvalidJsonDocument("Entry key must be a string")
}
guard fields.get("value") is Some(raw) else {
raise InvalidJsonDocument("Entry value is required")
}
check_import_text(key, options)
let value = match raw {
String(text) => {
check_import_text(text, options)
Value::Text(text)
}
Array(_) => Value::Object(import_entries(raw, options, depth + 1, count))
_ =>
raise InvalidJsonDocument(
"Entry value must be a string or an entry array",
)
}
entries.push({ key, value, })
}
entries
}
///|
fn check_import_text(
text : String,
options : ParseOptions,
) -> Unit raise JsonImportError {
if text.char_length() > options.max_token_chars {
raise InvalidJsonDocument("JSON string exceeds max_token_chars")
}
if text.any(c => c.to_int() == 0) {
raise InvalidJsonDocument("NUL cannot appear in KV1 strings")
}
}