From 4f1cd8af93994afa1f8252bd2f5638a7d4c88c6a Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Thu, 6 Jan 2022 11:50:00 -0800 Subject: [PATCH] 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. --- src/json.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++---- src/json.h | 13 +++++- 2 files changed, 125 insertions(+), 8 deletions(-) diff --git a/src/json.c b/src/json.c index f00b2772..4952c260 100644 --- a/src/json.c +++ b/src/json.c @@ -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; +} diff --git a/src/json.h b/src/json.h index 915b675b..9f00a9d5 100644 --- a/src/json.h +++ b/src/json.h @@ -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);