git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / 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

  List information: http://vger.kernel.org/majordomo-info.html

* 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.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).