mirror of
https://github.com/jorisvink/kore
synced 2025-03-09 12:39:01 -04:00
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.
This commit is contained in:
parent
0031f0271e
commit
9cfcd9a4be
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 *,
|
||||
|
98
src/json.c
98
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);
|
||||
}
|
||||
|
||||
const char *
|
||||
kore_json_strerror(struct kore_json *json)
|
||||
int
|
||||
kore_json_errno(void)
|
||||
{
|
||||
if (json->error >= 0 && json->error <= KORE_JSON_ERR_LAST)
|
||||
return (json_errtab[json->error]);
|
||||
return (json_errno);
|
||||
}
|
||||
|
||||
const char *
|
||||
kore_json_strerror(void)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user