Skip to content

Commit

Permalink
lib: JSON parser can now optionally parse input that isn't an object.
Browse files Browse the repository at this point in the history
Based on patch by Aki Tuomi.
  • Loading branch information
sirainen committed Feb 1, 2016
1 parent 4bfa47e commit f6ae9ae
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 5 deletions.
26 changes: 24 additions & 2 deletions src/lib/json-parser.c
Expand Up @@ -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
};

Expand Down Expand Up @@ -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;

Expand All @@ -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;
}

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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 == '}')
Expand Down
10 changes: 10 additions & 0 deletions src/lib/json-parser.h
Expand Up @@ -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
Expand Down
28 changes: 25 additions & 3 deletions src/lib/test-json-parser.c
Expand Up @@ -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;
Expand All @@ -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)
Expand All @@ -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[] = {
Expand All @@ -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();
}

Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit f6ae9ae

Please sign in to comment.