diff --git a/CHANGELOG.md b/CHANGELOG.md index 772a93f9..3abef9fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,7 +71,8 @@ Developers may need to change their code * `clicon_rpc_commit()` and `clicon_rpc_validate` * Added three-value return. * Code need to be changed from: checking for `<0` to `<1` to keep same semantics - + * Added `skiptop` parameter to `xml2json_vec()` + ### Minor features * [Code formatting: Change indentation style to space](https://github.com/clicon/clixon/issues/379) @@ -80,6 +81,7 @@ Developers may need to change their code ### Corrected Bugs +* Fixed: [CLI Show config JSON with multiple top-level elements is broken](https://github.com/clicon/clixon/issues/381) * Fixed: [Non-obvious behavior of clixon_snmp after snmpset command when transaction validation returns an error](https://github.com/clicon/clixon/issues/375) * Fixed by validating writes on ACTION instead of COMMIT since libnetsnmp seems not to accept commit errors * Fixed: [YANG when condition evaluated as false combined with a mandatory leaf does not work](https://github.com/clicon/clixon/issues/380) diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index d3187123..469a58f8 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -414,7 +414,7 @@ show_yang(clicon_handle h, * @param[in] prefix CLI prefix to prepend cli syntax, eg "set " * @param[in] xpath XPath * @param[in] nsc Namespace mapping for xpath - * @param[in] skiproot If set, do not show object itself, only its children + * @param[in] skiptop If set, do not show object itself, only its children */ static int cli_show_common(clicon_handle h, @@ -427,7 +427,7 @@ cli_show_common(clicon_handle h, char *prefix, char *xpath, cvec *nsc, - int skiproot + int skiptop ) { int retval = -1; @@ -477,10 +477,10 @@ cli_show_common(clicon_handle h, else ys_keyword = 0; /* Special case LIST */ - if ((ys_keyword == Y_LIST || ys_keyword == Y_LEAF_LIST) && format == FORMAT_JSON){ + if (format == FORMAT_JSON){ switch (format){ case FORMAT_JSON: - if (xml2json_vec(stdout, vec, veclen, pretty) < 0) // XXX cligen_output + if (xml2json_vec(stdout, vec, veclen, pretty, skiptop) < 0) // XXX cligen_output goto done; break; default: @@ -497,21 +497,17 @@ cli_show_common(clicon_handle h, /* Print configuration according to format */ switch (format){ case FORMAT_XML: - if (clixon_xml2file(stdout, xp, 0, pretty, cligen_output, skiproot, 1) < 0) + if (clixon_xml2file(stdout, xp, 0, pretty, cligen_output, skiptop, 1) < 0) goto done; if (!pretty && i == veclen-1) cligen_output(stdout, "\n"); break; - case FORMAT_JSON: - if (clixon_json2file(stdout, xp, pretty, cligen_output, skiproot, 1) < 0) - goto done; - break; case FORMAT_TEXT: /* XXX does not handle multiple leaf-list */ - if (clixon_txt2file(stdout, xp, 0, cligen_output, skiproot, 1) < 0) + if (clixon_txt2file(stdout, xp, 0, cligen_output, skiptop, 1) < 0) goto done; break; case FORMAT_CLI: - if (clixon_cli2file(h, stdout, xp, prefix, cligen_output, skiproot) < 0) /* cli syntax */ + if (clixon_cli2file(h, stdout, xp, prefix, cligen_output, skiptop) < 0) /* cli syntax */ goto done; break; case FORMAT_NETCONF: @@ -521,11 +517,13 @@ cli_show_common(clicon_handle h, if (pretty) cligen_output(stdout, "\n"); } - if (clixon_xml2file(stdout, xp, 2, pretty, cligen_output, skiproot, 1) < 0) + if (clixon_xml2file(stdout, xp, 2, pretty, cligen_output, skiptop, 1) < 0) goto done; if (i == veclen-1) cligen_output(stdout, "]]>]]>\n"); break; + default: + break; } } } @@ -693,7 +691,6 @@ cli_show_config(clicon_handle h, char *withdefault = NULL; /* RFC 6243 modes */ char *extdefault = NULL; /* with extended tagged modes */ int argc = 0; - int skiproot = 0; char *xpath = "/"; char *namespace = NULL; @@ -733,7 +730,7 @@ cli_show_config(clicon_handle h, } if (cli_show_common(h, dbname, format, pretty, state, withdefault, extdefault, - prefix, xpath, nsc, skiproot) < 0) + prefix, xpath, nsc, 0) < 0) goto done; retval = 0; done: @@ -865,7 +862,6 @@ cli_show_auto(clicon_handle h, char *withdefault = NULL; /* RFC 6243 modes */ char *extdefault = NULL; /* with extended tagged modes */ int argc = 0; - int skiproot = 0; char *xpath = NULL; yang_stmt *yspec; char *api_path = NULL; @@ -912,7 +908,7 @@ cli_show_auto(clicon_handle h, } if (cli_show_common(h, dbname, format, pretty, state, withdefault, extdefault, - prefix, xpath, nsc, skiproot) < 0) + prefix, xpath, nsc, 0) < 0) goto done; retval = 0; done: @@ -973,7 +969,7 @@ cli_show_auto_mode(clicon_handle h, char *withdefault = NULL; /* RFC 6243 modes */ char *extdefault = NULL; /* with extended tagged modes */ int argc = 0; - int skiproot = 0; + int skiptop = 0; char *xpath = NULL; yang_stmt *yspec; char *api_path = NULL; @@ -1018,10 +1014,10 @@ cli_show_auto_mode(clicon_handle h, clicon_err(OE_FATAL, 0, "Invalid api-path: %s", api_path); goto done; } - skiproot = (strcmp(xpath,"/") != 0); + skiptop = (strcmp(xpath,"/") != 0); if (cli_show_common(h, dbname, format, pretty, state, withdefault, extdefault, - prefix, xpath, nsc, skiproot) < 0) + prefix, xpath, nsc, skiptop) < 0) goto done; retval = 0; done: diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index 82dd059e..8c20df26 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -283,7 +283,7 @@ api_data_get2(clicon_handle h, /* In: 0 * Out: {"example:x": {"0"}} */ - if (xml2json_cbuf_vec(cbx, xvec, xlen, pretty) < 0) + if (xml2json_cbuf_vec(cbx, xvec, xlen, pretty, 0) < 0) goto done; break; default: @@ -546,7 +546,7 @@ api_data_pagination(clicon_handle h, goto done; break; case YANG_DATA_JSON: - if (xml2json_cbuf_vec(cbx, xvec, xlen, pretty) < 0) + if (xml2json_cbuf_vec(cbx, xvec, xlen, pretty, 0) < 0) goto done; break; default: diff --git a/lib/clixon/clixon_json.h b/lib/clixon/clixon_json.h index bd8cdfec..4b5a9785 100644 --- a/lib/clixon/clixon_json.h +++ b/lib/clixon/clixon_json.h @@ -45,10 +45,10 @@ */ int json2xml_decode(cxobj *x, cxobj **xerr); int clixon_json2cbuf(cbuf *cb, cxobj *x, int pretty, int skiptop, int autocliext); -int xml2json_cbuf_vec(cbuf *cb, cxobj **vec, size_t veclen, int pretty); +int xml2json_cbuf_vec(cbuf *cb, cxobj **vec, size_t veclen, int pretty, int skiptop); int clixon_json2file(FILE *f, cxobj *x, int pretty, clicon_output_cb *fn, int skiptop, int autocliext); int json_print(FILE *f, cxobj *x); -int xml2json_vec(FILE *f, cxobj **vec, size_t veclen, int pretty); +int xml2json_vec(FILE *f, cxobj **vec, size_t veclen, int pretty, int skiptop); int clixon_json_parse_string(char *str, int rfc7951, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xret); int clixon_json_parse_file(FILE *fp, int rfc7951, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xret); diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index 7525f630..709d8340 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -1157,12 +1157,16 @@ clixon_json2cbuf(cbuf *cb, { int retval = -1; cxobj *xc; + int i=0; if (skiptop){ xc = NULL; - while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) + while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL){ + if (i++) + cprintf(cb, ","); if (xml2json_cbuf1(cb, xc, pretty, autocliext) < 0) goto done; + } } else { if (xml2json_cbuf1(cb, xt, pretty, autocliext) < 0) @@ -1180,6 +1184,7 @@ clixon_json2cbuf(cbuf *cb, * @param[in] vec Vector of xml objecst * @param[in] veclen Length of vector * @param[in] pretty Set if output is pretty-printed (2 for debug) + * @param[in] skiptop 0: Include top object 1: Skip top-object, only children, * @retval 0 OK * @retval -1 Error * @note This only works if the vector is uniform, ie same object name. @@ -1190,12 +1195,14 @@ int xml2json_cbuf_vec(cbuf *cb, cxobj **vec, size_t veclen, - int pretty) + int pretty, + int skiptop) { int retval = -1; int level = 0; cxobj *xp = NULL; int i; + cxobj *xc0; cxobj *xc; cvec *nsc = NULL; @@ -1204,12 +1211,25 @@ xml2json_cbuf_vec(cbuf *cb, /* Make a copy of old and graft it into new top-object * Also copy namespace context */ for (i=0; i $dir/startup_db <${DATASTORE_TOP}> @@ -239,6 +240,7 @@ cat < $fin edit table parameter b show config xml EOF + new "edit table parameter b; show" expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "/clixon-example:table/parameter=b/>" --not-- "a42" '
' "" @@ -248,9 +250,19 @@ set value 71 up show config xml EOF -new "set value 71" +new "add b = 71" expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "/clixon-example:table>" "a42b71" +new "show top tree" +expectpart "$(echo "show config xml" | $clixon_cli -f $cfg 2>&1)" 0 '
a42b71
' + +cat < $fin +up +show config xml +EOF +new "up show" +expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 'a42b71
' + cat < $fin edit table parameter a top @@ -288,12 +300,22 @@ new "show state" expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "a4299" # Show other formats +cat < $fin +edit table parameter b +set value 71 +EOF +new "add b = 71" +expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 + +new "show top tree json" +expectpart "$(echo "show config json" | $clixon_cli -f $cfg 2>&1)" 0 '{"clixon-example:table":{"parameter":\[{"name":"a","value":"42"},{"name":"b","value":"71"}\]}}' + cat < $fin edit table show config json EOF new "show config json" -expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 '{"clixon-example:parameter":\[{"name":"a","value":"42"}\]}' +expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 '{"clixon-example:parameter":\[{"name":"a","value":"42"},{"name":"b","value":"71"}\]}' cat < $fin edit table diff --git a/test/test_autocli_show.sh b/test/test_autocli_show.sh index 709c7e81..d64eefff 100755 --- a/test/test_autocli_show.sh +++ b/test/test_autocli_show.sh @@ -60,6 +60,9 @@ module clixon-example { } } } + container table2{ + presence true; + } } EOF @@ -110,7 +113,7 @@ fi new "wait backend" wait_backend -# Create two lists +# Create two lists elements new "cli create list table x" expectpart "$($clixon_cli -1 -f $cfg -l o set table parameter x value 1)" 0 "^$" @@ -127,14 +130,19 @@ expectpart "$($clixon_cli -1 -f $cfg -l o set table parameter x array1 b)" 0 "^$ new "cli commit" expectpart "$($clixon_cli -1 -f $cfg -l o commit)" 0 "^$" +# Create a second top-level element +new "cli create list table2" +expectpart "$($clixon_cli -1 -f $cfg -l o set table2)" 0 "^$" + # XML format=xml new "cli check show config $format" -X='x1aby2
' +X='x1aby2
' expectpart "$($clixon_cli -1 -f $cfg -l o show config $format)" 0 "^$X$" new "cli check show auto $format table" +X='x1aby2
' expectpart "$($clixon_cli -1 -f $cfg -l o show auto $format table)" 0 "^$X$" new "cli check show auto $format table parameter" @@ -153,10 +161,11 @@ expectpart "$($clixon_cli -1 -f $cfg -l o show auto $format table parameter x ar format=json new "cli check show config $format" -X='{"clixon-example:table":{"parameter":\[{"name":"x","value":"1","array1":\["a","b"\]},{"name":"y","value":"2"}\]}}' +X='{"clixon-example:table":{"parameter":\[{"name":"x","value":"1","array1":\["a","b"\]},{"name":"y","value":"2"}\]},"clixon-example:table2":{}}' expectpart "$($clixon_cli -1 -f $cfg -l o show config $format)" 0 "^$X$" new "cli check show auto $format table" +X='{"clixon-example:table":{"parameter":\[{"name":"x","value":"1","array1":\["a","b"\]},{"name":"y","value":"2"}\]}}' expectpart "$($clixon_cli -1 -f $cfg -l o show auto $format table)" 0 "^$X$" new "cli check show auto $format table parameter" @@ -207,10 +216,11 @@ format=netconf # XXX netconf base capability 0, EOM framing new "cli check show config $format" -X='x1aby2
]]>]]>' +X='x1aby2
]]>]]>' expectpart "$($clixon_cli -1 -f $cfg -l o show config $format)" 0 "^$X$" new "cli check show auto $format table" +X='x1aby2
]]>]]>' expectpart "$($clixon_cli -1 -f $cfg -l o show auto $format table)" 0 "^$X$" # XXX rest does not print whole NETCONF path to root, eg does not include "table" diff --git a/test/test_json.sh b/test/test_json.sh index b0b9844f..d20e6481 100755 --- a/test/test_json.sh +++ b/test/test_json.sh @@ -67,7 +67,7 @@ new "json parse empty list to xml" expecteofx "$clixon_util_json" 0 '{"a":[]}' "" new "json parse list json" # should be {"a":[0,1,2,3]} -expecteofx "$clixon_util_json -j" 0 '{"a":[0,1,2,3]}' '{"a":"0"}{"a":"1"}{"a":"2"}{"a":"3"}' +expecteofx "$clixon_util_json -j" 0 '{"a":[0,1,2,3]}' '{"a":"0"},{"a":"1"},{"a":"2"},{"a":"3"}' # Multi-line JSON not pretty-print JSON='{"json:c":{"a":42,"s":"string"}}'