diff --git a/doc/apiref.rst b/doc/apiref.rst index 11041d4..fc839ab 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -849,6 +849,7 @@ denotes the C type that is expected as the corresponding argument. fourth, etc. format character represent a value. Any value may be an object or array, i.e. recursive value building is supported. +The following functions compose the value building API: .. function:: json_t *json_pack(const char *fmt, ...) @@ -896,10 +897,10 @@ and extract, or *unpack*, data from them. Like :ref:`building values While a JSON value is unpacked, the type specified in the format string is checked to match that of the JSON value. This is the -validation part of the process. By default, the unpacking functions -also check that all items of arrays and objects are unpacked. This -check be disabled with the format character ``*`` or by using the flag -``JSON_UNPACK_ONLY``. +validation part of the process. In addition to this, the unpacking +functions can also check that all items of arrays and objects are +unpacked. This check be enabled with the format character ``!`` or by +using the flag ``JSON_STRICT``. See below for details. Here's the full list of format characters. The type in parentheses denotes the JSON type, and the type in brackets (if any) denotes the C @@ -951,12 +952,21 @@ type whose address should be passed. ``fmt`` may contain objects and arrays as values, i.e. recursive value extraction is supporetd. -``*`` - This special format character is used to disable the check that - all object and array items are accessed on a per-value basis. It +``!`` + This special format character is used to enable the check that + all object and array items are accessed, on a per-value basis. It must appear inside an array or object as the last format character - before the closing bracket or brace. + before the closing bracket or brace. To enable the check globally, + use the ``JSON_STRICT`` unpacking flag. +``*`` + This special format character is the opposite of ``!``. If the + ``JSON_STRICT`` flag is used, ``*`` can be used to disable the + strict check on a per-value basis. It must appear inside an array + or object as the last format character before the closing bracket + or brace. + +The following functions compose the parsing and validation API: .. function:: int json_unpack(json_t *root, const char *fmt, ...) @@ -974,11 +984,11 @@ type whose address should be passed. The following unpacking flags are available: -``JSON_UNPACK_ONLY`` - Disable the validation step checking that all object and array - items are unpacked. This is equivalent to appending the format - character ``*`` to the end of every array and object in the format - string. +``JSON_STRICT`` + Enable the extra validation step checking that all object and + array items are unpacked. This is equivalent to appending the + format character ``!`` to the end of every array and object in the + format string. ``JSON_VALIDATE_ONLY`` Don't extract any data, just validate the JSON value against the diff --git a/src/jansson.h b/src/jansson.h index c76fbe2..248176e 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -195,7 +195,7 @@ json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...); json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap); #define JSON_VALIDATE_ONLY 0x1 -#define JSON_UNPACK_ONLY 0x2 +#define JSON_STRICT 0x2 int json_unpack(json_t *root, const char *fmt, ...); int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...); diff --git a/src/pack_unpack.c b/src/pack_unpack.c index e785357..758e2f9 100644 --- a/src/pack_unpack.c +++ b/src/pack_unpack.c @@ -192,26 +192,23 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap); static int unpack_object(scanner_t *s, json_t *root, va_list *ap) { int ret = -1; - int wildcard = 0; + int strict = 0; /* Use a set (emulated by a hashtable) to check that all object keys are accessed. Checking that the correct number of keys were accessed is not enough, as the same key can be unpacked multiple times. */ - hashtable_t *key_set; + hashtable_t key_set; - if(!(s->flags & JSON_UNPACK_ONLY)) { - key_set = hashtable_create(jsonp_hash_key, jsonp_key_equal, NULL, NULL); - if(!key_set) { - set_error(s, "Out of memory"); - return -1; - } + if(hashtable_init(&key_set, jsonp_hash_key, jsonp_key_equal, NULL, NULL)) { + set_error(s, "Out of memory"); + return -1; } if(!json_is_object(root)) { set_error(s, "Expected object, got %s", type_name(root)); - goto error; + goto out; } next_token(s); @@ -219,67 +216,64 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) const char *key; json_t *value; - if(wildcard) { - set_error(s, "Expected '}' after '*', got '%c'", s->token); - goto error; + if(strict != 0) { + set_error(s, "Expected '}' after '%c', got '%c'", + (strict == 1 ? '!' : '*'), s->token); + goto out; } if(!s->token) { set_error(s, "Unexpected end of format string"); - goto error; + goto out; } - if(s->token == '*') { - wildcard = 1; + if(s->token == '!' || s->token == '*') { + strict = (s->token == '!' ? 1 : -1); next_token(s); continue; } if(s->token != 's') { set_error(s, "Expected format 's', got '%c'\n", *s->fmt); - goto error; + goto out; } key = va_arg(*ap, const char *); if(!key) { set_error(s, "NULL object key"); - goto error; + goto out; } next_token(s); value = json_object_get(root, key); if(unpack(s, value, ap)) - goto error; - - if(!(s->flags & JSON_UNPACK_ONLY)) - hashtable_set(key_set, (void *)key, NULL); + goto out; + hashtable_set(&key_set, (void *)key, NULL); next_token(s); } - if(s->flags & JSON_UNPACK_ONLY) - wildcard = 1; + if(strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; - if(!wildcard && key_set->size != json_object_size(root)) { - long diff = (long)json_object_size(root) - (long)key_set->size; + if(strict == 1 && key_set.size != json_object_size(root)) { + long diff = (long)json_object_size(root) - (long)key_set.size; set_error(s, "%li object items left unpacked", diff); - goto error; + goto out; } ret = 0; -error: - if(!(s->flags & JSON_UNPACK_ONLY)) - hashtable_destroy(key_set); - +out: + hashtable_close(&key_set); return ret; } static int unpack_array(scanner_t *s, json_t *root, va_list *ap) { size_t i = 0; - int wildcard = 0; + int strict = 0; if(!json_is_array(root)) { set_error(s, "Expected array, got %s", type_name(root)); @@ -290,8 +284,10 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) while(s->token != ']') { json_t *value; - if(wildcard) { - set_error(s, "Expected ']' after '*', got '%c'", s->token); + if(strict != 0) { + set_error(s, "Expected ']' after '%c', got '%c'", + (strict == 1 ? '!' : '*'), + s->token); return -1; } @@ -300,8 +296,8 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) return -1; } - if(s->token == '*') { - wildcard = 1; + if(s->token == '!' || s->token == '*') { + strict = (s->token == '!' ? 1 : -1); next_token(s); continue; } @@ -319,10 +315,10 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) i++; } - if(s->flags & JSON_UNPACK_ONLY) - wildcard = 1; + if(strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; - if(!wildcard && i != json_array_size(root)) { + if(strict == 1 && i != json_array_size(root)) { long diff = (long)json_array_size(root) - (long)i; set_error(s, "%li array items left upacked", diff); return -1;