git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 1/2] json_writer: new routines to create data in JSON format

From
Ggit@jeffhostetler.com <git@jeffhostetler.com>
Date
Mar 16, 2018, 19:40 UTC
Message-ID
<20180316194057.77513-2-git@jeffhostetler.com>
In-Reply-To
<20180316194057.77513-1-git@jeffhostetler.com>
From: Jeff Hostetler <jeffhost@microsoft.com>
Add basic routines to generate data in JSON format.
Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
 Makefile      |   1 +
 json-writer.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 json-writer.h | 120 +++++++++++++++++++++++++++++++
 3 files changed, 345 insertions(+)
 create mode 100644 json-writer.c
 create mode 100644 json-writer.h
diff --git a/Makefile b/Makefile
index 1a9b23b..9000369 100644
--- a/Makefile
+++ b/Makefile
@@ -815,6 +815,7 @@ LIB_OBJS += hashmap.o
 LIB_OBJS += help.o
 LIB_OBJS += hex.o
 LIB_OBJS += ident.o
+LIB_OBJS += json-writer.o
 LIB_OBJS += kwset.o
 LIB_OBJS += levenshtein.o
 LIB_OBJS += line-log.o
diff --git a/json-writer.c b/json-writer.c
new file mode 100644
index 0000000..755ff80
--- /dev/null
+++ b/json-writer.c
@@ -0,0 +1,224 @@
+#include "cache.h"
+#include "json-writer.h"
+
+/*
+ * Append JSON-quoted version of the given string to 'out'.
+ */
+static void jw_append_quoted_string(struct strbuf *out, const char *in)
+{
+	strbuf_addch(out, '"');
+	for (/**/; *in; in++) {
+		unsigned char c = (unsigned char)*in;
+		if (c == '"')
+			strbuf_add(out, "\\\"", 2);
+		else if (c == '\\')
+			strbuf_add(out, "\\\\", 2);
+		else if (c == '\n')
+			strbuf_add(out, "\\n", 2);
+		else if (c == '\r')
+			strbuf_add(out, "\\r", 2);
+		else if (c == '\t')
+			strbuf_add(out, "\\t", 2);
+		else if (c == '\f')
+			strbuf_add(out, "\\f", 2);
+		else if (c == '\b')
+			strbuf_add(out, "\\b", 2);
+		else if (c < 0x20)
+			strbuf_addf(out, "\\u%04x", c);
+		else
+			strbuf_addch(out, c);
+	}
+	strbuf_addch(out, '"');
+}
+
+void jw_object_begin(struct strbuf *out)
+{
+	strbuf_reset(out);
+	strbuf_addch(out, '{');
+}
+
+void jw_object_append_string(struct strbuf *out, const char *key,
+			     const char *value)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+
+	jw_append_quoted_string(out, key);
+	strbuf_addch(out, ':');
+	jw_append_quoted_string(out, value);
+}
+
+void jw_object_append_int(struct strbuf *out, const char *key, int value)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+
+	jw_append_quoted_string(out, key);
+	strbuf_addf(out, ":%d", value);
+}
+
+void jw_object_append_uint64(struct strbuf *out, const char *key, uint64_t value)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+
+	jw_append_quoted_string(out, key);
+	strbuf_addf(out, ":%"PRIuMAX, value);
+}
+
+void jw_object_append_double(struct strbuf *out, const char *key, double value)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+
+	jw_append_quoted_string(out, key);
+	strbuf_addf(out, ":%f", value);
+}
+
+void jw_object_append_true(struct strbuf *out, const char *key)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+
+	jw_append_quoted_string(out, key);
+	strbuf_addstr(out, ":true");
+}
+
+void jw_object_append_false(struct strbuf *out, const char *key)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+
+	jw_append_quoted_string(out, key);
+	strbuf_addstr(out, ":false");
+}
+
+void jw_object_append_null(struct strbuf *out, const char *key)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+
+	jw_append_quoted_string(out, key);
+	strbuf_addstr(out, ":null");
+}
+
+void jw_object_append_object(struct strbuf *out, const char *key,
+			     const char *value)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+
+	jw_append_quoted_string(out, key);
+	strbuf_addch(out, ':');
+	strbuf_addstr(out, value);
+}
+
+void jw_object_append_array(struct strbuf *out, const char *key,
+			    const char *value)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+
+	jw_append_quoted_string(out, key);
+	strbuf_addch(out, ':');
+	strbuf_addstr(out, value);
+}
+
+void jw_object_end(struct strbuf *out)
+{
+	strbuf_addch(out, '}');
+}
+
+void jw_array_begin(struct strbuf *out)
+{
+	strbuf_reset(out);
+	strbuf_addch(out, '[');
+}
+
+void jw_array_append_string(struct strbuf *out, const char *elem)
+{
+	struct strbuf qe = STRBUF_INIT;
+
+	jw_append_quoted_string(&qe, elem);
+
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+	strbuf_addstr(out, qe.buf);
+
+	strbuf_release(&qe);
+}
+
+void jw_array_append_int(struct strbuf *out, int elem)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+	strbuf_addf(out, "%d", elem);
+}
+
+void jw_array_append_uint64(struct strbuf *out, uint64_t elem)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+	strbuf_addf(out, "%"PRIuMAX, elem);
+}
+
+void jw_array_append_double(struct strbuf *out, double elem)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+	strbuf_addf(out, "%f", elem);
+}
+
+void jw_array_append_true(struct strbuf *out)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+	strbuf_addstr(out, "true");
+}
+
+void jw_array_append_false(struct strbuf *out)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+	strbuf_addstr(out, "false");
+}
+
+void jw_array_append_null(struct strbuf *out)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+	strbuf_addstr(out, "null");
+}
+
+void jw_array_append_object(struct strbuf *out, const char *obj)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+	strbuf_addstr(out, obj);
+}
+
+void jw_array_append_array(struct strbuf *out, const char *array)
+{
+	if (out->len > 1)
+		strbuf_addch(out, ',');
+	strbuf_addstr(out, array);
+}
+
+void jw_array_append_argc_argv(struct strbuf *out, int argc, const char **argv)
+{
+	int k;
+
+	for (k = 0; k < argc; k++)
+		jw_array_append_string(out, argv[k]);
+}
+
+void jw_array_append_argv(struct strbuf *out, const char **argv)
+{
+	while (*argv)
+		jw_array_append_string(out, *argv++);
+}
+
+void jw_array_end(struct strbuf *out)
+{
+	strbuf_addch(out, ']');
+}
diff --git a/json-writer.h b/json-writer.h
new file mode 100644
index 0000000..0a60ab3
--- /dev/null
+++ b/json-writer.h
@@ -0,0 +1,120 @@
+#ifndef JSON_WRITER_H
+#define JSON_WRITER_H
+
+/*
+ * JSON data structures are defined at:
+ *      http://json.org/
+ *      http://www.ietf.org/rfc/rfc7159.txt
+ *
+ * The JSON-writer API allows one to build JSON data structures using a
+ * "struct strbuf" buffer.  This is intended as a simple API to build
+ * output strings; it is not intended to be a general object model for
+ * JSON data.
+ *
+ * All string values (both keys and string r-values) are properly quoted
+ * and escaped if they contain special characters.
+ *
+ * These routines create compact JSON data (with no unnecessary whitespace,
+ * newlines, or indenting).
+ *
+ * Both "JSON objects" (aka sets of k/v pairs) and "JSON array" can be
+ * constructed using a 'begin append* end' model.
+ *
+ * Example JSON Object Usage:
+ *
+ *      struct strbuf obj1 = STRBUF_INIT;
+ *
+ *      jw_object_begin(&obj1);
+ *          jw_object_append_string(&obj1, "a", "abc");
+ *          jw_object_append_int(&obj1, "b", 42);
+ *          jw_object_append_true(&obj1, "c");
+ *      jw_object_end(&obj1);
+ *
+ *      printf("%s\n", obj1.buf);
+ *
+ * Should yield:   
+ *
+ *      {"a":"abc","b":42,"c":true}
+ *
+ * Example JSON Array Usage:
+ *
+ *      struct strbuf arr1 = STRBUF_INIT;
+ *
+ *      jw_array_begin(&arr1);
+ *          jw_array_append_string(&arr1, "abc");
+ *          jw_array_append_int(&arr1, 42);
+ *          jw_array_append_true(&arr1);
+ *      jw_array_end(&arr1);
+ *
+ *      printf("%s\n", arr1.buf);
+ *
+ * Should yield:
+ *
+ *      ["abc",42,true]
+ *
+ * Nested JSON structures are also supported.  These should be composed bottom
+ * up using multiple strbuf variables.
+ *
+ * Example Nested Usage (using the above "obj1" and "arr1" variables):
+ *
+ *       struct strbuf obj2 = STRBUF_INIT;
+ *
+ *       jw_object_begin(&obj2);
+ *           jw_object_append_object(&obj2, "obj1", obj1.buf);
+ *           jw_object_append_array(&obj2, "arr1", arr1.buf);
+ *       jw_object_end(&obj2);
+ *
+ *       printf("%s\n", obj2.buf);
+ *
+ * Should yield:
+ *
+ *       {"obj1":{"a":"abc","b":42,"c":true},"arr1":["abc",42,true]}
+ *
+ * And:
+ *
+ *       struct strbuf arr2 = STRBUF_INIT;
+ *
+ *       jw_array_begin(&arr2);
+ *           jw_array_append_object(&arr2, obj1.buf);
+ *           jw_array_append_array(&arr2, arr1.buf);
+ *       jw_array_end(&arr2);
+ *
+ *       printf("%s\n", arr2.buf);
+ *
+ * Should yield:
+ *
+ *       [{"a":"abc","b":42,"c":true},["abc",42,true]]
+ *
+ */
+
+void jw_object_begin(struct strbuf *out);
+void jw_object_append_string(struct strbuf *out, const char *key,
+			     const char *value);
+void jw_object_append_int(struct strbuf *out, const char *key, int value);
+void jw_object_append_uint64(struct strbuf *out, const char *key,
+			     uint64_t value);
+void jw_object_append_double(struct strbuf *out, const char *key, double value);
+void jw_object_append_true(struct strbuf *out, const char *key);
+void jw_object_append_false(struct strbuf *out, const char *key);
+void jw_object_append_null(struct strbuf *out, const char *key);
+void jw_object_append_object(struct strbuf *out, const char *key,
+			     const char *value);
+void jw_object_append_array(struct strbuf *out, const char *key,
+			    const char *value);
+void jw_object_end(struct strbuf *out);
+
+void jw_array_begin(struct strbuf *out);
+void jw_array_append_string(struct strbuf *out, const char *elem);
+void jw_array_append_int(struct strbuf *out, int elem);
+void jw_array_append_uint64(struct strbuf *out, uint64_t elem);
+void jw_array_append_double(struct strbuf *out, double elem);
+void jw_array_append_true(struct strbuf *out);
+void jw_array_append_false(struct strbuf *out);
+void jw_array_append_null(struct strbuf *out);
+void jw_array_append_object(struct strbuf *out, const char *obj);
+void jw_array_append_array(struct strbuf *out, const char *array);
+void jw_array_append_argc_argv(struct strbuf *out, int argc, const char **argv);
+void jw_array_append_argv(struct strbuf *out, const char **argv);
+void jw_array_end(struct strbuf *out);
+
+#endif /* JSON_WRITER_H */
-- 
2.9.3
Previous: git@jeffhostetler.comNext: Jeff King
Message 3 of 11 in “routines to generate JSON data”
  1. 0/2 routines to generate JSON datagit@jeffhostetler.com, Mar 16, 2018
  2. 2/2 json-writer: unit testgit@jeffhostetler.com, Mar 16, 2018
  3. 1/2 json_writer: new routines to create data in JSON formatgit@jeffhostetler.com, Mar 16, 2018
  4. Jeff KingMar 16, 2018
  5. Ævar Arnfjörð BjarmasonMar 16, 2018
  6. Jeff KingMar 20, 2018
  7. Jacob KellerMar 17, 2018
  8. Jeff HostetlerMar 19, 2018
  9. Jeff HostetlerMar 19, 2018
  10. Jeff KingMar 20, 2018
  11. Jeff HostetlerMar 20, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.