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 ''
+
+cat < $fin
+up
+show config xml
+EOF
+new "up show"
+expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 ''
+
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=''
+X=''
expectpart "$($clixon_cli -1 -f $cfg -l o show config $format)" 0 "^$X$"
new "cli check show auto $format table"
+X=''
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=']]>]]>'
+X=']]>]]>'
expectpart "$($clixon_cli -1 -f $cfg -l o show config $format)" 0 "^$X$"
new "cli check show auto $format table"
+X=']]>]]>'
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"}}'