Mercurial > dovecot > core-2.2
changeset 15439:f456fffeec5c
JSON parser: Added support for arrays.
The parser should be complete now.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 29 Nov 2012 10:29:19 +0200 |
parents | 4f7e0be9cc35 |
children | 9cab24687819 |
files | src/lib/json-parser.c src/lib/json-parser.h src/lib/test-json-parser.c |
diffstat | 3 files changed, 133 insertions(+), 40 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib/json-parser.c Thu Nov 29 09:38:34 2012 +0200 +++ b/src/lib/json-parser.c Thu Nov 29 10:29:19 2012 +0200 @@ -1,6 +1,7 @@ /* Copyright (c) 2012 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "array.h" #include "str.h" #include "istream.h" #include "hex-dec.h" @@ -14,8 +15,12 @@ JSON_STATE_OBJECT_KEY, JSON_STATE_OBJECT_COLON, JSON_STATE_OBJECT_VALUE, - JSON_STATE_OBJECT_VALUE_NEXT, - JSON_STATE_SKIP_STRING, + JSON_STATE_OBJECT_SKIP_STRING, + JSON_STATE_OBJECT_NEXT, + JSON_STATE_ARRAY_OPEN, + JSON_STATE_ARRAY_VALUE, + JSON_STATE_ARRAY_SKIP_STRING, + JSON_STATE_ARRAY_NEXT, JSON_STATE_DONE }; @@ -29,7 +34,7 @@ struct istream *strinput; enum json_state state; - unsigned int nested_object_count; + ARRAY(enum json_state) nesting; unsigned int nested_skip_count; bool skipping; }; @@ -93,6 +98,7 @@ parser = i_new(struct json_parser, 1); parser->input = input; parser->value = str_new(default_pool, 128); + i_array_init(&parser->nesting, 8); i_stream_ref(input); return parser; } @@ -111,7 +117,6 @@ i_stream_get_name(parser->input)); } else if (parser->data == parser->end && !i_stream_have_bytes_left(parser->input) && - parser->state != JSON_STATE_ROOT && parser->state != JSON_STATE_DONE) { *error_r = "Missing '}'"; } else { @@ -119,6 +124,7 @@ } i_stream_unref(&parser->input); + array_free(&parser->nesting); str_free(&parser->value); i_free(parser); return *error_r != NULL ? -1 : 0; @@ -318,25 +324,55 @@ return 1; } -static int -json_parse_object_close(struct json_parser *parser, enum json_type *type_r) +static int json_parse_denest(struct json_parser *parser) { + const enum json_state *nested_states; + unsigned count; + parser->data++; json_parser_update_input_pos(parser); - if (parser->nested_object_count > 0) { - /* closing a nested object */ - parser->nested_object_count--; - parser->state = JSON_STATE_OBJECT_VALUE_NEXT; - if (parser->nested_skip_count > 0) { - parser->nested_skip_count--; - return 0; - } - *type_r = JSON_TYPE_OBJECT_END; - return 1; + nested_states = array_get(&parser->nesting, &count); + if (count == 0) { + /* closing root */ + parser->state = JSON_STATE_DONE; + return 0; + } + + /* closing a nested object */ + if (count == 1) { + /* we're back to root */ + parser->state = JSON_STATE_OBJECT_NEXT; + } else { + /* back to previous nested object */ + parser->state = nested_states[count-2] == JSON_STATE_OBJECT_OPEN ? + JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT; } - parser->state = JSON_STATE_DONE; - return 0; + array_delete(&parser->nesting, count-1, 1); + + if (parser->nested_skip_count > 0) { + parser->nested_skip_count--; + return 0; + } + return 1; +} + +static int +json_parse_close_object(struct json_parser *parser, enum json_type *type_r) +{ + if (json_parse_denest(parser) == 0) + return 0; + *type_r = JSON_TYPE_OBJECT_END; + return 1; +} + +static int +json_parse_close_array(struct json_parser *parser, enum json_type *type_r) +{ + if (json_parse_denest(parser) == 0) + return 0; + *type_r = JSON_TYPE_ARRAY_END; + return 1; } static int @@ -360,13 +396,11 @@ json_parser_update_input_pos(parser); return 0; case JSON_STATE_OBJECT_VALUE: - if (*parser->data == '[') { - parser->error = "Arrays not supported"; - return -1; - } else if (*parser->data == '{') { + case JSON_STATE_ARRAY_VALUE: + if (*parser->data == '{') { parser->data++; parser->state = JSON_STATE_OBJECT_OPEN; - parser->nested_object_count++; + array_append(&parser->nesting, &parser->state, 1); json_parser_update_input_pos(parser); if (parser->skipping) { @@ -375,7 +409,20 @@ } *type_r = JSON_TYPE_OBJECT; return 1; + } else if (*parser->data == '[') { + parser->data++; + parser->state = JSON_STATE_ARRAY_OPEN; + array_append(&parser->nesting, &parser->state, 1); + json_parser_update_input_pos(parser); + + if (parser->skipping) { + parser->nested_skip_count++; + return 0; + } + *type_r = JSON_TYPE_ARRAY; + return 1; } + if ((ret = json_parse_string(parser, TRUE, value_r)) >= 0) { *type_r = JSON_TYPE_STRING; } else if ((ret = json_parse_number(parser, value_r)) >= 0) { @@ -398,18 +445,19 @@ if (parser->skipping && *type_r == JSON_TYPE_STRING) { /* a large string that we want to skip over. */ json_parser_update_input_pos(parser); - parser->state = JSON_STATE_SKIP_STRING; + parser->state = parser->state == JSON_STATE_OBJECT_VALUE ? + JSON_STATE_OBJECT_SKIP_STRING : + JSON_STATE_ARRAY_SKIP_STRING; return 0; } return -1; } - parser->state = parser->state == JSON_STATE_ROOT ? - JSON_STATE_DONE : - JSON_STATE_OBJECT_VALUE_NEXT; + parser->state = parser->state == JSON_STATE_OBJECT_VALUE ? + JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT; break; case JSON_STATE_OBJECT_OPEN: if (*parser->data == '}') - return json_parse_object_close(parser, type_r); + return json_parse_close_object(parser, type_r); parser->state = JSON_STATE_OBJECT_KEY; /* fall through */ case JSON_STATE_OBJECT_KEY: @@ -429,13 +477,13 @@ parser->state = JSON_STATE_OBJECT_VALUE; json_parser_update_input_pos(parser); return 0; - case JSON_STATE_OBJECT_VALUE_NEXT: + case JSON_STATE_OBJECT_NEXT: if (parser->skipping && parser->nested_skip_count == 0) { /* we skipped over the previous value */ parser->skipping = FALSE; } if (*parser->data == '}') - return json_parse_object_close(parser, type_r); + return json_parse_close_object(parser, type_r); if (*parser->data != ',') { parser->error = "Expected ',' or '}' after object value"; return -1; @@ -444,10 +492,32 @@ parser->data++; json_parser_update_input_pos(parser); return 0; - case JSON_STATE_SKIP_STRING: + case JSON_STATE_ARRAY_OPEN: + if (*parser->data == ']') + return json_parse_close_array(parser, type_r); + parser->state = JSON_STATE_ARRAY_VALUE; + return 0; + case JSON_STATE_ARRAY_NEXT: + if (parser->skipping && parser->nested_skip_count == 0) { + /* we skipped over the previous value */ + parser->skipping = FALSE; + } + if (*parser->data == ']') + return json_parse_close_array(parser, type_r); + if (*parser->data != ',') { + parser->error = "Expected ',' or '}' after array value"; + return -1; + } + parser->state = JSON_STATE_ARRAY_VALUE; + parser->data++; + json_parser_update_input_pos(parser); + return 0; + case JSON_STATE_OBJECT_SKIP_STRING: + case JSON_STATE_ARRAY_SKIP_STRING: if (json_skip_string(parser) <= 0) return -1; - parser->state = JSON_STATE_OBJECT_VALUE_NEXT; + parser->state = parser->state == JSON_STATE_OBJECT_SKIP_STRING ? + JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT; return 0; case JSON_STATE_DONE: parser->error = "Unexpected data at the end"; @@ -486,7 +556,8 @@ i_assert(!parser->skipping); i_assert(parser->strinput == NULL); i_assert(parser->state == JSON_STATE_OBJECT_COLON || - parser->state == JSON_STATE_OBJECT_VALUE); + parser->state == JSON_STATE_OBJECT_VALUE || + parser->state == JSON_STATE_ARRAY_VALUE); parser->skipping = TRUE; } @@ -521,7 +592,8 @@ parser->data++; json_parser_update_input_pos(parser); - parser->state = JSON_STATE_SKIP_STRING; + parser->state = parser->state == JSON_STATE_OBJECT_VALUE ? + JSON_STATE_OBJECT_SKIP_STRING : JSON_STATE_ARRAY_SKIP_STRING; parser->strinput = i_stream_create_jsonstr(parser->input); i_stream_set_destroy_callback(parser->strinput, json_strinput_destroyed, parser); @@ -538,7 +610,8 @@ i_assert(!parser->skipping); i_assert(parser->strinput == NULL); i_assert(parser->state == JSON_STATE_OBJECT_COLON || - parser->state == JSON_STATE_OBJECT_VALUE); + parser->state == JSON_STATE_OBJECT_VALUE || + parser->state == JSON_STATE_ARRAY_VALUE); *input_r = NULL;
--- a/src/lib/json-parser.h Thu Nov 29 09:38:34 2012 +0200 +++ b/src/lib/json-parser.h Thu Nov 29 10:29:19 2012 +0200 @@ -9,6 +9,9 @@ /* } (not returned for the root object) */ JSON_TYPE_OBJECT_END, + JSON_TYPE_ARRAY, + JSON_TYPE_ARRAY_END, + JSON_TYPE_STRING, JSON_TYPE_NUMBER, JSON_TYPE_TRUE, @@ -16,8 +19,7 @@ JSON_TYPE_NULL }; -/* Parse JSON tokens from the input stream. Currently arrays aren't - supported. */ +/* Parse JSON tokens from the input stream. */ struct json_parser *json_parser_init(struct istream *input); int json_parser_deinit(struct json_parser **parser, const char **error_r);
--- a/src/lib/test-json-parser.c Thu Nov 29 09:38:34 2012 +0200 +++ b/src/lib/test-json-parser.c Thu Nov 29 10:29:19 2012 +0200 @@ -15,7 +15,7 @@ "\"key3\":true," "\"key4\":false," "\"skip1\": \"jsifjaisfjiasji\"," - "\"skip2\": { \"x\":{ \"y\":123}, \"z\":5}," + "\"skip2\": { \"x\":{ \"y\":123}, \"z\":[5,[6],{\"k\":0},3]}," "\"key5\":null," "\"key6\": {}," "\"key7\": {" @@ -27,7 +27,9 @@ " \"sub4\":0.456e-789" "}," "\"key9\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\uffff\"," - "\"key10\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\uffff\"" + "\"key10\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\uffff\"," + "\"key11\": []," + "\"key12\": [ \"foo\" , 5.24,[true],{\"aobj\":[]}]" "}\n"; static struct { @@ -68,7 +70,23 @@ { JSON_TYPE_OBJECT_KEY, "key9" }, { JSON_TYPE_STRING, "foo\\\"\b\f\n\r\t\001\xef\xbf\xbf" }, { JSON_TYPE_OBJECT_KEY, "key10" }, - { TYPE_STREAM, "foo\\\"\b\f\n\r\t\001\xef\xbf\xbf" } + { TYPE_STREAM, "foo\\\"\b\f\n\r\t\001\xef\xbf\xbf" }, + { JSON_TYPE_OBJECT_KEY, "key11" }, + { JSON_TYPE_ARRAY, NULL }, + { JSON_TYPE_ARRAY_END, NULL }, + { JSON_TYPE_OBJECT_KEY, "key12" }, + { JSON_TYPE_ARRAY, NULL }, + { JSON_TYPE_STRING, "foo" }, + { JSON_TYPE_NUMBER, "5.24" }, + { 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_ARRAY_END, NULL } }; static int