From a0b7d28bd1673f2523a4fe9b9008e62d29eb942a Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 30 Sep 2016 17:57:00 +0200 Subject: [PATCH] xml2json again --- lib/src/clixon_json.c | 491 +++++++++++++++++++++++------------------- 1 file changed, 271 insertions(+), 220 deletions(-) diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index 91baf0c8..02fca755 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -18,75 +18,7 @@ along with CLIXON; see the file LICENSE. If not, see . - * For unit testing compile with -_MAIN: - * - * JSON support functions. - -curl -G http://localhost/api/data/sender/userid=a4315f60-e890-4f8f-9a0b-eb53d4da2d3a - -[{ - "sender": { - "name": "dk-ore", - "userid": "a4315f60-e890-4f8f-9a0b-eb53d4da2d3a", - "ipv4_daddr": "109.105.110.78", - "template": "nordunet", - "version": "0", - "description": "Nutanix ORE", - "start": "true", - "udp_dport": "43713", - "debug": "0", - "proto": "udp" - } - -is translated into this: -[ - {"sender": - ["name":"dk-ore", - "userid":"a4315f60-e890-4f8f-9a0b-eb53d4da2d3a", - "ipv4_daddr":"109.105.110.78", - "template":"nordunet", - "version":"0", - "description":"Nutanix ORE", - "start":"true", - "udp_dport":"43713", - "debug":"0", - "proto":"udp"} - , - {"name":"dk-uni", - - -------------------------- - - - - hunerik - - - foo - - - -{ "t": - { - "sender": { - "name": "hunerik" - }, - "sender": { - "name": "foo" - } - } -} - -{ - "t": { - "sender": [ - { "name": "hunerik" }, - { "name": "foo" } - ] - } -} - -OK, still something wrong with grafana plots + * JSON support functions. */ @@ -112,42 +44,96 @@ OK, still something wrong with grafana plots #include "clixon_json.h" #include "clixon_json_parse.h" -#define JSON_INDENT 3 /* maybe we should set this programmatically? */ - -/*! x is element and has eactly one child which in turn has none - * Clone from clixon_xml_map.c - */ -static int -tleaf(cxobj *x) -{ - cxobj *c; - - if (xml_type(x) != CX_ELMNT) - return 0; - if (xml_child_nr(x) != 1) - return 0; - c = xml_child_i(x, 0); - return (xml_child_nr(c) == 0); -} - +#define JSON_INDENT 2 /* maybe we should set this programmatically? */ enum list_element_type{ - LIST_NO, - LIST_FIRST, - LIST_MIDDLE, - LIST_LAST + NO_LIST=0, + FIRST_LIST, + MIDDLE_LIST, + LAST_LIST, + BODY_LIST }; +enum childtype{ + NULL_CHILD=0, /* eg no children */ + BODY_CHILD, /* eg one child which is a body, eg 1 */ + ANY_CHILD, /* eg or */ +}; + +/*! x is element and has exactly one child which in turn has none + * Clone from clixon_xml_map.c + */ +static enum childtype +childtype(cxobj *x) +{ + cxobj *xc1; /* the only child of x */ + + if (xml_type(x) != CX_ELMNT) + return -1; /* n/a */ + if (xml_child_nr(x) == 0) + return NULL_CHILD; + if (xml_child_nr(x) > 1) + return ANY_CHILD; + xc1 = xml_child_i(x, 0); /* From here exactly one child */ + if (xml_child_nr(xc1) == 0 && xml_type(xc1)==CX_BODY) + return BODY_CHILD; + else + return ANY_CHILD; +} + +static char* +childtype2str(enum childtype lt) +{ + switch(lt){ + case NULL_CHILD: + return "null"; + break; + case BODY_CHILD: + return "body"; + break; + case ANY_CHILD: + return "any"; + break; + } + return ""; +} + +static char* +listtype2str(enum list_element_type lt) +{ + switch(lt){ + case NO_LIST: + return "no"; + break; + case FIRST_LIST: + return "first"; + break; + case MIDDLE_LIST: + return "middle"; + break; + case LAST_LIST: + return "last"; + break; + case BODY_LIST: + return "body"; + break; + } + return ""; +} + static enum list_element_type list_eval(cxobj *xprev, cxobj *x, cxobj *xnext) { - enum list_element_type list = LIST_NO; + enum list_element_type list = NO_LIST; int eqprev=0; int eqnext=0; - assert(xml_type(x)==CX_ELMNT); + if (xml_type(x)!=CX_ELMNT){ + list=BODY_LIST; + goto done; + } if (xnext && xml_type(xnext)==CX_ELMNT && strcmp(xml_name(x),xml_name(xnext))==0) @@ -157,141 +143,199 @@ list_eval(cxobj *xprev, strcmp(xml_name(x),xml_name(xprev))==0) eqprev++; if (eqprev && eqnext) - list = LIST_MIDDLE; + list = MIDDLE_LIST; else if (eqprev) - list = LIST_LAST; + list = LAST_LIST; else if (eqnext) - list = LIST_FIRST; + list = FIRST_LIST; else - list = LIST_NO; - // done: + list = NO_LIST; + done: return list; } -/*! - * @param[in] pretty set if the output should be pretty-printed - * List only if adjacent, - * ie 123 -> {"a":[1,2],"b":3} - * ie 132 -> {"a":1,"b":3,"a":2} +/*! Do the actual work of translating XML to JSON + * @param[out] cb Cligen text buffer containing json on exit + * @param[in] x XML tree structure containing XML to translate + * @param[in] listtype Does x occur in a list (of its parent) and how? + * @param[in] level Indentation level + * @param[in] pretty Pretty-print output (2 means debug) + * + * The following matrix explains how the mapping is done. + * You need to understand what listtype means (no/first/middle/last) + * and what childtype is (null,body,any) ++---------+--------------+--------------+--------------+ +|list,leaf| null | body | any | ++---------+--------------+--------------+--------------+ +|no | |1 | | +| | | | | +| json: |\ta:null |\ta: |\ta:{\n | +| | | |\n} | ++---------+--------------+--------------+--------------+ +|first |11 |..a>1 | | +| | | | | +| json: |\tnull |\t |\t{a | +| |\n\t] |\n\t] |\n\t}\t] | ++---------+--------------+--------------+--------------+ */ static int -xml2json1_cbuf(cbuf *cb, - cxobj *xprev, - cxobj *x, - cxobj *xnext, - int level, - int pretty) +xml2json1_cbuf(cbuf *cb, + cxobj *x, + enum list_element_type listtype, + int level, + int pretty) { - int retval = -1; - int i; - cxobj *xc; - enum list_element_type list; + int retval = -1; + int i; + cxobj *xc; + enum childtype childt; - switch(xml_type(x)){ - case CX_BODY: - if (xml_value(x)) - cprintf(cb, "\"%s\"", xml_value(x)); - else - cprintf(cb, "null"); + childt = childtype(x); + if (pretty==2) + cprintf(cb, "#%s_list, %s_child ", + listtype2str(listtype), + childtype2str(childt)); + switch(listtype){ + case BODY_LIST: + assert(xml_value(x)); + cprintf(cb, "\"%s\"", xml_value(x)); break; - case CX_ELMNT: - list = list_eval(xprev, x, xnext); - switch (list){ - case LIST_NO: - cprintf(cb, "%*s\"%s\": ", - pretty?(level*JSON_INDENT):0, "", - xml_name(x)); - if (!tleaf(x)){ - if (xml_child_nr(x)) - cprintf(cb, "{%s", - pretty?"\n":""); - else - cprintf(cb, "null"); - } + case NO_LIST: + cprintf(cb, "%*s\"%s\": ", + pretty?(level*JSON_INDENT):0, "", + xml_name(x)); + switch (childt){ + case NULL_CHILD: + cprintf(cb, "null"); break; - case LIST_FIRST: - cprintf(cb, "%*s\"%s\": [%s%*s", - pretty?(level*JSON_INDENT):0, "", - xml_name(x), + case BODY_CHILD: + break; + case ANY_CHILD: + cprintf(cb, "{%s", pretty?"\n":""); + break; + default: + break; + } + break; + case FIRST_LIST: + cprintf(cb, "%*s\"%s\": ", + pretty?(level*JSON_INDENT):0, "", + xml_name(x)); + level++; + cprintf(cb, "[%s%*s", + pretty?"\n":"", + pretty?(level*JSON_INDENT):0, ""); + switch (childt){ + case NULL_CHILD: + cprintf(cb, "null"); + break; + case BODY_CHILD: + break; + case ANY_CHILD: + cprintf(cb, "{%s", pretty?"\n":""); + break; + default: + break; + } + break; + case MIDDLE_LIST: + case LAST_LIST: + level++; + cprintf(cb, "%*s", + pretty?(level*JSON_INDENT):0, ""); + switch (childt){ + case NULL_CHILD: + cprintf(cb, "null"); + break; + case BODY_CHILD: + break; + case ANY_CHILD: + cprintf(cb, "{ %s", pretty?"\n":""); + break; + default: + break; + } + break; + default: + break; + } + for (i=0; i --> --> {"a" : {"b" : null,"c" : null}} */ int xml2json_cbuf_vec(cbuf *cb, @@ -358,19 +401,26 @@ xml2json_cbuf_vec(cbuf *cb, int level = 0; int i; cxobj *xc; + enum list_element_type listtype; - if (pretty) - cprintf(cb, "%*s[\n", - level*JSON_INDENT,""); - else - cprintf(cb, "["); + /* Note: We add a pseudo-object on top of the vector. + * This object is list_element_type == NO_LIST in xml2json1_cbuf + * and child_type == ANY_CHILD + */ + cprintf(cb, "{%s", + pretty?"\n":" "); level++; + cprintf(cb, "%*s\"top\": ", /* NO_LIST */ + pretty?(level*JSON_INDENT):0, ""); + cprintf(cb, "{%s", pretty?"\n":""); /* ANY_CHILD */ for (i=0; i