]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: JSON parser can now optionally parse input that isn't an object.
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Mon, 1 Feb 2016 15:38:11 +0000 (17:38 +0200)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Mon, 1 Feb 2016 15:45:48 +0000 (17:45 +0200)
Based on patch by Aki Tuomi.

src/lib/json-parser.c
src/lib/json-parser.h
src/lib/test-json-parser.c

index 09be703b0604efc77a0c605f54cd43939533f33e..1ff8c05340b377e1a01e2b7270bfc3e108db62e7 100644 (file)
@@ -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 == '}')
index e16f5e1b329e4cdf2156ad0ee909ccdda4e19de2..f9b6e800d4fad9c975247524c0746f85ef1acf51 100644 (file)
@@ -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
index da24f0fcbb40cac476d6bb90109cdf6dfc762424..ab86c2e268cd1676d92f5e0bb057c454900b6be2 100644 (file)
@@ -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();