From f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Mon, 1 Feb 2016 17:38:11 +0200 Subject: [PATCH] lib: JSON parser can now optionally parse input that isn't an object. Based on patch by Aki Tuomi. --- src/lib/json-parser.c | 26 ++++++++++++++++++++++++-- src/lib/json-parser.h | 10 ++++++++++ src/lib/test-json-parser.c | 28 +++++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/lib/json-parser.c b/src/lib/json-parser.c index 09be703b06..1ff8c05340 100644 --- a/src/lib/json-parser.c +++ b/src/lib/json-parser.c @@ -21,6 +21,7 @@ enum json_state { JSON_STATE_ARRAY_VALUE, JSON_STATE_ARRAY_SKIP_STRING, JSON_STATE_ARRAY_NEXT, + JSON_STATE_VALUE, JSON_STATE_DONE }; @@ -99,6 +100,12 @@ static void json_parser_update_input_pos(struct json_parser *parser) } 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; @@ -107,6 +114,9 @@ struct json_parser *json_parser_init(struct istream *input) 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 @@ json_try_parse_next(struct json_parser *parser, enum json_type *type_r, 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 @@ json_try_parse_next(struct json_parser *parser, enum json_type *type_r, } 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 == '}') diff --git a/src/lib/json-parser.h b/src/lib/json-parser.h index e16f5e1b32..f9b6e800d4 100644 --- a/src/lib/json-parser.h +++ b/src/lib/json-parser.h @@ -19,8 +19,18 @@ enum json_type { 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 diff --git a/src/lib/test-json-parser.c b/src/lib/test-json-parser.c index da24f0fcbb..ab86c2e268 100644 --- a/src/lib/test-json-parser.c +++ b/src/lib/test-json-parser.c @@ -163,7 +163,8 @@ static void test_json_parser_success(bool full_size) 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 @@ static int test_json_parse_input(const char *test_input) 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 @@ static int test_json_parse_input(const char *test_input) 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 @@ static void test_json_parser_errors(void) 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 @@ void test_json_parser(void) { 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();