json parser REFACTOR remove labels in recursive function
diff --git a/src/parser_json.c b/src/parser_json.c
index 578070d..d6115d6 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -498,14 +498,19 @@
enum LYJSON_PARSER_STATUS status;
assert(snode);
- assert(snode->nodetype & (LYD_NODE_TERM | LYS_LIST));
- if ((lydctx->parse_options & LYD_PARSE_OPAQ)) {
+ if (!(snode->nodetype & (LYD_NODE_TERM | LYS_LIST))) {
+ /* can always be parsed as a data node if we have the schema node */
+ return LY_SUCCESS;
+ }
+
+ if (lydctx->parse_options & LYD_PARSE_OPAQ) {
/* backup parser */
lyjson_ctx_backup(jsonctx);
status = lyjson_ctx_status(jsonctx, 0);
- /* check if the node is parseable. if not, NULL the snode to announce that it is supposed to be parsed as an opaq node */
+ /* check if the node is parseable. if not, NULL the snode to announce that it is supposed to be parsed
+ * as an opaq node */
switch (snode->nodetype) {
case LYS_LEAFLIST:
case LYS_LEAF:
@@ -515,7 +520,8 @@
break;
}
- if (_lys_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, lydjson_resolve_prefix, jsonctx, LYD_JSON)) {
+ if (_lys_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, lydjson_resolve_prefix, jsonctx,
+ LYD_JSON)) {
ret = LY_ENOT;
}
break;
@@ -525,11 +531,12 @@
/* invalid list, parse as opaque if it missing/has invalid some keys */
ret = LY_ENOT;
}
+ break;
}
/* restore parser */
lyjson_ctx_restore(jsonctx);
- } else if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+ } else if (snode->nodetype & LYD_NODE_TERM) {
status = lyjson_ctx_status(jsonctx, 0);
ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
}
@@ -1072,6 +1079,127 @@
}
/**
+ * @brief Parse a single instance of a node.
+ *
+ * @param[in] lydctx JSON data parser context. When the function returns, the context is in the same state
+ * as before calling, despite it is necessary to process input data for checking.
+ * @param[in] parent Data parent of the subtree, must be set if @p first is not.
+ * @param[in,out] first_p Pointer to the variable holding the first top-level sibling, must be set if @p parent is not.
+ * @param[in] snode Schema node corresponding to the member currently being processed in the context.
+ * @param[in] name Parsed JSON node name.
+ * @param[in] name_len Lenght of @p name.
+ * @param[in] prefix Parsed JSON node prefix.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in,out] status JSON parser status, is updated.
+ * @param[out] node Parsed data (or opaque) node.
+ * @return LY_SUCCESS if a node was successfully parsed,
+ * @return LY_EINVAL in case of invalid JSON encoding,
+ * @return LY_ERR on other errors.
+ */
+static LY_ERR
+lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node_inner *parent, struct lyd_node **first_p,
+ const struct lysc_node *snode, const char *name, size_t name_len, const char *prefix, size_t prefix_len,
+ enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
+{
+ LY_ERR ret;
+ int type_hint;
+ uint32_t prev_opts;
+ struct lyd_node *tree = NULL;
+
+ ret = lydjson_data_check_opaq(lydctx, snode, &type_hint);
+ if (ret == LY_SUCCESS) {
+ assert(snode->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER | LYD_NODE_ANY));
+ if (snode->nodetype & LYD_NODE_TERM) {
+ /* create terminal node */
+ ret = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, lydctx->jsonctx->value,
+ lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, type_hint,
+ lydjson_resolve_prefix, lydctx->jsonctx, LYD_JSON, node);
+ LY_CHECK_RET(ret);
+
+ /* move JSON parser */
+ ret = lyjson_ctx_next(lydctx->jsonctx, status);
+ LY_CHECK_RET(ret);
+ } else if (snode->nodetype & LYD_NODE_INNER) {
+ /* create inner node */
+ LY_CHECK_RET(*status != LYJSON_OBJECT && *status != LYJSON_OBJECT_EMPTY, LY_EINVAL);
+
+ ret = lyd_create_inner(snode, node);
+ LY_CHECK_RET(ret);
+
+ /* process children */
+ while (*status != LYJSON_OBJECT_CLOSED && *status != LYJSON_OBJECT_EMPTY) {
+ ret = lydjson_subtree_r(lydctx, (struct lyd_node_inner *)*node, lyd_node_children_p(*node));
+ LY_CHECK_RET(ret);
+ *status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ }
+
+ /* finish linking metadata */
+ ret = lydjson_metadata_finish(lydctx, lyd_node_children_p(*node));
+ LY_CHECK_RET(ret);
+
+ if (snode->nodetype == LYS_LIST) {
+ /* check all keys exist */
+ ret = lyd_parse_check_keys(*node);
+ LY_CHECK_RET(ret);
+ }
+
+ if (!(lydctx->parse_options & LYD_PARSE_ONLY)) {
+ /* new node validation, autodelete CANNOT occur, all nodes are new */
+ ret = lyd_validate_new(lyd_node_children_p(*node), snode, NULL, NULL);
+ LY_CHECK_RET(ret);
+
+ /* add any missing default children */
+ ret = lyd_new_implicit_r(*node, lyd_node_children_p(*node), NULL, NULL, &lydctx->unres_node_type,
+ &lydctx->when_check, (lydctx->validate_options & LYD_VALIDATE_NO_STATE)
+ ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+ LY_CHECK_RET(ret);
+ }
+
+ /* move JSON parser */
+ ret = lyjson_ctx_next(lydctx->jsonctx, status);
+ LY_CHECK_RET(ret);
+ } else if (snode->nodetype & LYD_NODE_ANY) {
+ /* create any node */
+ LY_CHECK_RET(*status != LYJSON_OBJECT && *status != LYJSON_OBJECT_EMPTY, LY_EINVAL);
+
+ /* parse any data tree with correct options */
+ /* first backup the current options and then make the parser to process data as opaq nodes */
+ prev_opts = lydctx->parse_options;
+ lydctx->parse_options &= ~LYD_PARSE_STRICT;
+ lydctx->parse_options |= LYD_PARSE_OPAQ;
+
+ /* process the anydata content */
+ while (*status != LYJSON_OBJECT_CLOSED && *status != LYJSON_OBJECT_EMPTY) {
+ ret = lydjson_subtree_r(lydctx, NULL, &tree);
+ LY_CHECK_RET(ret);
+ *status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ }
+
+ /* restore parser options */
+ lydctx->parse_options = prev_opts;
+
+ /* finish linking metadata */
+ ret = lydjson_metadata_finish(lydctx, &tree);
+ LY_CHECK_RET(ret);
+
+ ret = lyd_create_any(snode, tree, LYD_ANYDATA_DATATREE, node);
+ LY_CHECK_RET(ret);
+ }
+ } else if (ret == LY_ENOT) {
+ /* parse it again as an opaq node */
+ ret = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent,
+ status, status, first_p, node);
+ LY_CHECK_RET(ret);
+
+ if (snode->nodetype & (LYS_LEAFLIST | LYS_LIST)) {
+ ((struct lyd_node_opaq *)*node)->hint |= LYD_NODE_OPAQ_ISLIST;
+ }
+ }
+
+ return ret;
+}
+
+/**
* @brief Parse JSON subtree. All leaf-list and list instances of a node are considered one subtree.
*
* @param[in] lydctx JSON data parser context.
@@ -1089,11 +1217,9 @@
size_t name_len, prefix_len = 0;
int is_meta = 0;
const struct lysc_node *snode = NULL;
- struct lyd_node *node = NULL, *attr_node = NULL, *anchor = NULL;
+ struct lyd_node *node = NULL, *attr_node = NULL;
const struct ly_ctx *ctx = lydctx->jsonctx->ctx;
const char *expected = NULL;
- int type_hint = 0;
- uint32_t prev_opts;
assert(parent || first_p);
assert(status == LYJSON_OBJECT);
@@ -1157,7 +1283,7 @@
parent, &status, status_inner == LYJSON_ERROR ? &status : &status_inner, first_p, &node);
LY_CHECK_GOTO(ret, cleanup);
} else {
- /* parse as a standard lyd_node */
+ /* parse as a standard lyd_node but it can still turn out to be an opaque node */
/* move to the second item in the name/X pair */
ret = lyjson_ctx_next(lydctx->jsonctx, &status);
@@ -1166,7 +1292,12 @@
/* first check the expected representation according to the nodetype and then continue with the content */
switch (snode->nodetype) {
case LYS_LEAFLIST:
- expected = "name/array of values";
+ case LYS_LIST:
+ if (snode->nodetype == LYS_LEAFLIST) {
+ expected = "name/array of values";
+ } else {
+ expected = "name/array of objects";
+ }
LY_CHECK_GOTO(status != LYJSON_ARRAY, representation_error);
@@ -1174,173 +1305,59 @@
ret = lyjson_ctx_next(lydctx->jsonctx, &status);
LY_CHECK_GOTO(ret, cleanup);
- /* process the value */
- goto next_term;
- case LYS_LEAF:
- if (status == LYJSON_ARRAY) {
- expected = "name/[null]";
- } else {
- expected = "name/value";
- }
+ /* process all the values/objects */
+ do {
+ lydjson_maintain_children(parent, first_p, &node);
-next_term:
- ret = lydjson_data_check_opaq(lydctx, snode, &type_hint);
- if (ret == LY_SUCCESS) {
- /* create terminal node */
- ret = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, lydctx->jsonctx->value,
- lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, type_hint,
- lydjson_resolve_prefix, lydctx->jsonctx, LYD_JSON, &node);
- LY_CHECK_GOTO(ret, cleanup);
-
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
- } else if (ret == LY_ENOT) {
- /* parse it again as an opaq node */
- ret = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent,
- &status, &status, first_p, &node);
- LY_CHECK_GOTO(ret, cleanup);
-
- if (snode->nodetype == LYS_LEAFLIST) {
- ((struct lyd_node_opaq *)node)->hint |= LYD_NODE_OPAQ_ISLIST;
+ ret = lydjson_parse_instance(lydctx, parent, first_p, snode, name, name_len, prefix, prefix_len,
+ &status, &node);
+ if (ret == LY_EINVAL) {
+ goto representation_error;
+ } else if (ret) {
+ goto cleanup;
}
- } else if (ret == LY_EINVAL) {
- goto representation_error;
- } else {
- goto cleanup;
- }
+ } while (status != LYJSON_ARRAY_CLOSED);
- if (snode->nodetype == LYS_LEAFLIST) {
- /* continue with the next instance of the leaf-list */
- if(status != LYJSON_ARRAY_CLOSED) {
- assert(node);
- lydjson_maintain_children(parent, first_p, &node);
- goto next_term;
- }
-
- /* move after the array */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
- }
+ /* move after the array */
+ ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_GOTO(ret, cleanup);
break;
- case LYS_LIST:
- expected = "name/array of objects";
-
- LY_CHECK_GOTO(status != LYJSON_ARRAY, representation_error);
-
- /* move into array */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
-
-next_list:
- if (lydjson_data_check_opaq(lydctx, snode, &type_hint)) {
- /* parse it again as an opaq node */
- ret = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent,
- &status, &status, first_p, &node);
- LY_CHECK_GOTO(ret, cleanup);
- ((struct lyd_node_opaq *)node)->hint |= LYD_NODE_OPAQ_ISLIST;
-
- goto list_loop;
- }
- /* fallthrough - process children */
+ case LYS_LEAF:
case LYS_CONTAINER:
case LYS_NOTIF:
case LYS_ACTION:
case LYS_RPC:
- if (snode->nodetype != LYS_LIST) {
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ if (snode->nodetype == LYS_LEAF) {
+ if (status == LYJSON_ARRAY) {
+ expected = "name/[null]";
+ } else {
+ expected = "name/value";
+ }
+ } else {
expected = "name/object";
}
- LY_CHECK_GOTO(status != LYJSON_OBJECT && status != LYJSON_OBJECT_EMPTY, representation_error);
-
- /* create inner node */
- ret = lyd_create_inner(snode, &node);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* process children */
- while (status != LYJSON_OBJECT_CLOSED && status != LYJSON_OBJECT_EMPTY) {
- ret = lydjson_subtree_r(lydctx, (struct lyd_node_inner *)node, lyd_node_children_p(node));
- LY_CHECK_GOTO(ret, cleanup);
- status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ /* process the value/object */
+ ret = lydjson_parse_instance(lydctx, parent, first_p, snode, name, name_len, prefix, prefix_len, &status, &node);
+ if (ret == LY_EINVAL) {
+ goto representation_error;
+ } else if (ret) {
+ goto cleanup;
}
- /* finish linking metadata */
- ret = lydjson_metadata_finish(lydctx, lyd_node_children_p(node));
- LY_CHECK_GOTO(ret, cleanup);
-
- if (snode->nodetype == LYS_LIST) {
- /* check all keys exist */
- ret = lyd_parse_check_keys(node);
- LY_CHECK_GOTO(ret, cleanup);
- }
-
- if (!(lydctx->parse_options & LYD_PARSE_ONLY)) {
- /* new node validation, autodelete CANNOT occur, all nodes are new */
- ret = lyd_validate_new(lyd_node_children_p(node), snode, NULL, NULL);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* add any missing default children */
- ret = lyd_new_implicit_r(node, lyd_node_children_p(node), NULL, NULL, &lydctx->unres_node_type,
- &lydctx->when_check, (lydctx->validate_options & LYD_VALIDATE_NO_STATE)
- ? LYD_IMPLICIT_NO_STATE : 0, NULL);
- LY_CHECK_GOTO(ret, cleanup);
- }
-
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
- if (snode->nodetype == LYS_LIST) {
-list_loop:
- /* continue with the next instance of the list */
- if (status != LYJSON_ARRAY_CLOSED) {
- assert(node);
- lydjson_maintain_children(parent, first_p, &node);
- goto next_list;
- }
-
- /* move after the array */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
-
- } else if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+ if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
/* rememeber the RPC/action/notification */
lydctx->op_node = node;
}
break;
- case LYS_ANYDATA:
- case LYS_ANYXML:
- expected = "name/object";
- LY_CHECK_GOTO(status != LYJSON_OBJECT && status != LYJSON_OBJECT_EMPTY, representation_error);
-
- /* parse any data tree with correct options */
- /* first backup the current options and then make the parser to process data as opaq nodes */
- prev_opts = lydctx->parse_options;
- lydctx->parse_options &= ~LYD_PARSE_STRICT;
- lydctx->parse_options |= LYD_PARSE_OPAQ;
-
- /* process the anydata content */
- while (status != LYJSON_OBJECT_CLOSED && status != LYJSON_OBJECT_EMPTY) {
- ret = lydjson_subtree_r(lydctx, NULL, &anchor);
- LY_CHECK_GOTO(ret, cleanup);
- status = lyjson_ctx_status(lydctx->jsonctx, 0);
- }
-
- /* reset parser options */
- lydctx->parse_options = prev_opts;
- LY_CHECK_GOTO(ret, cleanup);
-
- /* finish linking metadata */
- ret = lydjson_metadata_finish(lydctx, &anchor);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* create any node */
- ret = lyd_create_any(snode, anchor, LYD_ANYDATA_DATATREE, &node);
- LY_CHECK_GOTO(ret, cleanup);
-
- break;
}
}
+ /* finally connect the parsed node */
lydjson_maintain_children(parent, first_p, &node);
cleanup: