json parser & printer CHANGE all data types now supported
diff --git a/src/parser_json.c b/src/parser_json.c
index 2a57775..0a464a5 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -735,7 +735,7 @@
static unsigned int
json_parse_data(struct ly_ctx *ctx, const char *data, const struct lys_node *schema_parent, struct lyd_node **parent,
struct lyd_node *first_sibling, struct lyd_node *prev, struct attr_cont **attrs, int options,
- struct unres_data *unres)
+ struct unres_data *unres, struct lyd_node **act_notif)
{
unsigned int len = 0;
unsigned int r;
@@ -819,22 +819,6 @@
if (module) {
/* get the proper schema node */
while ((schema = (struct lys_node *)lys_getnext(schema, NULL, module, 0))) {
- /* skip nodes in module's data which are not expected here according to options' data type */
- if (options & LYD_OPT_RPC) {
- if (schema->nodetype != LYS_RPC) {
- continue;
- }
- } else if (options & LYD_OPT_NOTIF) {
- if (schema->nodetype != LYS_NOTIF) {
- continue;
- }
- } else if (!(options & LYD_OPT_RPCREPLY)) {
- /* rest of the data types except RPCREPLY which cannot be here */
- if (schema->nodetype & (LYS_INPUT | LYS_OUTPUT | LYS_NOTIF)) {
- continue;
- }
- }
-
if (!strcmp(schema->name, name)) {
break;
}
@@ -857,9 +841,9 @@
/* go through RPC's input/output following the options' data type */
if ((*parent)->schema->nodetype == LYS_RPC) {
while ((schema = (struct lys_node *)lys_getnext(schema, (*parent)->schema, module, LYS_GETNEXT_WITHINOUT))) {
- if ((options & LYD_OPT_RPC) && schema->nodetype == LYS_INPUT) {
+ if ((options & LYD_OPT_RPC) && (schema->nodetype == LYS_INPUT)) {
break;
- } else if ((options & LYD_OPT_RPCREPLY) && schema->nodetype == LYS_OUTPUT) {
+ } else if ((options & LYD_OPT_RPCREPLY) && (schema->nodetype == LYS_OUTPUT)) {
break;
}
}
@@ -946,6 +930,7 @@
case LYS_LIST:
case LYS_NOTIF:
case LYS_RPC:
+ case LYS_ACTION:
result = calloc(1, sizeof *result);
break;
case LYS_LEAF:
@@ -1032,7 +1017,24 @@
}
len += r;
len += skip_ws(&data[len]);
- } else if (schema->nodetype & (LYS_CONTAINER | LYS_RPC | LYS_NOTIF)) {
+ } else if (schema->nodetype & (LYS_CONTAINER | LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+ if (schema->nodetype & (LYS_RPC | LYS_ACTION)) {
+ if (!(options & LYD_OPT_RPC) || *act_notif) {
+ LOGVAL(LYE_INELEM, LY_VLOG_LYD, result, schema->name);
+ LOGVAL(LYE_SPEC, LY_VLOG_LYD, result, "Unexpected %s node \"%s\".",
+ (schema->nodetype == LYS_RPC ? "rpc" : "action"), schema->name);
+ goto error;
+ }
+ *act_notif = result;
+ } else if (schema->nodetype == LYS_NOTIF) {
+ if (!(options & LYD_OPT_NOTIF) || *act_notif) {
+ LOGVAL(LYE_INELEM, LY_VLOG_LYD, result, schema->name);
+ LOGVAL(LYE_SPEC, LY_VLOG_LYD, result, "Unexpected notification node \"%s\".", schema->name);
+ goto error;
+ }
+ *act_notif = result;
+ }
+
if (data[len] != '{') {
LOGVAL(LYE_XML_INVAL, LY_VLOG_LYD, result, "JSON data (missing begin-object)");
goto error;
@@ -1049,7 +1051,7 @@
len++;
len += skip_ws(&data[len]);
- r = json_parse_data(ctx, &data[len], NULL, &result, result->child, diter, &attrs_aux, options, unres);
+ r = json_parse_data(ctx, &data[len], NULL, &result, result->child, diter, &attrs_aux, options, unres, act_notif);
if (!r) {
goto error;
}
@@ -1103,7 +1105,7 @@
len++;
len += skip_ws(&data[len]);
- r = json_parse_data(ctx, &data[len], NULL, &list, list->child, diter, &attrs_aux, options, unres);
+ r = json_parse_data(ctx, &data[len], NULL, &list, list->child, diter, &attrs_aux, options, unres, act_notif);
if (!r) {
goto error;
}
@@ -1223,16 +1225,16 @@
lyd_parse_json(struct ly_ctx *ctx, const char *data, int options, const struct lyd_node *rpc_act,
const struct lyd_node *data_tree)
{
- struct lyd_node *result = NULL, *next = NULL, *iter = NULL;
+ struct lyd_node *result = NULL, *next, *iter, *reply_parent = NULL, *reply_top = NULL, *act_notif = NULL;
struct unres_data *unres = NULL;
unsigned int len = 0, r;
- int i;
+ int i, act_cont = 0;
struct attr_cont *attrs = NULL;
struct ly_set *set;
ly_errno = LY_SUCCESS;
- if (!ctx || !data || (options & LYD_OPT_RPCREPLY)) {
+ if (!ctx || !data) {
LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
return NULL;
}
@@ -1258,12 +1260,64 @@
return NULL;
}
+ /* create RPC/action reply part that is not in the parsed data */
+ if (rpc_act) {
+ assert(options & LYD_OPT_RPCREPLY);
+ if (rpc_act->schema->nodetype == LYS_RPC) {
+ /* RPC request */
+ reply_top = reply_parent = _lyd_new(NULL, rpc_act->schema, 0);
+ } else {
+ /* action request */
+ reply_top = lyd_dup(rpc_act, 1);
+ LY_TREE_DFS_BEGIN(reply_top, iter, reply_parent) {
+ if (reply_parent->schema->nodetype == LYS_ACTION) {
+ break;
+ }
+ LY_TREE_DFS_END(reply_top, iter, reply_parent);
+ }
+ if (!reply_parent) {
+ LOGERR(LY_EINVAL, "%s: invalid variable parameter (const struct lyd_node *rpc_act).", __func__);
+ lyd_free_withsiblings(reply_top);
+ goto error;
+ }
+ lyd_free_withsiblings(reply_parent->child);
+ }
+ }
+
+ iter = NULL;
+ next = reply_parent;
do {
len++;
len += skip_ws(&data[len]);
- r = json_parse_data(ctx, &data[len], NULL, &next, result, iter, &attrs, options, unres);
+ if (!act_cont) {
+ if (!strncmp(&data[len], "\"yang:action\"", 13)) {
+ len += 13;
+ len += skip_ws(&data[len]);
+ if (data[len] != ':') {
+ LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "JSON data (missing top-level begin-object)");
+ lyd_free_withsiblings(reply_top);
+ goto error;
+ }
+ ++len;
+ len += skip_ws(&data[len]);
+ if (data[len] != '{') {
+ LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "JSON data (missing top level yang:action object)");
+ lyd_free_withsiblings(reply_top);
+ goto error;
+ }
+ ++len;
+ len += skip_ws(&data[len]);
+
+ act_cont = 1;
+ } else {
+ act_cont = -1;
+ }
+ }
+
+ r = json_parse_data(ctx, &data[len], NULL, &next, result, iter, &attrs, options, unres, &act_notif);
if (!r) {
+ lyd_free_withsiblings(reply_top);
goto error;
}
len += r;
@@ -1275,7 +1329,7 @@
iter = next;
}
next = NULL;
- } while(data[len] == ',');
+ } while (data[len] == ',');
if (data[len] != '}') {
/* expecting end-object */
@@ -1285,16 +1339,38 @@
len++;
len += skip_ws(&data[len]);
+ if (act_cont == 1) {
+ if (data[len] != '}') {
+ LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "JSON data (missing top-level end-object)");
+ goto error;
+ }
+ len++;
+ len += skip_ws(&data[len]);
+ }
+
/* store attributes */
if (store_attrs(ctx, attrs, result)) {
goto error;
}
+ if (reply_top) {
+ result = reply_top;
+ }
+
if (!result) {
LOGERR(LY_EVALID, "Model for the data to be linked with not found.");
goto error;
}
+ if ((options & LYD_OPT_RPCREPLY) && (rpc_act->schema->nodetype != LYS_RPC)) {
+ /* action reply */
+ act_notif = reply_parent;
+ } else if ((options & (LYD_OPT_RPC | LYD_OPT_NOTIF)) && !act_notif) {
+ ly_vecode = LYVE_INELEM;
+ LOGVAL(LYE_SPEC, LY_VLOG_LYD, result, "Missing %s node.", (options & LYD_OPT_RPC ? "action" : "notification"));
+ goto error;
+ }
+
/* check for uniquness of top-level lists/leaflists because
* only the inner instances were tested in lyv_data_content() */
set = ly_set_new();
@@ -1318,12 +1394,12 @@
ly_set_free(set);
/* add/validate default values, unres */
- if (lyd_defaults_add_unres(&result, options, ctx, data_tree, NULL, unres)) {
+ if (lyd_defaults_add_unres(&result, options, ctx, data_tree, act_notif, unres)) {
goto error;
}
/* check for missing top level mandatory nodes */
- if (!(options & LYD_OPT_TRUSTED) && lyd_check_mandatory_tree(result, ctx, options)) {
+ if (!(options & LYD_OPT_TRUSTED) && lyd_check_mandatory_tree((act_notif ? act_notif : result), ctx, options)) {
goto error;
}