changeset 19689:8bcb62e748b9

lib: JSON parser can now optionally parse input that isn't an object. Based on patch by Aki Tuomi.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 01 Feb 2016 17:38:11 +0200
parents 0869e8023eab
children f2430d582c74
files src/lib/json-parser.c src/lib/json-parser.h src/lib/test-json-parser.c
diffstat 3 files changed, 59 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib/json-parser.c	Mon Feb 01 17:30:23 2016 +0200
+++ b/src/lib/json-parser.c	Mon Feb 01 17:38:11 2016 +0200
@@ -21,6 +21,7 @@
 	JSON_STATE_ARRAY_VALUE,
 	JSON_STATE_ARRAY_SKIP_STRING,
 	JSON_STATE_ARRAY_NEXT,
+	JSON_STATE_VALUE,
 	JSON_STATE_DONE
 };
 
@@ -100,6 +101,12 @@
 
 struct json_parser *json_parser_init(struct istream *input)
 {
+	return json_parser_init_flags(input, 0);
+}
+
+struct json_parser *json_parser_init_flags(struct istream *input,
+					   enum json_parser_flags flags)
+{
 	struct json_parser *parser;
 
 	parser = i_new(struct json_parser, 1);
@@ -107,6 +114,9 @@
 	parser->value = str_new(default_pool, 128);
 	i_array_init(&parser->nesting, 8);
 	i_stream_ref(input);
+
+	if ((flags & JSON_PARSER_NO_ROOT_OBJECT) != 0)
+		parser->state = JSON_STATE_VALUE;
 	return parser;
 }
 
@@ -407,6 +417,7 @@
 		return 0;
 	case JSON_STATE_OBJECT_VALUE:
 	case JSON_STATE_ARRAY_VALUE:
+	case JSON_STATE_VALUE:
 		if (*parser->data == '{') {
 			json_parser_object_open(parser);
 
@@ -459,8 +470,19 @@
 			}
 			return -1;
 		}
-		parser->state = parser->state == JSON_STATE_OBJECT_VALUE ?
-			JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT;
+		switch (parser->state) {
+		case JSON_STATE_OBJECT_VALUE:
+			parser->state = JSON_STATE_OBJECT_NEXT;
+			break;
+		case JSON_STATE_ARRAY_VALUE:
+			parser->state = JSON_STATE_ARRAY_NEXT;
+			break;
+		case JSON_STATE_VALUE:
+			parser->state = JSON_STATE_DONE;
+			break;
+		default:
+			i_unreached();
+		}
 		break;
 	case JSON_STATE_OBJECT_OPEN:
 		if (*parser->data == '}')
--- a/src/lib/json-parser.h	Mon Feb 01 17:30:23 2016 +0200
+++ b/src/lib/json-parser.h	Mon Feb 01 17:38:11 2016 +0200
@@ -19,8 +19,18 @@
 	JSON_TYPE_NULL
 };
 
+enum json_parser_flags {
+	/* By default we assume that the input is an object and parsing skips
+	   the root level "{" and "}". If this flag is set, it's possible to
+	   parse any other type of JSON values directly. */
+	JSON_PARSER_NO_ROOT_OBJECT	= 0x01
+};
+
 /* Parse JSON tokens from the input stream. */
 struct json_parser *json_parser_init(struct istream *input);
+struct json_parser *json_parser_init_flags(struct istream *input,
+					   enum json_parser_flags flags);
+
 int json_parser_deinit(struct json_parser **parser, const char **error_r);
 
 /* Parse the next token. Returns 1 if found, 0 if more input stream is
--- a/src/lib/test-json-parser.c	Mon Feb 01 17:30:23 2016 +0200
+++ b/src/lib/test-json-parser.c	Mon Feb 01 17:38:11 2016 +0200
@@ -163,7 +163,8 @@
 	test_end();
 }
 
-static int test_json_parse_input(const char *test_input)
+static int
+test_json_parse_input(const char *test_input, enum json_parser_flags flags)
 {
 	struct json_parser *parser;
 	struct istream *input;
@@ -172,7 +173,7 @@
 	int ret = 0;
 
 	input = test_istream_create_data(test_input, strlen(test_input));
-	parser = json_parser_init(input);
+	parser = json_parser_init_flags(input, flags);
 	while (json_parse_next(parser, &type, &value) > 0)
 		ret++;
 	if (json_parser_deinit(&parser, &error) < 0)
@@ -181,6 +182,26 @@
 	return ret;
 }
 
+static void test_json_parser_primitive_values(void)
+{
+	static const char *test_inputs[] = {
+		"\"hello\"",
+		"null",
+		"1234",
+		"1234.1234",
+		"{}",
+		"[]",
+		"true",
+		"false"
+	};
+	unsigned int i;
+
+	test_begin("json_parser (primitives)");
+	for (i = 0; i < N_ELEMENTS(test_inputs); i++)
+		test_assert_idx(test_json_parse_input(test_inputs[i], JSON_PARSER_NO_ROOT_OBJECT) == 1, i);
+	test_end();
+}
+
 static void test_json_parser_errors(void)
 {
 	static const char *test_inputs[] = {
@@ -199,7 +220,7 @@
 
 	test_begin("json parser error handling");
 	for (i = 0; i < N_ELEMENTS(test_inputs); i++)
-		test_assert_idx(test_json_parse_input(test_inputs[i]) < 0, i);
+		test_assert_idx(test_json_parse_input(test_inputs[i], 0) < 0, i);
 	test_end();
 }
 
@@ -229,6 +250,7 @@
 {
 	test_json_parser_success(TRUE);
 	test_json_parser_success(FALSE);
+	test_json_parser_primitive_values();
 	test_json_parser_errors();
 	test_json_append_escaped();
 	test_json_append_escaped_data();