json: add support for primitive types

This adds support for boolean, (unsigned) integers, and
null types. JSON_PRIMITIVE should be used as the type when
parsing and the value should be struct json_iter.

Once parsed the actual value can be obtained using one of
the primitive getters. If the type does not match they will
return false.

If using JSON_OPTIONAL with JSON_PRIMITIVE the resulting
iterator can be checked with json_iter_is_valid. If false
the key/value was not found or the type was not matching.
This commit is contained in:
James Prestwood 2022-01-06 11:50:00 -08:00 committed by Denis Kenzior
parent 23a1a66aee
commit 4f1cd8af93
2 changed files with 125 additions and 8 deletions

View File

@ -82,14 +82,14 @@ static int count_tokens_in_container(struct json_iter *iter,
return contents - container;
}
static void iter_recurse(struct json_iter *iter, jsmntok_t *object,
static void iter_recurse(struct json_iter *iter, jsmntok_t *token,
struct json_iter *child)
{
struct json_contents *c = iter->contents;
child->contents = c;
child->start = object - c->tokens;
child->count = count_tokens_in_container(iter, object);
child->start = token - c->tokens;
child->count = count_tokens_in_container(iter, token);
}
struct json_contents *json_contents_new(const char *json, size_t json_len)
@ -150,7 +150,7 @@ static void assign_arg(void *data, void *user_data)
struct json_arg *arg = data;
struct json_contents *c = iter->contents;
char **sval;
struct json_iter *oval;
struct json_iter *iter_val;
switch (arg->type) {
case JSON_STRING:
@ -160,12 +160,13 @@ static void assign_arg(void *data, void *user_data)
break;
case JSON_OBJECT:
oval = arg->value;
case JSON_PRIMITIVE:
iter_val = arg->value;
if (!arg->v)
oval->start = -1;
iter_val->start = -1;
else
iter_recurse(iter, arg->v, oval);
iter_recurse(iter, arg->v, iter_val);
break;
default:
@ -206,6 +207,7 @@ bool json_iter_parse(struct json_iter *iter, enum json_type type, ...)
goto done;
case JSON_STRING:
case JSON_OBJECT:
case JSON_PRIMITIVE:
break;
default:
goto error;
@ -265,3 +267,107 @@ error:
l_queue_destroy(args, l_free);
return false;
}
static bool iter_get_primitive_data(struct json_iter *iter, void **ptr,
size_t *len)
{
struct json_contents *c = iter->contents;
jsmntok_t *t = c->tokens + iter->start;
if (t->type != JSMN_PRIMITIVE)
return false;
*ptr = TOK_PTR(c->json, t);
*len = TOK_LEN(t);
return true;
}
bool json_iter_get_int(struct json_iter *iter, int *i)
{
void *ptr;
size_t len;
long int r;
int t;
char *endp;
if (!iter_get_primitive_data(iter, &ptr, &len))
return false;
errno = 0;
t = r = strtol(ptr, &endp, 10);
if (endp != ptr + len)
return false;
if (errno == ERANGE || r != t)
return false;
if (i)
*i = r;
return true;
}
bool json_iter_get_uint(struct json_iter *iter, unsigned int *i)
{
void *ptr;
size_t len;
unsigned long int r;
unsigned int t;
char *endp;
if (!iter_get_primitive_data(iter, &ptr, &len))
return false;
errno = 0;
t = r = strtoul(ptr, &endp, 10);
if (endp != ptr + len)
return false;
if (errno == ERANGE || r != t)
return false;
if (i)
*i = r;
return true;
}
bool json_iter_get_boolean(struct json_iter *iter, bool *b)
{
void *ptr;
size_t len;
if (!iter_get_primitive_data(iter, &ptr, &len))
return false;
if (len == 4 && !memcmp(ptr, "true", 4)) {
if (b)
*b = true;
return true;
} else if (len == 5 && !memcmp(ptr, "false", 5)) {
if (b)
*b = false;
return true;
}
return false;
}
bool json_iter_get_null(struct json_iter *iter)
{
void *ptr;
size_t len;
if (!iter_get_primitive_data(iter, &ptr, &len))
return false;
if (len == 4 && !memcmp(ptr, "null", 4))
return true;
return false;
}

View File

@ -73,11 +73,22 @@ void json_iter_init(struct json_iter *iter, struct json_contents *c);
*
* String values should be of type char ** and must be freed
* Object values should be of type struct json_iter *
* Primitive types (numbers, booleans, null) should be of type
* struct json_iter *. This is to allow the caller to distinguish
* between the actual value type after parsing using a getter for
* the expected type (get_uint/get_int/get_boolean etc.). In
* addition this lets the caller use JSON_OPTIONAL and check post
* json_iter_parse if the iterator is valid (json_iter_is_valid).
*
* No other types are supported at this time, and json_iter_parse will fail if
* other types are encountered.
*
* JSON_OPTIONAL string values will point to NULL if not found
* JSON_OPTIONAL objects can be checked with json_object_not_found.
* JSON_OPTIONAL objects/primitives can be checked with json_object_is_valid.
*/
bool json_iter_parse(struct json_iter *iter, enum json_type type, ...);
bool json_iter_get_int(struct json_iter *iter, int *i);
bool json_iter_get_uint(struct json_iter *iter, unsigned int *i);
bool json_iter_get_boolean(struct json_iter *iter, bool *b);
bool json_iter_get_null(struct json_iter *iter);