More JSON utilities
authorAdam Dickmeiss <adam@indexdata.dk>
Wed, 13 Jan 2010 14:44:10 +0000 (15:44 +0100)
committerAdam Dickmeiss <adam@indexdata.dk>
Wed, 13 Jan 2010 14:49:10 +0000 (15:49 +0100)
New functions json_get_object, json_detach_object, json_get_elem,
json_count_children, json_parser_subst, json_append_array.

include/yaz/json.h
src/json.c
test/tst_json.c

index b56ee4c..4cefba3 100644 (file)
@@ -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
index 41b99eb..a2d4f1d 100644 (file)
 
 #include <yaz/xmalloc.h>
 
+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;
index d20beba..8720290 100644 (file)
@@ -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;
 }