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:
Petri Lehtinen 2011-01-24 21:45:54 +02:00
parent 6825c2c706
commit 2770dca2c0
3 changed files with 57 additions and 11 deletions

View File

@ -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];

View File

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

View File

@ -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);