xml2json again

This commit is contained in:
Olof hagsand 2016-09-30 17:57:00 +02:00
parent 1c061aaf26
commit a0b7d28bd1

View file

@ -18,75 +18,7 @@
along with CLIXON; see the file LICENSE. If not, see along with CLIXON; see the file LICENSE. If not, see
<http://www.gnu.org/licenses/>. <http://www.gnu.org/licenses/>.
* For unit testing compile with -_MAIN: * JSON support functions.
*
* 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",
-------------------------
<t>
<sender>
<name>hunerik</name>
</sender>
<sender>
<name>foo</name>
</sender>
</t>
{ "t":
{
"sender": {
"name": "hunerik"
},
"sender": {
"name": "foo"
}
}
}
{
"t": {
"sender": [
{ "name": "hunerik" },
{ "name": "foo" }
]
}
}
OK, still something wrong with grafana plots
*/ */
@ -112,42 +44,96 @@ OK, still something wrong with grafana plots
#include "clixon_json.h" #include "clixon_json.h"
#include "clixon_json_parse.h" #include "clixon_json_parse.h"
#define JSON_INDENT 3 /* maybe we should set this programmatically? */ #define JSON_INDENT 2 /* 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);
}
enum list_element_type{ enum list_element_type{
LIST_NO, NO_LIST=0,
LIST_FIRST, FIRST_LIST,
LIST_MIDDLE, MIDDLE_LIST,
LIST_LAST LAST_LIST,
BODY_LIST
}; };
enum childtype{
NULL_CHILD=0, /* eg <a/> no children */
BODY_CHILD, /* eg one child which is a body, eg <a>1</a> */
ANY_CHILD, /* eg <a><b/></a> or <a><b/><c/></a> */
};
/*! 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 static enum list_element_type
list_eval(cxobj *xprev, list_eval(cxobj *xprev,
cxobj *x, cxobj *x,
cxobj *xnext) cxobj *xnext)
{ {
enum list_element_type list = LIST_NO; enum list_element_type list = NO_LIST;
int eqprev=0; int eqprev=0;
int eqnext=0; int eqnext=0;
assert(xml_type(x)==CX_ELMNT); if (xml_type(x)!=CX_ELMNT){
list=BODY_LIST;
goto done;
}
if (xnext && if (xnext &&
xml_type(xnext)==CX_ELMNT && xml_type(xnext)==CX_ELMNT &&
strcmp(xml_name(x),xml_name(xnext))==0) strcmp(xml_name(x),xml_name(xnext))==0)
@ -157,141 +143,199 @@ list_eval(cxobj *xprev,
strcmp(xml_name(x),xml_name(xprev))==0) strcmp(xml_name(x),xml_name(xprev))==0)
eqprev++; eqprev++;
if (eqprev && eqnext) if (eqprev && eqnext)
list = LIST_MIDDLE; list = MIDDLE_LIST;
else if (eqprev) else if (eqprev)
list = LIST_LAST; list = LAST_LIST;
else if (eqnext) else if (eqnext)
list = LIST_FIRST; list = FIRST_LIST;
else else
list = LIST_NO; list = NO_LIST;
// done: done:
return list; return list;
} }
/*! /*! Do the actual work of translating XML to JSON
* @param[in] pretty set if the output should be pretty-printed * @param[out] cb Cligen text buffer containing json on exit
* List only if adjacent, * @param[in] x XML tree structure containing XML to translate
* ie <a>1</a><a>2</a><b>3</b> -> {"a":[1,2],"b":3} * @param[in] listtype Does x occur in a list (of its parent) and how?
* ie <a>1</a><b>3</b><a>2</a> -> {"a":1,"b":3,"a":2} * @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 | <a/> |<a>1</a> |<a><b/></a> |
| | | | |
| json: |\ta:null |\ta: |\ta:{\n |
| | | |\n} |
+---------+--------------+--------------+--------------+
|first |<a/><a.. |<a>1</a><a.. |<a><b/></a><a.|
| | | | |
| json: |\ta:[\n\tnull |\ta:[\n\t |\ta:[\n\t{\n |
| | | |\n\t} |
+---------+--------------+--------------+--------------+
|middle |..a><a/><a.. |.a><a>1</a><a.| |
| | | | |
| json: |\tnull |\t |\t{a |
| | | |\n\t} |
+---------+--------------+--------------+--------------+
|last |..a></a> |..a><a>1</a> | |
| | | | |
| json: |\tnull |\t |\t{a |
| |\n\t] |\n\t] |\n\t}\t] |
+---------+--------------+--------------+--------------+
*/ */
static int static int
xml2json1_cbuf(cbuf *cb, xml2json1_cbuf(cbuf *cb,
cxobj *xprev, cxobj *x,
cxobj *x, enum list_element_type listtype,
cxobj *xnext, int level,
int level, int pretty)
int pretty)
{ {
int retval = -1; int retval = -1;
int i; int i;
cxobj *xc; cxobj *xc;
enum list_element_type list; enum childtype childt;
switch(xml_type(x)){ childt = childtype(x);
case CX_BODY: if (pretty==2)
if (xml_value(x)) cprintf(cb, "#%s_list, %s_child ",
cprintf(cb, "\"%s\"", xml_value(x)); listtype2str(listtype),
else childtype2str(childt));
cprintf(cb, "null"); switch(listtype){
case BODY_LIST:
assert(xml_value(x));
cprintf(cb, "\"%s\"", xml_value(x));
break; break;
case CX_ELMNT: case NO_LIST:
list = list_eval(xprev, x, xnext); cprintf(cb, "%*s\"%s\": ",
switch (list){ pretty?(level*JSON_INDENT):0, "",
case LIST_NO: xml_name(x));
cprintf(cb, "%*s\"%s\": ", switch (childt){
pretty?(level*JSON_INDENT):0, "", case NULL_CHILD:
xml_name(x)); cprintf(cb, "null");
if (!tleaf(x)){
if (xml_child_nr(x))
cprintf(cb, "{%s",
pretty?"\n":"");
else
cprintf(cb, "null");
}
break; break;
case LIST_FIRST: case BODY_CHILD:
cprintf(cb, "%*s\"%s\": [%s%*s", break;
pretty?(level*JSON_INDENT):0, "", case ANY_CHILD:
xml_name(x), 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<xml_child_nr(x); i++){
enum list_element_type xc_listtype;
xc = xml_child_i(x, i);
xc_listtype = list_eval(i?xml_child_i(x,i-1):NULL,
xc,
xml_child_i(x, i+1));
if (xml2json1_cbuf(cb,
xc,
xc_listtype,
level+1, pretty) < 0)
goto done;
if (i<xml_child_nr(x)-1)
cprintf(cb, ",%s", pretty?"\n":"");
}
switch (listtype){
case BODY_LIST:
break;
case NO_LIST:
switch (childt){
case NULL_CHILD:
case BODY_CHILD:
break;
case ANY_CHILD:
cprintf(cb, "%s%*s}",
pretty?"\n":"", pretty?"\n":"",
pretty?((level+1)*JSON_INDENT):0, "");
level++;
if (!tleaf(x)){
cprintf(cb, "{%s",
pretty?"\n":"");
}
break;
case LIST_MIDDLE:
case LIST_LAST:
level++;
cprintf(cb, "%*s",
pretty?(level*JSON_INDENT):0, ""); pretty?(level*JSON_INDENT):0, "");
if (!tleaf(x))
cprintf(cb, "{ %s", pretty?"\n":"");
break; break;
default: default:
break; break;
} }
for (i=0; i<xml_child_nr(x); i++){ level--;
xc = xml_child_i(x, i); break;
if (xml2json1_cbuf(cb, case FIRST_LIST:
i?xml_child_i(x,i-1):NULL, case MIDDLE_LIST:
xc, switch (childt){
xml_child_i(x, i+1), case NULL_CHILD:
level+1, pretty) < 0) case BODY_CHILD:
goto done;
if (i<xml_child_nr(x)-1){
cprintf(cb, ",");
if (pretty)
cprintf(cb, "\n");
}
}
switch (list){
case LIST_NO:
if (!tleaf(x)){
if (xml_child_nr(x)){
if (pretty)
cprintf(cb, "\n%*s}",
(level*JSON_INDENT), "");
else
cprintf(cb, "}");
}
}
break; break;
case LIST_FIRST: case ANY_CHILD:
if (!tleaf(x)){ cprintf(cb, "%s%*s}",
cprintf(cb, "%s%*s}", pretty?"\n":"",
pretty?"\n":"", pretty?(level*JSON_INDENT):0, "");
pretty?(level*JSON_INDENT):0, ""); level--;
}
break;
case LIST_MIDDLE:
if (!tleaf(x))
cprintf(cb, "%s%*s}%s%*s",
pretty?"\n":"",
pretty?(level*JSON_INDENT):0, "",
pretty?"\n":"",
pretty?(level*JSON_INDENT):0, "");
break;
case LIST_LAST:
if (!tleaf(x)){
if (pretty)
cprintf(cb, "\n%*s}\n",
(level*JSON_INDENT), "");
else
cprintf(cb, "}");
level--;
}
else
if (pretty)
cprintf(cb, "\n");
cprintf(cb, "%*s]",
pretty?((level-1)*JSON_INDENT):0,"");
break; break;
default: default:
break; break;
} }
break; break;
case LAST_LIST:
switch (childt){
case NULL_CHILD:
case BODY_CHILD:
cprintf(cb, "%s",pretty?"\n":"");
break;
case ANY_CHILD:
cprintf(cb, "%s%*s}",
pretty?"\n":"",
pretty?(level*JSON_INDENT):0, "");
cprintf(cb, "%s",pretty?"\n":"");
level--;
break;
default:
break;
}
cprintf(cb, "%*s]",
pretty?(level*JSON_INDENT):0,"");
break;
default: default:
break; break;
} }
@ -331,9 +375,8 @@ xml2json_cbuf(cbuf *cb,
else else
cprintf(cb, "{"); cprintf(cb, "{");
if (xml2json1_cbuf(cb, if (xml2json1_cbuf(cb,
NULL,
x, x,
NULL, NO_LIST,
level+1, pretty) < 0) level+1, pretty) < 0)
goto done; goto done;
if (pretty) if (pretty)
@ -345,8 +388,8 @@ xml2json_cbuf(cbuf *cb,
return retval; return retval;
} }
/*! /*! Translate a vectro of xml objects to xml by adding a top pseudo-object.
* @note can be a problem with vector since xml2json1_cbuf checks parents * example: <b/><c/> --> <a><b/><c/></a> --> {"a" : {"b" : null,"c" : null}}
*/ */
int int
xml2json_cbuf_vec(cbuf *cb, xml2json_cbuf_vec(cbuf *cb,
@ -358,19 +401,26 @@ xml2json_cbuf_vec(cbuf *cb,
int level = 0; int level = 0;
int i; int i;
cxobj *xc; cxobj *xc;
enum list_element_type listtype;
if (pretty) /* Note: We add a pseudo-object on top of the vector.
cprintf(cb, "%*s[\n", * This object is list_element_type == NO_LIST in xml2json1_cbuf
level*JSON_INDENT,""); * and child_type == ANY_CHILD
else */
cprintf(cb, "["); cprintf(cb, "{%s",
pretty?"\n":" ");
level++; level++;
cprintf(cb, "%*s\"top\": ", /* NO_LIST */
pretty?(level*JSON_INDENT):0, "");
cprintf(cb, "{%s", pretty?"\n":""); /* ANY_CHILD */
for (i=0; i<veclen; i++){ for (i=0; i<veclen; i++){
xc = vec[i]; xc = vec[i];
listtype = list_eval(i?vec[i-1]:NULL,
xc,
i<veclen-1?vec[i+1]:NULL);
if (xml2json1_cbuf(cb, if (xml2json1_cbuf(cb,
i?vec[i-1]:NULL,
xc, xc,
i<veclen-1?vec[i+1]:NULL, listtype,
level, pretty) < 0) level, pretty) < 0)
goto done; goto done;
if (i<veclen-1){ if (i<veclen-1){
@ -380,11 +430,12 @@ xml2json_cbuf_vec(cbuf *cb,
} }
} }
level--; level--;
if (pretty) cprintf(cb, "%s%*s}",
cprintf(cb, "\n%*s]\n", pretty?"\n":"",
level*JSON_INDENT,""); pretty?(level*JSON_INDENT):0, "");
else cprintf(cb, "%s}%s",
cprintf(cb, "]"); pretty?"\n":"",
pretty?"\n":""); /* top object */
retval = 0; retval = 0;
done: done:
return retval; return retval;