"Callable interface used when traversing JSON data. It is the callers responsiblity to ensure the methods of this interface are called in a sequence that corresponds to well-formed JSON. For example, callers should never generate the calling sequence `onKey()`, `onKey()`." by("Tom Bentley") shared interface Visitor { "Called at the start of a new object. Further calls pertain to this new object until a corresponding call to [[onEndObject]]." shared formal void onStartObject(); "Called when encountering a key within a the current object." shared formal void onKey(String key); "Called at the end of an object." shared formal void onEndObject(); "Called at the start of a new array. Further calls pertain to this new object until a corresponding call to [[onEndArray]]." shared formal void onStartArray(); "Called at the end of an array." shared formal void onEndArray(); "Called when encountering a number." shared formal void onNumber(Integer|Float number); "Called when encountering true or false." shared formal void onBoolean(Boolean boolean); "Called when encountering a null." shared formal void onNull(); "Called when encountering a string." shared formal void onString(String string); } "Recursively visit the given subject using the given visitor. If [[sortedKeys]] is true then the keys of Objects will be visited in alphabetical order" by("Tom Bentley") shared void visit(subject, visitor, sortedKeys=false) { "The value to visit." Value subject; "The visitor to apply." Visitor visitor; "Whether keys should be visited in alphabetical order, when visiting objects." Boolean sortedKeys; switch(subject) case (is Object) { visitor.onStartObject(); value items = sortedKeys then subject else subject.sort(compareKeys); for (key->child in items) { visitor.onKey(key); visit(child, visitor); } visitor.onEndObject(); } case (is Array) { visitor.onStartArray(); for (element in subject) { visit(element, visitor); } visitor.onEndArray(); } case (is String) { visitor.onString(subject); } case (is Float|Integer) { visitor.onNumber(subject); } case (is Boolean) { visitor.onBoolean(subject); } case (is Null) { visitor.onNull(); } }