Add validation features to json_unpack()
* By default, json_unpack() now checks that all items of arrays and objects are unpacked. This is useful for validation. * Add format specifier '*' to suppress this check for individual arrays and objects. '*' must appear as the last format specifier before the closing ']' or '}'.
This commit is contained in:
parent
6825c2c706
commit
2770dca2c0
@ -57,6 +57,9 @@ typedef struct {
|
|||||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||||
|
|
||||||
|
size_t jsonp_hash_key(const void *ptr);
|
||||||
|
int jsonp_key_equal(const void *ptr1, const void *ptr2);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t serial;
|
size_t serial;
|
||||||
char key[1];
|
char key[1];
|
||||||
|
@ -179,9 +179,18 @@ 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)
|
static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||||
{
|
{
|
||||||
|
int ret = -1;
|
||||||
|
int wildcard = 0;
|
||||||
|
hashtable_t key_set;
|
||||||
|
|
||||||
|
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)) {
|
if(!json_is_object(root)) {
|
||||||
set_error(s, "Expected object, got %i", json_typeof(root));
|
set_error(s, "Expected object, got %i", json_typeof(root));
|
||||||
return -1;
|
goto error;
|
||||||
}
|
}
|
||||||
next_token(s);
|
next_token(s);
|
||||||
|
|
||||||
@ -189,37 +198,59 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
|||||||
const char *key;
|
const char *key;
|
||||||
json_t *value;
|
json_t *value;
|
||||||
|
|
||||||
|
if(wildcard) {
|
||||||
|
set_error(s, "Expected '}', got '%c'", s->token);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
if(!s->token) {
|
if(!s->token) {
|
||||||
set_error(s, "Unexpected end of format string");
|
set_error(s, "Unexpected end of format string");
|
||||||
return -1;
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s->token == '*') {
|
||||||
|
wildcard = 1;
|
||||||
|
next_token(s);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(s->token != 's') {
|
if(s->token != 's') {
|
||||||
set_error(s, "Expected format 's', got '%c'\n", *s->fmt);
|
set_error(s, "Expected format 's', got '%c'\n", *s->fmt);
|
||||||
return -1;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
key = va_arg(*ap, const char *);
|
key = va_arg(*ap, const char *);
|
||||||
if(!key) {
|
if(!key) {
|
||||||
set_error(s, "NULL object key");
|
set_error(s, "NULL object key");
|
||||||
return -1;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
next_token(s);
|
next_token(s);
|
||||||
|
|
||||||
value = json_object_get(root, key);
|
value = json_object_get(root, key);
|
||||||
if(unpack(s, value, ap))
|
if(unpack(s, value, ap))
|
||||||
return -1;
|
goto error;
|
||||||
|
|
||||||
|
hashtable_set(&key_set, (void *)key, NULL);
|
||||||
next_token(s);
|
next_token(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
if(!wildcard && 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;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
hashtable_close(&key_set);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
|
static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
int wildcard = 0;
|
||||||
|
|
||||||
if(!json_is_array(root)) {
|
if(!json_is_array(root)) {
|
||||||
set_error(s, "Expected array, got %d", json_typeof(root));
|
set_error(s, "Expected array, got %d", json_typeof(root));
|
||||||
@ -230,11 +261,22 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
|
|||||||
while(s->token != ']') {
|
while(s->token != ']') {
|
||||||
json_t *value;
|
json_t *value;
|
||||||
|
|
||||||
|
if(wildcard) {
|
||||||
|
set_error(s, "Expected ']', got '%c'", s->token);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if(!s->token) {
|
if(!s->token) {
|
||||||
set_error(s, "Unexpected end of format string");
|
set_error(s, "Unexpected end of format string");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(s->token == '*') {
|
||||||
|
wildcard = 1;
|
||||||
|
next_token(s);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
value = json_array_get(root, i);
|
value = json_array_get(root, i);
|
||||||
if(!value) {
|
if(!value) {
|
||||||
set_error(s, "Array index %lu out of range", (unsigned long)i);
|
set_error(s, "Array index %lu out of range", (unsigned long)i);
|
||||||
@ -248,9 +290,9 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(i != json_array_size(root)) {
|
if(!wildcard && i != json_array_size(root)) {
|
||||||
long diff = (long)json_array_size(root) - (long)i;
|
long diff = (long)json_array_size(root) - (long)i;
|
||||||
set_error(s, "%li array items were not upacked", diff);
|
set_error(s, "%li array items left upacked", diff);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ static JSON_INLINE void json_init(json_t *json, json_type type)
|
|||||||
an object_key_t instance. */
|
an object_key_t instance. */
|
||||||
#define string_to_key(string) container_of(string, object_key_t, key)
|
#define string_to_key(string) container_of(string, object_key_t, key)
|
||||||
|
|
||||||
static size_t hash_key(const void *ptr)
|
size_t jsonp_hash_key(const void *ptr)
|
||||||
{
|
{
|
||||||
const char *str = ((const object_key_t *)ptr)->key;
|
const char *str = ((const object_key_t *)ptr)->key;
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ static size_t hash_key(const void *ptr)
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int key_equal(const void *ptr1, const void *ptr2)
|
int jsonp_key_equal(const void *ptr1, const void *ptr2)
|
||||||
{
|
{
|
||||||
return strcmp(((const object_key_t *)ptr1)->key,
|
return strcmp(((const object_key_t *)ptr1)->key,
|
||||||
((const object_key_t *)ptr2)->key) == 0;
|
((const object_key_t *)ptr2)->key) == 0;
|
||||||
@ -66,7 +66,8 @@ json_t *json_object(void)
|
|||||||
return NULL;
|
return NULL;
|
||||||
json_init(&object->json, JSON_OBJECT);
|
json_init(&object->json, JSON_OBJECT);
|
||||||
|
|
||||||
if(hashtable_init(&object->hashtable, hash_key, key_equal,
|
if(hashtable_init(&object->hashtable,
|
||||||
|
jsonp_hash_key, jsonp_key_equal,
|
||||||
free, value_decref))
|
free, value_decref))
|
||||||
{
|
{
|
||||||
free(object);
|
free(object);
|
||||||
|
Loading…
Reference in New Issue
Block a user