From 71c7a4f8703ace2283848bc1587d7116b5a1d372 Mon Sep 17 00:00:00 2001 From: Andrew Bettison Date: Mon, 31 Aug 2015 19:03:40 +0930 Subject: [PATCH] strbuf_json_atom() supports JSON objects and arrays --- http_server.c | 2 +- strbuf_helpers.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++- strbuf_helpers.h | 24 +++++++++++-- 3 files changed, 113 insertions(+), 4 deletions(-) diff --git a/http_server.c b/http_server.c index fef23a6d..19867120 100644 --- a/http_server.c +++ b/http_server.c @@ -1937,7 +1937,7 @@ static strbuf strbuf_status_body(strbuf sb, struct http_response *hr, const char strbuf_puts(sb, "\r\n"); strbuf_puts(sb, hr->result_extra[i].label); strbuf_puts(sb, "="); - strbuf_json_atom_as_text(sb, &hr->result_extra[i].value); + strbuf_json_atom_as_text(sb, &hr->result_extra[i].value, "\r\n"); } strbuf_puts(sb, "\r\n"); } diff --git a/strbuf_helpers.c b/strbuf_helpers.c index ef498394..1561648f 100644 --- a/strbuf_helpers.c +++ b/strbuf_helpers.c @@ -777,11 +777,43 @@ strbuf strbuf_json_atom(strbuf sb, const struct json_atom *atom) return strbuf_json_string(sb, atom->u.string.content); case JSON_STRING_LENGTH: return strbuf_json_string_len(sb, atom->u.string.content, atom->u.string.length); + case JSON_OBJECT: { + strbuf_putc(sb, '{'); + size_t i; + for (i = 0; i != atom->u.object.itemc; ++i) { + if (i) + strbuf_putc(sb, ','); + strbuf_json_string(sb, atom->u.object.itemv[i].key); + strbuf_putc(sb, ':'); + strbuf_json_atom(sb, atom->u.object.itemv[i].value); + } + strbuf_putc(sb, '}'); + return sb; + } + case JSON_ARRAY: { + strbuf_putc(sb, '['); + size_t i; + for (i = 0; i != atom->u.array.itemc; ++i) { + if (i) + strbuf_putc(sb, ','); + strbuf_json_atom(sb, atom->u.array.itemv[i]); + } + strbuf_putc(sb, ']'); + return sb; + } } abort(); } -strbuf strbuf_json_atom_as_text(strbuf sb, const struct json_atom *atom) +static strbuf strbuf_puts_repeat(strbuf sb, const char *text, size_t repeat) +{ + size_t i; + for (i = 0; i != repeat; ++i) + strbuf_puts(sb, text); + return sb; +} + +static strbuf strbuf_json_atom_as_text_indented(strbuf sb, const struct json_atom *atom, const char *eol, const char *tab, unsigned indent) { switch (atom->type) { case JSON_NULL: @@ -795,10 +827,43 @@ strbuf strbuf_json_atom_as_text(strbuf sb, const struct json_atom *atom) return strbuf_puts(sb, atom->u.string.content); case JSON_STRING_LENGTH: return strbuf_ncat(sb, atom->u.string.content, atom->u.string.length); + case JSON_OBJECT: { + strbuf_puts(sb, "{"); + size_t i; + for (i = 0; i != atom->u.object.itemc; ++i) { + strbuf_puts(sb, eol); + strbuf_puts_repeat(sb, tab, indent); + strbuf_puts(sb, atom->u.object.itemv[i].key); + strbuf_puts(sb, ": "); + strbuf_json_atom_as_text_indented(sb, atom->u.object.itemv[i].value, eol, tab, indent + 1); + } + if (i) { + strbuf_puts(sb, eol); + strbuf_puts_repeat(sb, tab, indent); + } + strbuf_puts(sb, "}"); + return sb; + } + case JSON_ARRAY: { + strbuf_puts(sb, "["); + size_t i; + for (i = 0; i != atom->u.array.itemc; ++i) { + if (i) + strbuf_puts(sb, ", "); + strbuf_json_atom_as_text_indented(sb, atom->u.array.itemv[i], eol, tab, indent + 1); + } + strbuf_puts(sb, "]"); + return sb; + } } abort(); } +strbuf strbuf_json_atom_as_text(strbuf sb, const struct json_atom *atom, const char *eol) +{ + return strbuf_json_atom_as_text_indented(sb, atom, eol, " ", 0); +} + strbuf strbuf_json_atom_as_html(strbuf sb, const struct json_atom *atom) { switch (atom->type) { @@ -813,6 +878,30 @@ strbuf strbuf_json_atom_as_html(strbuf sb, const struct json_atom *atom) return strbuf_html_escape(sb, atom->u.string.content, strlen(atom->u.string.content)); case JSON_STRING_LENGTH: return strbuf_html_escape(sb, atom->u.string.content, atom->u.string.length); + case JSON_OBJECT: { + strbuf_puts(sb, "
"); + size_t i; + for (i = 0; i != atom->u.object.itemc; ++i) { + strbuf_puts(sb, "\n
"); + strbuf_html_escape(sb, atom->u.object.itemv[i].key, strlen(atom->u.object.itemv[i].key)); + strbuf_puts(sb, "
"); + strbuf_json_atom_as_html(sb, atom->u.object.itemv[i].value); + strbuf_puts(sb, "
"); + } + strbuf_puts(sb, "\n
"); + return sb; + } + case JSON_ARRAY: { + strbuf_puts(sb, "
    "); + size_t i; + for (i = 0; i != atom->u.array.itemc; ++i) { + strbuf_puts(sb, "\n
  1. "); + strbuf_json_atom_as_html(sb, atom->u.array.itemv[i]); + strbuf_puts(sb, "
  2. "); + } + strbuf_puts(sb, "\n
"); + return sb; + } } abort(); } diff --git a/strbuf_helpers.h b/strbuf_helpers.h index ea89395c..c66b92ac 100644 --- a/strbuf_helpers.h +++ b/strbuf_helpers.h @@ -226,8 +226,20 @@ strbuf strbuf_json_boolean(strbuf sb, int boolean); strbuf strbuf_json_string(strbuf sb, const char *str); // str can be NULL strbuf strbuf_json_string_len(strbuf sb, const char *str, size_t strlen); // str cannot be NULL strbuf strbuf_json_hex(strbuf sb, const unsigned char *buf, size_t len); +struct json_key_value { + const char *key; + struct json_atom *value; +}; struct json_atom { - enum json_atomic_type { JSON_NULL, JSON_BOOLEAN, JSON_INTEGER, JSON_STRING_NULTERM, JSON_STRING_LENGTH } type; + enum json_atomic_type { + JSON_NULL, + JSON_BOOLEAN, // u.boolean + JSON_INTEGER, // u.integer + JSON_STRING_NULTERM, // u.string.content (nul terminated) + JSON_STRING_LENGTH, // u.string.content[0 .. u.string.length-1] + JSON_OBJECT, // u.object.itemv[0 .. u.object.itemc].key .value + JSON_ARRAY, // u.array.itemv[0 .. u.object.itemc] + } type; union { int boolean; int64_t integer; @@ -235,11 +247,19 @@ struct json_atom { const char *content; size_t length; } string; + struct { + size_t itemc; + struct json_key_value *itemv; + } object; + struct { + size_t itemc; + struct json_atom **itemv; + } array; } u; }; strbuf strbuf_json_atom(strbuf sb, const struct json_atom *); strbuf strbuf_json_atom_as_html(strbuf sb, const struct json_atom *); -strbuf strbuf_json_atom_as_text(strbuf sb, const struct json_atom *); +strbuf strbuf_json_atom_as_text(strbuf sb, const struct json_atom *, const char *eol); /* Append a representation of a struct http_range[] array. * @author Andrew Bettison