commit 9cfcd9a4be6e17c24f606440fbd2c224aac892e8
parent 0031f0271eb23f1fb29de7cee97ca63c9e6584e3
Author: Joris Vink <joris@coders.se>
Date: Tue, 30 Mar 2021 14:19:48 +0200
JSON API improvements.
- Try harder to mark integers as KORE_JSON_TYPE_INTEGER, especially if
they fit in the internal representation of one (int64_t).
- Move error codes into the JSON code itself, rather then requiring
a kore_json data structure. This allows the JSON API to relay errors
such as "item not found" or "type mismatch" properly when looking at items.
- When asking for a KORE_JSON_TYPE_INTEGER_U64 and a KORE_JSON_TYPE_INTEGER
was found with the same name, check if it could be returned properly and do
so if possible.
Diffstat:
3 files changed, 83 insertions(+), 33 deletions(-)
diff --git a/examples/json/src/json.c b/examples/json/src/json.c
@@ -30,14 +30,24 @@ page(struct http_request *req)
kore_json_init(&json, req->http_body->data, req->http_body->length);
if (!kore_json_parse(&json)) {
- kore_buf_appendf(&buf, "%s\n", kore_json_strerror(&json));
+ kore_buf_appendf(&buf, "%s\n", kore_json_strerror());
} else {
item = kore_json_find_string(json.root, "foo/bar");
if (item != NULL) {
kore_buf_appendf(&buf,
"foo.bar = '%s'\n", item->data.string);
} else {
- kore_buf_appendf(&buf, "string foo.bar not found\n");
+ kore_buf_appendf(&buf, "foo.bar %s\n",
+ kore_json_strerror());
+ }
+
+ item = kore_json_find_integer_u64(json.root, "foo/integer");
+ if (item != NULL) {
+ kore_buf_appendf(&buf,
+ "foo.integer = '%" PRIu64 "'\n", item->data.u64);
+ } else {
+ kore_buf_appendf(&buf, "foo.integer %s\n",
+ kore_json_strerror());
}
}
diff --git a/include/kore/kore.h b/include/kore/kore.h
@@ -570,7 +570,6 @@ struct kore_buf {
struct kore_json {
const u_int8_t *data;
int depth;
- int error;
size_t length;
size_t offset;
@@ -588,7 +587,7 @@ struct kore_json_item {
char *string;
double number;
int literal;
- int64_t s64;
+ int64_t integer;
u_int64_t u64;
} data;
@@ -1038,13 +1037,14 @@ void kore_buf_appendv(struct kore_buf *, const char *, va_list);
void kore_buf_replace_string(struct kore_buf *,
const char *, const void *, size_t);
+int kore_json_errno(void);
int kore_json_parse(struct kore_json *);
void kore_json_cleanup(struct kore_json *);
void kore_json_item_free(struct kore_json_item *);
void kore_json_init(struct kore_json *, const void *, size_t);
void kore_json_item_tobuf(struct kore_json_item *, struct kore_buf *);
-const char *kore_json_strerror(struct kore_json *);
+const char *kore_json_strerror(void);
struct kore_json_item *kore_json_find(struct kore_json_item *,
const char *, u_int32_t);
struct kore_json_item *kore_json_create_item(struct kore_json_item *,
diff --git a/src/json.c b/src/json.c
@@ -49,6 +49,8 @@ static u_int8_t json_null_literal[] = { 'n', 'u', 'l', 'l' };
static u_int8_t json_true_literal[] = { 't', 'r', 'u', 'e' };
static u_int8_t json_false_literal[] = { 'f', 'a', 'l', 's', 'e' };
+static int json_errno = 0;
+
static const char *json_errtab[] = {
"no error",
"invalid JSON object",
@@ -84,8 +86,10 @@ kore_json_parse(struct kore_json *json)
if (json->root)
return (KORE_RESULT_OK);
+ json_errno = 0;
+
if (json_consume_whitespace(json) == -1) {
- json->error = KORE_JSON_ERR_INVALID_JSON;
+ json_errno = KORE_JSON_ERR_INVALID_JSON;
return (KORE_RESULT_ERROR);
}
@@ -93,22 +97,22 @@ kore_json_parse(struct kore_json *json)
return (KORE_RESULT_ERROR);
if (!json_guess_type(ch, &type)) {
- json->error = KORE_JSON_ERR_INVALID_JSON;
+ json_errno = KORE_JSON_ERR_INVALID_JSON;
return (KORE_RESULT_ERROR);
}
json->root = json_item_alloc(type, NULL, NULL);
if (!json->root->parse(json, json->root)) {
- if (json->error == 0)
- json->error = KORE_JSON_ERR_INVALID_JSON;
+ if (json_errno == 0)
+ json_errno = KORE_JSON_ERR_INVALID_JSON;
return (KORE_RESULT_ERROR);
}
/* Don't allow garbage at the end. */
(void)json_consume_whitespace(json);
if (json->offset != json->length) {
- json->error = KORE_JSON_ERR_INVALID_JSON;
+ json_errno = KORE_JSON_ERR_INVALID_JSON;
return (KORE_RESULT_ERROR);
}
@@ -122,16 +126,21 @@ kore_json_find(struct kore_json_item *root, const char *path, u_int32_t type)
char *copy;
char *tokens[KORE_JSON_DEPTH_MAX + 1];
+ json_errno = 0;
copy = kore_strdup(path);
if (!kore_split_string(copy, "/", tokens, KORE_JSON_DEPTH_MAX)) {
kore_free(copy);
+ json_errno = KORE_JSON_ERR_INVALID_SEARCH;
return (NULL);
}
item = json_find_item(root, tokens, type, 0);
kore_free(copy);
+ if (item == NULL && json_errno == 0)
+ json_errno = KORE_JSON_ERR_INVALID_SEARCH;
+
return (item);
}
@@ -145,11 +154,17 @@ kore_json_cleanup(struct kore_json *json)
kore_json_item_free(json->root);
}
+int
+kore_json_errno(void)
+{
+ return (json_errno);
+}
+
const char *
-kore_json_strerror(struct kore_json *json)
+kore_json_strerror(void)
{
- if (json->error >= 0 && json->error <= KORE_JSON_ERR_LAST)
- return (json_errtab[json->error]);
+ if (json_errno >= 0 && json_errno <= KORE_JSON_ERR_LAST)
+ return (json_errtab[json_errno]);
return ("unknown JSON error");
}
@@ -182,7 +197,7 @@ kore_json_create_item(struct kore_json_item *parent, const char *name,
item->data.number = va_arg(args, double);
break;
case KORE_JSON_TYPE_INTEGER:
- item->data.s64 = va_arg(args, int64_t);
+ item->data.integer = va_arg(args, int64_t);
break;
case KORE_JSON_TYPE_INTEGER_U64:
item->data.u64 = va_arg(args, u_int64_t);
@@ -248,7 +263,7 @@ kore_json_item_tobuf(struct kore_json_item *item, struct kore_buf *buf)
kore_buf_appendf(buf, "%f", item->data.number);
break;
case KORE_JSON_TYPE_INTEGER:
- kore_buf_appendf(buf, "%" PRId64, item->data.s64);
+ kore_buf_appendf(buf, "%" PRId64, item->data.integer);
break;
case KORE_JSON_TYPE_INTEGER_U64:
kore_buf_appendf(buf, "%" PRIu64, item->data.u64);
@@ -321,15 +336,33 @@ json_find_item(struct kore_json_item *object, char **tokens,
break;
}
- if (nitem == NULL)
+ if (nitem == NULL) {
+ json_errno = KORE_JSON_ERR_NOT_FOUND;
return (NULL);
+ }
item = nitem;
}
if (tokens[pos + 1] == NULL) {
+ /*
+ * If an uint64 was required and we find an item
+ * with the same name but marked as an integer check
+ * if it can be represented as a uint64.
+ *
+ * If it can, reduce the type to integer so we match
+ * on it as well.
+ */
+ if (type == KORE_JSON_TYPE_INTEGER_U64 &&
+ item->type == KORE_JSON_TYPE_INTEGER) {
+ if (item->data.integer >= 0)
+ type = KORE_JSON_TYPE_INTEGER;
+ }
+
if (item->type == type)
return (item);
+
+ json_errno = KORE_JSON_ERR_TYPE_MISMATCH;
return (NULL);
}
@@ -343,6 +376,9 @@ json_find_item(struct kore_json_item *object, char **tokens,
break;
}
+ if (item == NULL && json_errno == 0)
+ json_errno = KORE_JSON_ERR_NOT_FOUND;
+
return (item);
}
@@ -443,7 +479,7 @@ static int
json_next_byte(struct kore_json *json, u_int8_t *ch, int peek)
{
if (json->offset >= json->length) {
- json->error = KORE_JSON_ERR_EOF;
+ json_errno = KORE_JSON_ERR_EOF;
return (KORE_RESULT_ERROR);
}
@@ -513,7 +549,7 @@ json_parse_object(struct kore_json *json, struct kore_json_item *object)
int ret, hasnext;
if (json->depth++ >= KORE_JSON_DEPTH_MAX) {
- json->error = KORE_JSON_ERR_DEPTH;
+ json_errno = KORE_JSON_ERR_DEPTH;
return (KORE_RESULT_ERROR);
}
@@ -537,7 +573,7 @@ json_parse_object(struct kore_json *json, struct kore_json_item *object)
switch (ch) {
case '}':
if (hasnext) {
- json->error = KORE_JSON_ERR_INVALID_JSON;
+ json_errno = KORE_JSON_ERR_INVALID_JSON;
goto cleanup;
}
json->offset++;
@@ -596,8 +632,8 @@ json_parse_object(struct kore_json *json, struct kore_json_item *object)
}
cleanup:
- if (ret == KORE_RESULT_ERROR && json->error == 0)
- json->error = KORE_JSON_ERR_INVALID_OBJECT;
+ if (ret == KORE_RESULT_ERROR && json_errno == 0)
+ json_errno = KORE_JSON_ERR_INVALID_OBJECT;
json->depth--;
@@ -614,7 +650,7 @@ json_parse_array(struct kore_json *json, struct kore_json_item *array)
int ret, hasnext;
if (json->depth++ >= KORE_JSON_DEPTH_MAX) {
- json->error = KORE_JSON_ERR_DEPTH;
+ json_errno = KORE_JSON_ERR_DEPTH;
return (KORE_RESULT_ERROR);
}
@@ -637,7 +673,7 @@ json_parse_array(struct kore_json *json, struct kore_json_item *array)
if (ch == ']') {
if (hasnext) {
- json->error = KORE_JSON_ERR_INVALID_JSON;
+ json_errno = KORE_JSON_ERR_INVALID_JSON;
goto cleanup;
}
json->offset++;
@@ -675,8 +711,8 @@ json_parse_array(struct kore_json *json, struct kore_json_item *array)
}
cleanup:
- if (ret == KORE_RESULT_ERROR && json->error == 0)
- json->error = KORE_JSON_ERR_INVALID_ARRAY;
+ if (ret == KORE_RESULT_ERROR && json_errno == 0)
+ json_errno = KORE_JSON_ERR_INVALID_ARRAY;
json->depth--;
@@ -762,10 +798,14 @@ json_parse_number(struct kore_json *json, struct kore_json_item *number)
kore_strtodouble(str, -DBL_MAX, DBL_MAX, &ret);
break;
case KORE_JSON_TYPE_INTEGER:
- number->data.s64 = (int64_t)kore_strtonum64(str, 1, &ret);
+ number->data.integer = (int64_t)kore_strtonum64(str, 1, &ret);
break;
case KORE_JSON_TYPE_INTEGER_U64:
- number->data.s64 = kore_strtonum64(str, 0, &ret);
+ number->data.u64 = kore_strtonum64(str, 0, &ret);
+ if (number->data.u64 <= INT64_MAX) {
+ type = KORE_JSON_TYPE_INTEGER;
+ number->data.integer = number->data.u64;
+ }
break;
default:
goto cleanup;
@@ -774,8 +814,8 @@ json_parse_number(struct kore_json *json, struct kore_json_item *number)
number->type = type;
cleanup:
- if (ret == KORE_RESULT_ERROR && json->error == 0)
- json->error = KORE_JSON_ERR_INVALID_NUMBER;
+ if (ret == KORE_RESULT_ERROR && json_errno == 0)
+ json_errno = KORE_JSON_ERR_INVALID_NUMBER;
return (ret);
}
@@ -826,8 +866,8 @@ json_parse_literal(struct kore_json *json, struct kore_json_item *literal)
ret = KORE_RESULT_OK;
cleanup:
- if (ret == KORE_RESULT_ERROR && json->error == 0)
- json->error = KORE_JSON_ERR_INVALID_LITERAL;
+ if (ret == KORE_RESULT_ERROR && json_errno == 0)
+ json_errno = KORE_JSON_ERR_INVALID_LITERAL;
return (ret);
}
@@ -895,8 +935,8 @@ json_get_string(struct kore_json *json)
res = kore_buf_stringify(&json->tmpbuf, NULL);
cleanup:
- if (res == NULL && json->error == 0)
- json->error = KORE_JSON_ERR_INVALID_STRING;
+ if (res == NULL && json_errno == 0)
+ json_errno = KORE_JSON_ERR_INVALID_STRING;
return (res);
}