JsonBuilder
A streaming writer that tracks just enough structure to place commas.
JsonBuilder writes JSON incrementally. It tracks enough state to place commas correctly; balancing begin/end pairs is the caller's job.
var body = new JsonBuilder();
body.beginObject();
body.field("status", "ok"); // string, int, bool and real overloads
body.key("items");
body.beginArray();
body.value("first");
body.endArray();
body.rawValue(alreadySerialised); // the caller owns validity
body.endObject();
return jsonResponse(body.done());Why not string concatenation
Two reasons, and both are correctness rather than taste.
Escaping. Strings are escaped on the way in, including U+2028 and U+2029 โ literal line terminators that are legal in JSON but break a <script> block when JSON is inlined into a document. <, > and & are escaped as \u003c, \u003e and \u0026 for the same reason.
Numbers. NaN and the infinities are not valid JSON. They serialise as null rather than producing a document that no parser accepts.
A typed result, serialised strictly
Define a record, fill it, then serialise it in one place:
record SearchResult {
var title: string;
var path: string;
var excerpt: string;
var kind: string;
var score: int;
}body.key("results");
body.beginArray();
for hit in hits {
body.beginObject();
body.field("title", hit.title);
body.field("path", hit.path);
body.field("excerpt", hit.excerpt);
body.field("kind", hit.kind);
body.field("score", hit.score);
body.endObject();
}
body.endArray();The API route for this wiki's search is that loop and nothing else. The record is the schema, the builder is the encoder, and there is no place where a stray + "\"" + can go wrong.
Island props
Island props are JSON too, and they are attribute text in a document the client can read:
var props = new JsonBuilder();
props.beginObject();
props.field("endpoint", "/api/search");
props.field("limit", 8);
props.endObject();