All of lore.kernel.org
 help / color / mirror / Atom feed
From: git@jeffhostetler.com
To: git@vger.kernel.org
Cc: gitster@pobox.com, peff@peff.net, lars.schneider@autodesk.com,
	Jeff Hostetler <jeffhost@microsoft.com>
Subject: [PATCH 1/2] json_writer: new routines to create data in JSON format
Date: Fri, 16 Mar 2018 19:40:56 +0000	[thread overview]
Message-ID: <20180316194057.77513-2-git@jeffhostetler.com> (raw)
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


  reply	other threads:[~2018-03-16 20:00 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-03-16 19:40 [PATCH 0/2] routines to generate JSON data git
2018-03-16 19:40 ` git [this message]
2018-03-16 19:40 ` [PATCH 2/2] json-writer: unit test git
2018-03-16 21:18 ` [PATCH 0/2] routines to generate JSON data Jeff King
2018-03-16 23:00   ` Ævar Arnfjörð Bjarmason
2018-03-20  5:52     ` Jeff King
2018-03-17  7:38   ` Jacob Keller
2018-03-19 17:31     ` Jeff Hostetler
2018-03-19 10:19   ` Jeff Hostetler
2018-03-20  5:42     ` Jeff King
2018-03-20 16:44       ` Jeff Hostetler

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180316194057.77513-2-git@jeffhostetler.com \
    --to=git@jeffhostetler.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=jeffhost@microsoft.com \
    --cc=lars.schneider@autodesk.com \
    --cc=peff@peff.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.