diff --git a/CHANGELOG.md b/CHANGELOG.md
index 161bb162..708d7616 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -72,6 +72,7 @@ Users may have to change how they access the system
### Corrected Bugs
+* Fixed: [JSON leaf-list output single element leaf-list does not use array](https://github.com/clicon/clixon/issues/261)
* Fixed: Netconf diff callback did not work with choice and same value replace
* Eg if YANG is `choice c { leaf x; leaf y }` and XML changed from `42` to `42` the datastrore changed, but was not detected by diff algorithms and provided to validate callbacks.
* Thanks: Alexander Skorichenko, Netgate
diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c
index caf79351..85aab8f7 100644
--- a/lib/src/clixon_json.c
+++ b/lib/src/clixon_json.c
@@ -36,6 +36,8 @@
* JSON syntax is according to:
* http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
* RFC 7951 JSON Encoding of Data Modeled with YANG
+ * XXX: The complexity of xml2json1_cbuf() mapping from internal cxobj structure to JSON output
+ * needs a rewrite due to complexity of lists/leaf-lists/null-values, etc.
*/
#ifdef HAVE_CONFIG_H
@@ -180,14 +182,21 @@ arraytype2str(enum array_element_type lt)
}
/*! Check typeof x in array
+ *
+ * Check if element is in an array, and if so, if it is in the start "[x,", in the middle: "[..,x,..]"
+ * in the end: ",x]", or a single element: "[x]"
* Some complexity when x is in different namespaces
+ * @param[in] xprev The previous element (if any)
+ * @param[in] x The element itself
+ * @param[in] xnext The next element (if any)
+ * @retval arraytype Type of array
*/
static enum array_element_type
array_eval(cxobj *xprev,
cxobj *x,
cxobj *xnext)
{
- enum array_element_type array = NO_ARRAY;
+ enum array_element_type arraytype = NO_ARRAY;
int eqprev=0;
int eqnext=0;
yang_stmt *ys;
@@ -196,10 +205,9 @@ array_eval(cxobj *xprev,
nsx = xml_find_type_value(x, NULL, "xmlns", CX_ATTR);
if (xml_type(x) != CX_ELMNT){
- array=BODY_ARRAY;
+ arraytype = BODY_ARRAY;
goto done;
}
- ys = xml_spec(x);
if (xnext &&
xml_type(xnext)==CX_ELMNT &&
strcmp(xml_name(x), xml_name(xnext))==0){
@@ -217,17 +225,25 @@ array_eval(cxobj *xprev,
eqprev++;
}
if (eqprev && eqnext)
- array = MIDDLE_ARRAY;
+ arraytype = MIDDLE_ARRAY;
else if (eqprev)
- array = LAST_ARRAY;
+ arraytype = LAST_ARRAY;
else if (eqnext)
- array = FIRST_ARRAY;
- else if (ys && yang_keyword_get(ys) == Y_LIST)
- array = SINGLE_ARRAY;
+ arraytype = FIRST_ARRAY;
+ else if ((ys = xml_spec(x)) != NULL) {
+ if (yang_keyword_get(ys) == Y_LIST
+#if 0 /* XXX instead see special case in xml2json_encode_leafs */
+ || yang_keyword_get(ys) == Y_LEAF_LIST
+#endif
+ )
+ arraytype = SINGLE_ARRAY;
+ else
+ arraytype = NO_ARRAY;
+ }
else
- array = NO_ARRAY;
+ arraytype = NO_ARRAY;
done:
- return array;
+ return arraytype;
}
/*! Escape a json string as well as decode xml cdata
@@ -414,7 +430,7 @@ json2xml_decode(cxobj *x,
enum rfc_6020 keyword;
cxobj *xc;
int ret;
- yang_stmt *ytype;
+ yang_stmt *ytype = NULL;
if ((y = xml_spec(x)) != NULL){
keyword = yang_keyword_get(y);
@@ -514,8 +530,9 @@ xml2json_encode_identityref(cxobj *xb,
}
/*! Encode leaf/leaf_list types from XML to JSON
- * @param[in] x XML body
- * @param[in] ys Yang spec of parent
+ * @param[in] xb XML body
+ * @param[in] xp XML parent
+ * @param[in] yp Yang spec of parent
* @param[out] cb0 Encoded string
*/
static int
@@ -561,8 +578,9 @@ xml2json_encode_leafs(cxobj *xb,
if (xml2json_encode_identityref(xb, body, yp, cb) < 0)
goto done;
}
- else
+ else{
cprintf(cb, "%s", body);
+ }
}
else
cprintf(cb, "%s", body);
@@ -577,6 +595,12 @@ xml2json_encode_leafs(cxobj *xb,
case CGV_UINT64:
case CGV_DEC64:
case CGV_BOOL:
+#if 1 /* Special case */
+ if (yang_keyword_get(yp) == Y_LEAF_LIST
+ && xml_child_nr_type(xml_parent(xp), CX_ELMNT) == 1)
+ cprintf(cb, "[%s]", body);
+ else
+#endif
cprintf(cb, "%s", body);
quote = 0;
break;
diff --git a/test/test_json_list.sh b/test/test_json_list.sh
new file mode 100755
index 00000000..1283ed42
--- /dev/null
+++ b/test/test_json_list.sh
@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+# Test: JSON (leaf-)list and YANG. See RFC7951 sec 5.3 / 5.4
+
+# Magic line must be first in script (see README.md)
+s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
+
+: ${clixon_util_json:=clixon_util_json}
+: ${clixon_util_xml:=clixon_util_xml}
+
+fyang=$dir/json.yang
+cat < $fyang
+module json{
+ prefix ex;
+ namespace "urn:example:clixon";
+ container c{
+ leaf-list l1{
+ type int32;
+ }
+ list l2{
+ key name;
+ leaf name{
+ type int32;
+ }
+ leaf value{
+ type string;
+ }
+ }
+
+ }
+}
+EOF
+
+# JSON list input/output tests auth test with arguments:
+# 1. test name
+# 2. JSON
+# 3. XML
+function testrun()
+{
+ test=$1
+ json=$2
+ xml=$3
+
+ new "$test json in/out"
+ expecteofx "$clixon_util_json -jy $fyang -D $DBG" 0 "$json" "$json"
+
+ new "$test json in / xml out"
+ expecteofx "$clixon_util_json -y $fyang -D $DBG" 0 "$json" "$xml"
+
+ new "$test xml in / json out"
+ expecteofx "$clixon_util_xml -ojvy $fyang -D $DBG" 0 "$xml" "$json"
+}
+
+new "test params: -y $fyang"
+
+testrun "one leaf-list" '{"json:c":{"l1":[1]}}' '1'
+
+testrun "two leaf-list" '{"json:c":{"l1":[1,2]}}' '12'
+
+testrun "three leaf-list" '{"json:c":{"l1":[1,2,3]}}' '123'
+
+testrun "one list" '{"json:c":{"l2":[{"name":1,"value":"x"}]}}' '1x'
+
+testrun "two list" '{"json:c":{"l2":[{"name":1,"value":"x"},{"name":2,"value":"y"}]}}' '1x2y'
+
+testrun "three list" '{"json:c":{"l2":[{"name":1,"value":"x"},{"name":2,"value":"y"},{"name":3,"value":"z"}]}}' '1x2y3z'
+
+rm -rf $dir
+
+# unset conditional parameters
+unset clixon_util_json
+unset clixon_util_xml
+
+new "endtest"
+endtest