* Fixed: [JSON leaf-list output single element leaf-list does not use array](https://github.com/clicon/clixon/issues/261)

This commit is contained in:
Olof hagsand 2021-08-24 17:08:31 +02:00
parent 9fce0a1214
commit 562320dcbc
3 changed files with 113 additions and 14 deletions

View file

@ -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 `<x>42</x>` to `<y>42</y>` the datastrore changed, but was not detected by diff algorithms and provided to validate callbacks.
* Thanks: Alexander Skorichenko, Netgate

View file

@ -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
array = NO_ARRAY;
arraytype = NO_ARRAY;
}
else
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,9 +578,10 @@ 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);
break;
@ -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;

74
test/test_json_list.sh Executable file
View file

@ -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 <<EOF > $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]}}' '<c xmlns="urn:example:clixon"><l1>1</l1></c>'
testrun "two leaf-list" '{"json:c":{"l1":[1,2]}}' '<c xmlns="urn:example:clixon"><l1>1</l1><l1>2</l1></c>'
testrun "three leaf-list" '{"json:c":{"l1":[1,2,3]}}' '<c xmlns="urn:example:clixon"><l1>1</l1><l1>2</l1><l1>3</l1></c>'
testrun "one list" '{"json:c":{"l2":[{"name":1,"value":"x"}]}}' '<c xmlns="urn:example:clixon"><l2><name>1</name><value>x</value></l2></c>'
testrun "two list" '{"json:c":{"l2":[{"name":1,"value":"x"},{"name":2,"value":"y"}]}}' '<c xmlns="urn:example:clixon"><l2><name>1</name><value>x</value></l2><l2><name>2</name><value>y</value></l2></c>'
testrun "three list" '{"json:c":{"l2":[{"name":1,"value":"x"},{"name":2,"value":"y"},{"name":3,"value":"z"}]}}' '<c xmlns="urn:example:clixon"><l2><name>1</name><value>x</value></l2><l2><name>2</name><value>y</value></l2><l2><name>3</name><value>z</value></l2></c>'
rm -rf $dir
# unset conditional parameters
unset clixon_util_json
unset clixon_util_xml
new "endtest"
endtest