dovecot-2.2: lib: Added json-tree API for parsing JSON input int...

dovecot at dovecot.org dovecot at dovecot.org
Wed Apr 8 04:56:59 UTC 2015


details:   http://hg.dovecot.org/dovecot-2.2/rev/6bde7868cffd
changeset: 18399:6bde7868cffd
user:      Timo Sirainen <tss at iki.fi>
date:      Wed Apr 08 13:55:35 2015 +0900
description:
lib: Added json-tree API for parsing JSON input into a tree structure.
This makes it easier to access complicated JSON structs that can fit into
memory.

diffstat:

 src/lib/Makefile.am      |    3 +
 src/lib/json-tree.c      |  167 +++++++++++++++++++++++++++++++++++++++++++++++
 src/lib/json-tree.h      |   40 +++++++++++
 src/lib/test-json-tree.c |  114 ++++++++++++++++++++++++++++++++
 src/lib/test-lib.c       |    1 +
 src/lib/test-lib.h       |    1 +
 6 files changed, 326 insertions(+), 0 deletions(-)

diffs (truncated from 382 to 300 lines):

diff -r 8146fdc0de34 -r 6bde7868cffd src/lib/Makefile.am
--- a/src/lib/Makefile.am	Mon Apr 06 12:07:32 2015 +0900
+++ b/src/lib/Makefile.am	Wed Apr 08 13:55:35 2015 +0900
@@ -86,6 +86,7 @@
 	ioloop-epoll.c \
 	ioloop-kqueue.c \
 	json-parser.c \
+	json-tree.c \
 	lib.c \
 	lib-signals.c \
 	md4.c \
@@ -212,6 +213,7 @@
 	ioloop-private.h \
 	ioloop-notify-fd.h \
 	json-parser.h \
+	json-tree.h \
 	lib.h \
 	lib-signals.h \
 	llist.h \
@@ -301,6 +303,7 @@
 	test-istream-seekable.c \
 	test-istream-tee.c \
 	test-json-parser.c \
+	test-json-tree.c \
 	test-llist.c \
 	test-mempool-alloconly.c \
 	test-network.c \
