From bf4af27e54345e280223257161448ace0f0b1d67 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Wed, 13 Jan 2010 15:44:10 +0100 Subject: [PATCH] More JSON utilities New functions json_get_object, json_detach_object, json_get_elem, json_count_children, json_parser_subst, json_append_array. --- include/yaz/json.h | 59 ++++++++++++++++++++ src/json.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++-- test/tst_json.c | 77 ++++++++++++++++++++++++++ 3 files changed, 286 insertions(+), 5 deletions(-) diff --git a/include/yaz/json.h b/include/yaz/json.h index b56ee4c..4cefba3 100644 --- a/include/yaz/json.h +++ b/include/yaz/json.h @@ -93,12 +93,71 @@ struct json_node *json_parser_parse(json_parser_t p, const char *json_str); YAZ_EXPORT const char *json_parser_get_errmsg(json_parser_t p); +/** \brief parses JSON string + \param json_str JSON string + \param errmsg pointer to error message string + \returns JSON tree or NULL if parse error occurred. + + The resulting tree should be removed with a call to json_remove_node. + The errmsg may be NULL in which case the error message is not returned. +*/ +YAZ_EXPORT +struct json_node *json_parse(const char *json_str, const char **errmsg); + /** \brief destroys JSON tree node and its children \param n JSON node */ YAZ_EXPORT void json_remove_node(struct json_node *n); +/** \brief gets object pair value for some name + \param n JSON node (presumably object node) + \param name name to match + \returns node or NULL if not found +*/ +YAZ_EXPORT +struct json_node *json_get_object(struct json_node *n, const char *name); + +/** \brief gets object value and detaches from existing tree + \param n JSON node (presumably object node) + \param name name to match + \returns node or NULL if not found +*/ +YAZ_EXPORT +struct json_node *json_detach_object(struct json_node *n, const char *name); + +/** \brief gets array element + \param n JSON node (presumably array node) + \param idx (0=first, 1=second, ..) + \returns node or NULL if not found +*/ +YAZ_EXPORT +struct json_node *json_get_elem(struct json_node *n, int idx); + +/** \brief returns number of children (array or object) + \param n JSON node (presumably array node or object node) + \returns number of children +*/ +YAZ_EXPORT +int json_count_children(struct json_node *n); + +/** \brief appends array to another + \param dst original array and resulting array + \param src array to be appended to dst + \retval -1 not arrays + \retval 0 array appended OK +*/ +YAZ_EXPORT +int json_append_array(struct json_node *dst, struct json_node *src); + +/** \brief configure subst rule + \param p JSON parser + \param idx (%id) + \param node node to be substituted for idx (%idx) +*/ +YAZ_EXPORT +void json_parser_subst(json_parser_t p, int idx, struct json_node *n); + /** \brief converts JSON tree to JSON string \param node JSON tree \param result resulting JSON string buffer diff --git a/src/json.c b/src/json.c index 41b99eb..a2d4f1d 100644 --- a/src/json.c +++ b/src/json.c @@ -18,10 +18,17 @@ #include +struct json_subst_info { + int idx; + struct json_subst_info *next; + struct json_node *node; +}; + struct json_parser_s { const char *buf; const char *cp; const char *err_msg; + struct json_subst_info *subst; }; json_parser_t json_parser_create(void) @@ -31,11 +38,34 @@ json_parser_t json_parser_create(void) p->buf = 0; p->cp = 0; p->err_msg = 0; + p->subst = 0; return p; } +void json_parser_subst(json_parser_t p, int idx, struct json_node *n) +{ + struct json_subst_info **sb = &p->subst; + for (; *sb; sb = &(*sb)->next) + if ((*sb)->idx == idx) + { + (*sb)->node = n; + return; + } + *sb = xmalloc(sizeof(**sb)); + (*sb)->next = 0; + (*sb)->node = n; + (*sb)->idx = idx; +} + void json_parser_destroy(json_parser_t p) { + struct json_subst_info *sb = p->subst; + while (sb) + { + struct json_subst_info *sb_next = sb->next; + xfree(sb); + sb = sb_next; + } xfree(p); } @@ -212,6 +242,22 @@ static struct json_node *json_parse_value(json_parser_t p) return json_parse_object(p); else if (c == '[') return json_parse_array(p); + else if (c == '%') + { + struct json_subst_info *sb; + int idx = 0; + p->cp++; + c = *p->cp; + while (c >= '0' && c <= '9') + { + idx = idx*10 + (c - '0'); + p->cp++; + c = *p->cp; + } + for (sb = p->subst; sb; sb = sb->next) + if (sb->idx == idx) + return sb->node; + } else { char tok[8]; @@ -229,12 +275,9 @@ static struct json_node *json_parse_value(json_parser_t p) return json_new_node(p, json_node_false); else if (!strcmp(tok, "null")) return json_new_node(p, json_node_null); - else - { - p->err_msg = "bad value"; - return 0; - } } + p->err_msg = "bad token"; + return 0; } static struct json_node *json_parse_elements(json_parser_t p) @@ -295,6 +338,7 @@ static struct json_node *json_parse_pair(json_parser_t p) return 0; if (look_ch(p) != ':') { + p->err_msg = "missing :"; json_remove_node(s); return 0; } @@ -377,6 +421,8 @@ struct json_node *json_parser_parse(json_parser_t p, const char *json_str) p->cp = p->buf; n = json_parse_object(p); + if (!n) + return 0; c = look_ch(p); if (c != 0) { @@ -387,6 +433,25 @@ struct json_node *json_parser_parse(json_parser_t p, const char *json_str) return n; } +struct json_node *json_parse(const char *json_str, const char **errmsg) +{ + json_parser_t p = json_parser_create(); + struct json_node *n = 0; + if (!p) + { + if (errmsg) + *errmsg = "could not create parser"; + } + else + { + n = json_parser_parse(p, json_str); + if (!n && errmsg) + *errmsg = json_parser_get_errmsg(p); + json_parser_destroy(p); + } + return n; +} + void json_write_wrbuf(struct json_node *node, WRBUF result) { switch (node->type) @@ -436,6 +501,86 @@ void json_write_wrbuf(struct json_node *node, WRBUF result) } } +static struct json_node **json_get_objectp(struct json_node *n, + const char *name) +{ + if (n && n->type == json_node_object) + { + for (n = n->u.link[0]; n; n = n->u.link[1]) + { + struct json_node *c = n->u.link[0]; + if (c && c->type == json_node_pair && + c->u.link[0] && c->u.link[0]->type == json_node_string) + if (!strcmp(name, c->u.link[0]->u.string)) + return &c->u.link[1]; + } + } + return 0; +} + +struct json_node *json_get_object(struct json_node *n, const char *name) +{ + struct json_node **np = json_get_objectp(n, name); + + if (np) + return *np; + return 0; +} + +struct json_node *json_detach_object(struct json_node *n, const char *name) +{ + struct json_node **np = json_get_objectp(n, name); + + if (np) + { + struct json_node *n = *np; + *np = 0; + return n; + } + return 0; +} + +struct json_node *json_get_elem(struct json_node *n, int idx) +{ + if (n && n->type == json_node_array) + { + for (n = n->u.link[0]; n; n = n->u.link[1]) + { + if (--idx < 0) + return n->u.link[0]; + } + } + return 0; +} + +int json_count_children(struct json_node *n) +{ + int i = 0; + + if (n && (n->type == json_node_array || n->type == json_node_object)) + { + for (n = n->u.link[0]; n; n = n->u.link[1]) + i++; + } + return i; +} + +int json_append_array(struct json_node *dst, struct json_node *src) +{ + if (dst && src && + dst->type == json_node_array && src->type == json_node_array) + { + struct json_node **np = &dst->u.link[0]; + while (*np) + np = &(*np)->u.link[1]; + *np = src->u.link[0]; + src->u.link[0] = 0; + json_remove_node(src); + return 0; + } + return -1; +} + const char *json_parser_get_errmsg(json_parser_t p) { return p->err_msg; diff --git a/test/tst_json.c b/test/tst_json.c index d20beba..8720290 100644 --- a/test/tst_json.c +++ b/test/tst_json.c @@ -117,10 +117,87 @@ static void tst1(void) json_parser_destroy(p); } +static void tst2(void) +{ + struct json_node *n, *n1; + + n = json_parse("{\"a\":1,\"b\":2,\"c\":[true,false,null]}", 0); + YAZ_CHECK(n); + if (!n) + return; + + YAZ_CHECK_EQ(json_count_children(n), 3); + + n1 = json_get_object(n, "a"); + YAZ_CHECK(n1 && n1->type == json_node_number && n1->u.number == 1.0); + YAZ_CHECK_EQ(json_count_children(n1), 0); + + n1 = json_get_object(n, "b"); + YAZ_CHECK(n1 && n1->type == json_node_number && n1->u.number == 2.0); + YAZ_CHECK_EQ(json_count_children(n1), 0); + + n1 = json_get_object(n, "b"); + YAZ_CHECK(n1 && n1->type == json_node_number && n1->u.number == 2.0); + YAZ_CHECK_EQ(json_count_children(n1), 0); + + n1 = json_get_object(n, "c"); + YAZ_CHECK(n1 && n1->type == json_node_array); + YAZ_CHECK_EQ(json_count_children(n1), 3); + + n1 = json_get_elem(json_get_object(n, "c"), 0); + YAZ_CHECK(n1 && n1->type == json_node_true); + + n1 = json_get_elem(json_get_object(n, "c"), 1); + YAZ_CHECK(n1 && n1->type == json_node_false); + + n1 = json_get_elem(json_get_object(n, "c"), 2); + YAZ_CHECK(n1 && n1->type == json_node_null); + + n1 = json_get_elem(json_get_object(n, "c"), 3); + YAZ_CHECK(n1 == 0); + + json_remove_node(n); +} + +static int append_check(const char *a, const char *b, const char *exp) +{ + WRBUF w = wrbuf_alloc(); + struct json_node *n_a, *n_b; + int ret = 0; + + n_a = json_parse(a, 0); + n_b = json_parse(b, 0); + json_append_array(json_get_object(n_a, "a"), + json_detach_object(n_b, "b")); + + json_write_wrbuf(n_a, w); + + if (!strcmp(wrbuf_cstr(w), exp)) + ret = 1; + wrbuf_destroy(w); + json_remove_node(n_a); + json_remove_node(n_b); + return ret; +} + +static void tst3(void) +{ + YAZ_CHECK(append_check("{\"a\":[1,2,3]}", "{\"b\":[5,6,7]}", + "{\"a\":[1,2,3,5,6,7]}")); + + YAZ_CHECK(append_check("{\"a\":[]}", "{\"b\":[5,6,7]}", + "{\"a\":[5,6,7]}")); + + YAZ_CHECK(append_check("{\"a\":[1,2,3]}", "{\"b\":[]}", + "{\"a\":[1,2,3]}")); +} + int main (int argc, char **argv) { YAZ_CHECK_INIT(argc, argv); tst1(); + tst2(); + tst3(); YAZ_CHECK_TERM; } -- 1.7.10.4