/* This file is part of the YAZ toolkit. * Copyright (C) Index Data * See the file LICENSE for details. */ /** * \file json.c * \brief JSON encoding/decoding */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #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) { json_parser_t p = (json_parser_t) xmalloc(sizeof(*p)); 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); } static int look_ch(json_parser_t p) { while (*p->cp && strchr(" \t\r\n\f", *p->cp)) (p->cp)++; return *p->cp; } static void move_ch(json_parser_t p) { if (*p->cp) (p->cp)++; } static struct json_node *json_new_node(json_parser_t p, enum json_node_type type) { struct json_node *n = (struct json_node *) xmalloc(sizeof(*n)); n->type = type; n->u.link[0] = n->u.link[1] = 0; return n; } void json_remove_node(struct json_node *n) { if (!n) return; switch (n->type) { case json_node_object: case json_node_array: case json_node_list: case json_node_pair: json_remove_node(n->u.link[0]); json_remove_node(n->u.link[1]); break; case json_node_string: xfree(n->u.string); break; case json_node_number: case json_node_true: case json_node_false: case json_node_null: break; } xfree(n); } static struct json_node *json_parse_object(json_parser_t p); static struct json_node *json_parse_array(json_parser_t p); static int json_one_char(const char **p, char *out) { if (**p == '\\' && p[0][1]) { (*p)++; switch(**p) { case '"': *out = '"'; break; case '\\': *out = '\\'; break; case '/': *out = '/'; break; case 'b': *out = '\b'; break; case 'f': *out = '\f'; break; case 'n': *out = '\n'; break; case 'r': *out = '\r'; break; case 't': *out = '\t'; break; case 'u': if (p[0][1]) { unsigned code; char *outp = out; int error; size_t outbytesleft = 6; sscanf(*p + 1, "%4x", &code); if (!yaz_write_UTF8_char(code, &outp, &outbytesleft, &error)) { *p += 5; return outp - out; } } default: *out = '_'; break; break; } (*p)++; return 1; } else { *out = **p; (*p)++; return 1; } } static struct json_node *json_parse_string(json_parser_t p) { struct json_node *n; const char *cp; char *dst; int l = 0; if (look_ch(p) != '\"') { p->err_msg = "string expected"; return 0; } move_ch(p); cp = p->cp; while (*cp && *cp != '"') { char out[6]; l += json_one_char(&cp, out); } if (!*cp) { p->err_msg = "missing \""; return 0; } n = json_new_node(p, json_node_string); dst = n->u.string = (char *) xmalloc(l + 1); cp = p->cp; while (*cp && *cp != '"') { char out[6]; l = json_one_char(&cp, out); memcpy(dst, out, l); dst += l; } *dst = '\0'; p->cp = cp+1; return n; } static struct json_node *json_parse_number(json_parser_t p) { struct json_node *n; char *endptr; double v; look_ch(p); // skip spaces v = strtod(p->cp, &endptr); if (endptr == p->cp) { p->err_msg = "bad number"; return 0; } p->cp = endptr; n = json_new_node(p, json_node_number); n->u.number = v; return n; } static struct json_node *json_parse_value(json_parser_t p) { int c = look_ch(p); if (c == '\"') return json_parse_string(p); else if (strchr("0123456789-+", c)) return json_parse_number(p); else if (c == '{') 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]; int i = 0; while (c >= 'a' && c <= 'z' && i < 7) { tok[i++] = c; p->cp++; c = *p->cp; } tok[i] = 0; if (!strcmp(tok, "true")) return json_new_node(p, json_node_true); else if (!strcmp(tok, "false")) return json_new_node(p, json_node_false); else if (!strcmp(tok, "null")) return json_new_node(p, json_node_null); } p->err_msg = "bad token"; return 0; } static struct json_node *json_parse_elements(json_parser_t p) { struct json_node *n1 = json_parse_value(p); struct json_node *m0, *m1; if (!n1) return 0; m0 = m1 = json_new_node(p, json_node_list); m1->u.link[0] = n1; while (look_ch(p) == ',') { struct json_node *n2, *m2; move_ch(p); n2 = json_parse_value(p); if (!n2) { json_remove_node(m0); return 0; } m2 = json_new_node(p, json_node_list); m2->u.link[0] = n2; m1->u.link[1] = m2; m1 = m2; } return m0; } static struct json_node *json_parse_array(json_parser_t p) { struct json_node *n; if (look_ch(p) != '[') { p->err_msg = "expecting ["; return 0; } move_ch(p); n = json_new_node(p, json_node_array); if (look_ch(p) != ']') n->u.link[0] = json_parse_elements(p); if (look_ch(p) != ']') { p->err_msg = "expecting ]"; json_remove_node(n); return 0; } move_ch(p); return n; } static struct json_node *json_parse_pair(json_parser_t p) { struct json_node *s = json_parse_string(p); struct json_node *v, *n; if (!s) return 0; if (look_ch(p) != ':') { p->err_msg = "missing :"; json_remove_node(s); return 0; } move_ch(p); v = json_parse_value(p); if (!v) { json_remove_node(s); return 0; } n = json_new_node(p, json_node_pair); n->u.link[0] = s; n->u.link[1] = v; return n; } static struct json_node *json_parse_members(json_parser_t p) { struct json_node *n1 = json_parse_pair(p); struct json_node *m0, *m1; if (!n1) return 0; m0 = m1 = json_new_node(p, json_node_list); m1->u.link[0] = n1; while (look_ch(p) == ',') { struct json_node *n2, *m2; move_ch(p); n2 = json_parse_pair(p); if (!n2) { json_remove_node(m0); return 0; } m2 = json_new_node(p, json_node_list); m2->u.link[0] = n2; m1->u.link[1] = m2; m1 = m2; } return m0; } static struct json_node *json_parse_object(json_parser_t p) { struct json_node *n; if (look_ch(p) != '{') { p->err_msg = "{ expected"; return 0; } move_ch(p); n = json_new_node(p, json_node_object); if (look_ch(p) != '}') { struct json_node *m = json_parse_members(p); if (!m) { json_remove_node(n); return 0; } n->u.link[0] = m; } if (look_ch(p) != '}') { p->err_msg = "Missing }"; json_remove_node(n); return 0; } move_ch(p); return n; } struct json_node *json_parser_parse(json_parser_t p, const char *json_str) { int c; struct json_node *n; p->buf = json_str; p->cp = p->buf; n = json_parse_value(p); if (!n) return 0; c = look_ch(p); if (c != 0) { p->err_msg = "extra characters"; json_remove_node(n); return 0; } return n; } struct json_node *json_parse2(const char *json_str, const char **errmsg, size_t *pos) { 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); if (pos) *pos = json_parser_get_position(p); json_parser_destroy(p); } return n; } struct json_node *json_parse(const char *json_str, const char **errmsg) { return json_parse2(json_str, errmsg, 0); } static void json_indent(WRBUF result, int indent) { size_t l = wrbuf_len(result); if (l == 0 || wrbuf_buf(result)[l-1] == '\n') { int i; for (i = 0; i < indent; i++) wrbuf_putc(result, ' '); } } static void json_write_wrbuf_r(struct json_node *node, WRBUF result, int indent) { int sub_indent = -1; if (indent >= 0) sub_indent = indent + 1; switch (node->type) { case json_node_object: json_indent(result, indent); wrbuf_puts(result, "{"); if (indent >= 0) { wrbuf_puts(result, "\n"); json_indent(result, sub_indent); } if (node->u.link[0]) json_write_wrbuf_r(node->u.link[0], result, sub_indent); if (indent >= 0) { wrbuf_puts(result, "\n"); json_indent(result, indent); } wrbuf_puts(result, "}"); break; case json_node_array: json_indent(result, indent); wrbuf_puts(result, "["); if (indent >= 0) { wrbuf_puts(result, "\n"); json_indent(result, sub_indent); } if (node->u.link[0]) { json_write_wrbuf_r(node->u.link[0], result, sub_indent); } if (indent >= 0) { wrbuf_puts(result, "\n"); json_indent(result, indent); } wrbuf_puts(result, "]"); break; case json_node_list: json_write_wrbuf_r(node->u.link[0], result, indent); if (node->u.link[1]) { wrbuf_puts(result, ","); if (indent >= 0) wrbuf_puts(result, " "); json_write_wrbuf_r(node->u.link[1], result, indent); } break; case json_node_pair: json_write_wrbuf_r(node->u.link[0], result, indent); wrbuf_puts(result, ":"); if (indent >= 0) wrbuf_puts(result, " "); json_write_wrbuf_r(node->u.link[1], result, indent); break; case json_node_string: wrbuf_puts(result, "\""); wrbuf_json_puts(result, node->u.string); wrbuf_puts(result, "\""); break; case json_node_number: wrbuf_printf(result, "%lg", node->u.number); break; case json_node_true: wrbuf_puts(result, "true"); break; case json_node_false: wrbuf_puts(result, "false"); break; case json_node_null: wrbuf_puts(result, "null"); break; } } void json_write_wrbuf_pretty(struct json_node *node, WRBUF result) { json_write_wrbuf_r(node, result, 1); } void json_write_wrbuf(struct json_node *node, WRBUF result) { json_write_wrbuf_r(node, result, -1); } 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; } size_t json_parser_get_position(json_parser_t p) { return p->cp - p->buf; } /* * Local variables: * c-basic-offset: 4 * c-file-style: "Stroustrup" * indent-tabs-mode: nil * End: * vim: shiftwidth=4 tabstop=8 expandtab */