diff -r 8146fdc0de34 -r 6bde7868cffd src/lib/json-tree.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/json-tree.c	Wed Apr 08 13:55:35 2015 +0900
@@ -0,0 +1,167 @@
+/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "json-tree.h"
+
+struct json_tree {
+	pool_t pool;
+	struct json_tree_node *root, *cur, *cur_child;
+};
+
+struct json_tree *json_tree_init(void)
+{
+	struct json_tree *tree;
+	pool_t pool;
+
+	pool = pool_alloconly_create("json tree", 1024);
+	tree = p_new(pool, struct json_tree, 1);
+	tree->pool = pool;
+	tree->root = tree->cur = p_new(tree->pool, struct json_tree_node, 1);
+	tree->cur->value_type = JSON_TYPE_OBJECT;
+	return tree;
+}
+
+void json_tree_deinit(struct json_tree **_tree)
+{
+	struct json_tree *tree = *_tree;
+
+	*_tree = NULL;
+	pool_unref(&tree->pool);
+}
+
+static void
+json_tree_append_child(struct json_tree *tree, enum json_type type,
+		       const char *value)
+{
+	struct json_tree_node *node;
+
+	node = p_new(tree->pool, struct json_tree_node, 1);
+	node->parent = tree->cur;
+	node->value_type = type;
+	node->value.str = p_strdup(tree->pool, value);
+
+	if (tree->cur_child == NULL)
+		tree->cur->value.child = node;
+	else
+		tree->cur_child->next = node;
+	tree->cur_child = node;
+}
+
+static void
+json_tree_set_cur(struct json_tree *tree, struct json_tree_node *node)
+{
+	tree->cur = node;
+	tree->cur_child = tree->cur->value.child;
+	if (tree->cur_child != NULL) {
+		while (tree->cur_child->next != NULL)
+			tree->cur_child = tree->cur_child->next;
+	}
+}
+
+static int
+json_tree_append_value(struct json_tree *tree, enum json_type type,
+		       const char *value)
+{
+	switch (tree->cur->value_type) {
+	case JSON_TYPE_OBJECT_KEY:
+		/* "key": value - we already added the node and set its key,
+		   so now just set the value */
+		tree->cur->value_type = type;
+		tree->cur->value.str = p_strdup(tree->pool, value);
+		json_tree_set_cur(tree, tree->cur->parent);
+		break;
+	case JSON_TYPE_ARRAY:
+		/* element in array - add a new node */
+		json_tree_append_child(tree, type, value);
+		break;
+	default:
+		return -1;
+	}
+	return 0;
+}
+
+int json_tree_append(struct json_tree *tree, enum json_type type,
+		     const char *value)
+{
+	switch (type) {
+	case JSON_TYPE_OBJECT_KEY:
+		if (tree->cur->value_type != JSON_TYPE_OBJECT)
+			return -1;
+		json_tree_append_child(tree, type, NULL);
+		json_tree_set_cur(tree, tree->cur_child);
+		tree->cur->key = p_strdup(tree->pool, value);
+		break;
+	case JSON_TYPE_ARRAY:
+		if (json_tree_append_value(tree, type, NULL) < 0)
+			return -1;
+		json_tree_set_cur(tree, tree->cur_child);
+		break;
+	case JSON_TYPE_OBJECT:
+		if (tree->cur->value_type == JSON_TYPE_OBJECT_KEY)
+			tree->cur->value_type = JSON_TYPE_OBJECT;
+		else if (tree->cur->value_type == JSON_TYPE_ARRAY) {
+			json_tree_append_child(tree, type, NULL);
+			json_tree_set_cur(tree, tree->cur_child);
+		} else {
+			return -1;
+		}
+		break;
+	case JSON_TYPE_OBJECT_END:
+		if (tree->cur->parent == NULL ||
+		    tree->cur->value_type != JSON_TYPE_OBJECT)
+			return -1;
+		json_tree_set_cur(tree, tree->cur->parent);
+		break;
+	case JSON_TYPE_ARRAY_END:
+		if (tree->cur->parent == NULL ||
+		    tree->cur->value_type != JSON_TYPE_ARRAY)
+			return -1;
+		json_tree_set_cur(tree, tree->cur->parent);
+		break;
+	case JSON_TYPE_STRING:
+	case JSON_TYPE_NUMBER:
+	case JSON_TYPE_TRUE:
+	case JSON_TYPE_FALSE:
+	case JSON_TYPE_NULL:
+		if (json_tree_append_value(tree, type, value) < 0)
+			return -1;
+		break;
+	}
+	return 0;
+}
+
+struct json_tree_node *json_tree_root(struct json_tree *tree)
+{
+	return tree->root;
+}
+
+struct json_tree_node *
+json_tree_find_key(struct json_tree_node *node, const char *key)
+{
+	for (; node != NULL; node = node->next) {
+		if (node->key != NULL && strcmp(node->key, key) == 0)
+			return node;
+	}
+	return NULL;
+}
+
+struct json_tree_node *
+json_tree_find_child_with(struct json_tree_node *node,
+			  const char *key, const char *value)
+{
+	struct json_tree_node *child;
+
+	i_assert(node->value_type == JSON_TYPE_OBJECT ||
+		 node->value_type == JSON_TYPE_ARRAY);
+
+	for (node = node->value.child; node != NULL; node = node->next) {
+		if (node->value_type != JSON_TYPE_OBJECT)
+			continue;
+
+		child = json_tree_find_key(node->value.child, key);
+		if (child != NULL && child->value.str != NULL &&
+		    strcmp(child->value.str, value) == 0)
+			return node;
+	}
+	return NULL;
+}
diff -r 8146fdc0de34 -r 6bde7868cffd src/lib/json-tree.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/json-tree.h	Wed Apr 08 13:55:35 2015 +0900
@@ -0,0 +1,40 @@
+#ifndef JSON_TREE_H
+#define JSON_TREE_H
+
+#include "json-parser.h"
+
+struct json_tree_node {
+	/* object key, or NULL if we're in a list */
+	const char *key;
+	struct json_tree_node *parent, *next;
+
+	enum json_type value_type;
+	struct {
+		/* for JSON_TYPE_OBJECT and JSON_TYPE_ARRAY */
+		struct json_tree_node *child;
+		/* for other types */
+		const char *str;
+	} value;
+};
+
+struct json_tree *json_tree_init(void);
+void json_tree_deinit(struct json_tree **tree);
+
+/* Append data to a tree. The type/value should normally come from json-parser.
+   Returns 0 on success, -1 if the input was invalid (which should never happen
+   if it's coming from json-parser). */
+int json_tree_append(struct json_tree *tree, enum json_type type,
+		     const char *value);
+
+/* Return the root node. */
+struct json_tree_node *json_tree_root(struct json_tree *tree);
+/* Find a node with the specified key (from node's siblings) */
+struct json_tree_node *
+json_tree_find_key(struct json_tree_node *node, const char *key);
+/* Find an object node (from an array), which contains the specified key=value
+   in its values. */
+struct json_tree_node *
+json_tree_find_child_with(struct json_tree_node *node,
+			  const char *key, const char *value);
+
+#endif
diff -r 8146fdc0de34 -r 6bde7868cffd src/lib/test-json-tree.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/test-json-tree.c	Wed Apr 08 13:55:35 2015 +0900
@@ -0,0 +1,114 @@
+/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "json-tree.h"
+
+struct {
+	enum json_type type;
+	const char *value;
+} test_input[] = {
+	{ JSON_TYPE_OBJECT_KEY, "key-str" },
+	{ JSON_TYPE_STRING, "string" },
+	{ JSON_TYPE_OBJECT_KEY, "key-num" },
+	{ JSON_TYPE_NUMBER, "1234" },
+	{ JSON_TYPE_OBJECT_KEY, "key-true" },
+	{ JSON_TYPE_TRUE, "true" },
+	{ JSON_TYPE_OBJECT_KEY, "key-false" },
+	{ JSON_TYPE_FALSE, "false" },
+	{ JSON_TYPE_OBJECT_KEY, "key-null" },
+	{ JSON_TYPE_NULL, NULL },
+
+	{ JSON_TYPE_OBJECT_KEY, "key-obj-empty" },
+	{ JSON_TYPE_OBJECT, NULL },
+	{ JSON_TYPE_OBJECT_END, NULL },
+
+	{ JSON_TYPE_OBJECT_KEY, "key-obj" },
+	{ JSON_TYPE_OBJECT, NULL },
+	  { JSON_TYPE_OBJECT_KEY, "sub" },
+	  { JSON_TYPE_STRING, "value" },
+	{ JSON_TYPE_OBJECT_END, NULL },
+
+	{ JSON_TYPE_OBJECT_KEY, "key-arr-empty" },
+	{ JSON_TYPE_ARRAY, NULL },
+	{ JSON_TYPE_ARRAY_END, NULL },
+
+	{ JSON_TYPE_OBJECT_KEY, "key-arr" },
+	{ JSON_TYPE_ARRAY, NULL },
+	  { JSON_TYPE_STRING, "foo" },
+	  { JSON_TYPE_ARRAY, NULL },
+	  { JSON_TYPE_TRUE, "true" },
+	  { JSON_TYPE_ARRAY_END, NULL },
+	  { JSON_TYPE_OBJECT, NULL },
+	    { JSON_TYPE_OBJECT_KEY, "aobj" },
+	    { JSON_TYPE_ARRAY, NULL },
+	    { JSON_TYPE_ARRAY_END, NULL },
+	  { JSON_TYPE_OBJECT_END, NULL },
+	  { JSON_TYPE_OBJECT, NULL },
+	    { JSON_TYPE_OBJECT_KEY, "aobj-key" },
+	    { JSON_TYPE_STRING, "value1" },
+	  { JSON_TYPE_OBJECT_END, NULL },
+	  { JSON_TYPE_OBJECT, NULL },
+	    { JSON_TYPE_OBJECT_KEY, "aobj-key" },
+	    { JSON_TYPE_STRING, "value2" },
+	  { JSON_TYPE_OBJECT_END, NULL },
+	{ JSON_TYPE_ARRAY_END, NULL }


More information about the dovecot-cvs mailing list