///|
/// Structural events emitted in document order for streaming-style consumers.
pub enum JsonEvent {
BeginObject(String, Int)
EndObject(String)
BeginArray(String, Int)
EndArray(String)
Scalar(String, String, String)
}
///|
pub fn walk_events(value : JsonValue) -> Array[JsonEvent] {
let events : Array[JsonEvent] = []
walk_value(value, "", events)
events
}
///|
pub fn JsonEvent::to_text(self : JsonEvent) -> String {
match self {
BeginObject(path, count) =>
"begin-object " + path + " (" + count.to_string() + ")"
EndObject(path) => "end-object " + path
BeginArray(path, count) =>
"begin-array " + path + " (" + count.to_string() + ")"
EndArray(path) => "end-array " + path
Scalar(path, kind, value) => "scalar " + path + " " + kind + " " + value
}
}
///|
pub fn JsonEvent::to_json(self : JsonEvent) -> String {
match self {
BeginObject(path, count) =>
"{\"event\":\"begin-object\",\"path\":" +
canonical_string(path) +
",\"count\":" +
count.to_string() +
"}"
EndObject(path) =>
"{\"event\":\"end-object\",\"path\":" + canonical_string(path) + "}"
BeginArray(path, count) =>
"{\"event\":\"begin-array\",\"path\":" +
canonical_string(path) +
",\"count\":" +
count.to_string() +
"}"
EndArray(path) =>
"{\"event\":\"end-array\",\"path\":" + canonical_string(path) + "}"
Scalar(path, kind, value) =>
"{\"event\":\"scalar\",\"path\":" +
canonical_string(path) +
",\"kind\":" +
canonical_string(kind) +
",\"value\":" +
value +
"}"
}
}
///|
pub fn events_text(events : Array[JsonEvent]) -> String {
let mut output = ""
for event in events {
if output != "" {
output = output + "\n"
}
output = output + event.to_text()
}
output
}
///|
fn walk_value(
value : JsonValue,
path : String,
events : Array[JsonEvent],
) -> Unit {
match value {
Null => events.push(Scalar(path, "null", "null"))
Bool(flag) =>
events.push(Scalar(path, "boolean", if flag { "true" } else { "false" }))
Number(number) =>
events.push(Scalar(path, "number", value_preview(Number(number))))
String(string) =>
events.push(Scalar(path, "string", canonical_string(string)))
Array(values) => {
events.push(BeginArray(path, values.length()))
let mut index = 0
while index < values.length() {
walk_value(values[index], path + "/" + index.to_string(), events)
index = index + 1
}
events.push(EndArray(path))
}
Object(entries) => {
events.push(BeginObject(path, entries.length()))
for entry in entries {
walk_value(entry.1, path + "/" + pointer_escape_local(entry.0), events)
}
events.push(EndObject(path))
}
}
}