parser json FEATURE support for nested ext instances with data
... such as schema-mount.
diff --git a/src/parser_json.c b/src/parser_json.c
index e1dd7fe..49fd941 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1,9 +1,10 @@
/**
* @file parser_json.c
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief JSON data parser for libyang
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2022 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -28,6 +29,7 @@
#include "log.h"
#include "parser_data.h"
#include "parser_internal.h"
+#include "plugins_exts.h"
#include "set.h"
#include "tree.h"
#include "tree_data.h"
@@ -54,15 +56,14 @@
}
/**
- * @brief Submit the responsibility for releasing the dynamic values to @p dst.
+ * @brief Pass the responsibility for releasing the dynamic values to @p dst.
*
* @param[in] jsonctx JSON context which contains the dynamic value.
* @param[in,out] dst Pointer to which the responsibility will be submited.
- * If the pointer is already pointing to some allocated memory,
- * it is released beforehand.
+ * If the pointer is already pointing to some allocated memory, it is released beforehand.
*/
static void
-lyjson_ctx_submit_dynamic_value(struct lyjson_ctx *jsonctx, char **dst)
+lyjson_ctx_give_dynamic_value(struct lyjson_ctx *jsonctx, char **dst)
{
assert(jsonctx && dst);
@@ -75,10 +76,10 @@
}
*dst = NULL;
- /* Submit the dynamic value. */
+ /* give the dynamic value */
*dst = (char *)jsonctx->value;
- /* Responsibility for the release is now passed to @p dst. */
+ /* responsibility for the release is now passed to dst */
jsonctx->dynamic = 0;
}
@@ -182,95 +183,73 @@
}
/**
- * @brief Get schema node corresponding to the input parameters.
+ * @brief Try to parse data with a parent based on an extension instance.
*
* @param[in] lydctx JSON data parser context.
- * @param[in] is_attr Flag if the reference to the node is an attribute, for logging only.
- * @param[in] prefix Requested node's prefix (module name).
- * @param[in] prefix_len Length of the @p prefix.
- * @param[in] name Requested node's name.
- * @param[in] name_len Length of the @p name.
- * @param[in] parent Parent of the node being processed, can be NULL in case of top-level.
- * @param[out] snode_p Pointer to the found schema node corresponding to the input parameters.
- * @return LY_SUCCES on success, note that even in this case the returned value of @p snode_p can be NULL, so the data are expected to be parsed as opaq.
- * @return LY_EVALID on failure, error message is logged
- * @return LY_ENOT in case the input data are expected to be skipped
+ * @param[in,out] parent Parent node where the children are inserted.
+ * @return LY_SUCCESS on success;
+ * @return LY_ENOT if no extension instance parsed the data;
+ * @return LY_ERR on error.
*/
static LY_ERR
-lydjson_get_snode(const struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *prefix, size_t prefix_len, const char *name,
- size_t name_len, const struct lyd_node_inner *parent, const struct lysc_node **snode_p)
+lydjson_nested_ext(struct lyd_json_ctx *lydctx, struct lyd_node *parent)
{
- LY_ERR ret = LY_SUCCESS;
- struct lys_module *mod = NULL;
- uint32_t getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
+ LY_ERR r;
+ struct ly_in in_bck, in_start, in_ext;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_ext_instance *nested_exts = NULL;
+ lyplg_ext_data_parse_clb ext_parse_cb;
+ struct lyd_ctx_ext_val *ext_val;
+ uint32_t quot_count;
- /* init return value */
- *snode_p = NULL;
+ /* backup current input */
+ in_bck = *lydctx->jsonctx->in;
- LOG_LOCSET(NULL, parent ? &parent->node : NULL, NULL, NULL);
-
- /* get the element module */
- if (prefix_len) {
- mod = ly_ctx_get_module_implemented2(lydctx->jsonctx->ctx, prefix, prefix_len);
- } else if (parent) {
- if (parent->schema) {
- mod = parent->schema->module;
+ /* go back in the input for extension parsing */
+ in_start = *lydctx->jsonctx->in;
+ assert(lyjson_ctx_status(lydctx->jsonctx, 0) == LYJSON_OBJECT);
+ quot_count = 0;
+ do {
+ --in_start.current;
+ if ((in_start.current[0] == '\"') && (in_start.current[-1] != '\\')) {
+ ++quot_count;
}
- } else {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Top-level JSON object member \"%.*s\" must be namespace-qualified.",
- (int)(is_attr ? name_len + 1 : name_len), is_attr ? name - 1 : name);
- ret = LY_EVALID;
- goto cleanup;
+ } while (quot_count < 2);
+
+ /* check if there are any nested extension instances */
+ if (parent && parent->schema) {
+ nested_exts = parent->schema->exts;
}
- if (!mod) {
- if (lydctx->parse_opts & LYD_PARSE_STRICT) {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "No module named \"%.*s\" in the context.", (int)prefix_len, prefix);
- ret = LY_EVALID;
- goto cleanup;
- }
- if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
- ret = LY_ENOT;
- goto cleanup;
- }
- }
-
- /* get the schema node */
- if (mod && (!parent || parent->schema)) {
- if (!parent && lydctx->ext) {
- *snode_p = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
- } else {
- *snode_p = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
- }
- if (!*snode_p) {
- if (lydctx->parse_opts & LYD_PARSE_STRICT) {
- if (lydctx->ext) {
- if (lydctx->ext->argument) {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" %s extension instance.",
- (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name);
- } else {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.",
- (int)name_len, name, lydctx->ext->def->name);
- }
- } else {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
- (int)name_len, name, mod->name);
- }
- ret = LY_EVALID;
- goto cleanup;
- } else if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
- /* skip element with children */
- ret = LY_ENOT;
- goto cleanup;
+ LY_ARRAY_FOR(nested_exts, u) {
+ /* prepare the input and try to parse this extension data */
+ in_ext = in_start;
+ ext_parse_cb = nested_exts[u].def->plugin->parse;
+ r = ext_parse_cb(&in_ext, LYD_JSON, &nested_exts[u], parent, lydctx->parse_opts | LYD_PARSE_ONLY);
+ if (!r) {
+ /* data successfully parsed, remember for validation */
+ if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
+ ext_val = malloc(sizeof *ext_val);
+ LY_CHECK_ERR_RET(!ext_val, LOGMEM(lydctx->jsonctx->ctx), LY_EMEM);
+ ext_val->ext = &nested_exts[u];
+ ext_val->sibling = lyd_child_no_keys(parent);
+ LY_CHECK_RET(ly_set_add(&lydctx->ext_val, ext_val, 1, NULL));
}
- } else {
- /* check that schema node is valid and can be used */
- ret = lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode_p);
+
+ /* adjust the jsonctx accordingly */
+ *lydctx->jsonctx->in = in_ext;
+ LYJSON_STATUS_PUSH_RET(lydctx->jsonctx, LYJSON_OBJECT_CLOSED);
+ LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, NULL));
+ return LY_SUCCESS;
+ } else if (r != LY_ENOT) {
+ /* fatal error */
+ return r;
}
+ /* data was not from this module, continue */
}
-cleanup:
- LOG_LOCBACK(0, parent ? 1 : 0, 0, 0);
- return ret;
+ /* no extensions or none matched, restore input */
+ *lydctx->jsonctx->in = in_bck;
+ return LY_ENOT;
}
/**
@@ -311,6 +290,135 @@
}
/**
+ * @brief Get schema node corresponding to the input parameters.
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in] is_attr Flag if the reference to the node is an attribute, for logging only.
+ * @param[in] prefix Requested node's prefix (module name).
+ * @param[in] prefix_len Length of the @p prefix.
+ * @param[in] name Requested node's name.
+ * @param[in] name_len Length of the @p name.
+ * @param[in] parent Parent of the node being processed, can be NULL in case of top-level.
+ * @param[out] snode_p Found schema node corresponding to the input parameters. If NULL, parse as an opaque node.
+ * @return LY_SUCCES on success.
+ * @return LY_ENOT if the whole object was parsed (skipped or as an extension).
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *prefix, size_t prefix_len, const char *name,
+ size_t name_len, struct lyd_node *parent, const struct lysc_node **snode_p)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ struct lys_module *mod = NULL;
+ uint32_t getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
+
+ /* init return value */
+ *snode_p = NULL;
+
+ LOG_LOCSET(NULL, parent, NULL, NULL);
+
+ /* get the element module */
+ if (prefix_len) {
+ mod = ly_ctx_get_module_implemented2(lydctx->jsonctx->ctx, prefix, prefix_len);
+ } else if (parent) {
+ if (parent->schema) {
+ mod = parent->schema->module;
+ }
+ } else {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Top-level JSON object member \"%.*s\" must be namespace-qualified.",
+ (int)(is_attr ? name_len + 1 : name_len), is_attr ? name - 1 : name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if (!mod) {
+ /* check for extension data */
+ r = lydjson_nested_ext(lydctx, parent);
+ if (!r) {
+ /* successfully parsed */
+ ret = LY_ENOT;
+ goto cleanup;
+ } else if (r != LY_ENOT) {
+ /* error */
+ ret = r;
+ goto cleanup;
+ }
+
+ /* unknown module */
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "No module named \"%.*s\" in the context.", (int)prefix_len, prefix);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
+ /* skip element with children */
+ ret = lydjson_data_skip(lydctx->jsonctx);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = LY_ENOT;
+ goto cleanup;
+ }
+ }
+
+ /* get the schema node */
+ if (mod && (!parent || parent->schema)) {
+ if (!parent && lydctx->ext) {
+ *snode_p = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
+ } else {
+ *snode_p = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
+ }
+ if (!*snode_p) {
+ /* check for extension data */
+ r = lydjson_nested_ext(lydctx, parent);
+ if (!r) {
+ /* successfully parsed */
+ ret = LY_ENOT;
+ goto cleanup;
+ } else if (r != LY_ENOT) {
+ /* error */
+ ret = r;
+ goto cleanup;
+ }
+
+ /* unknown data node */
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ if (parent) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.",
+ (int)name_len, name, LYD_NAME(parent));
+ } else if (lydctx->ext) {
+ if (lydctx->ext->argument) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE,
+ "Node \"%.*s\" not found in the \"%s\" %s extension instance.",
+ (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name);
+ } else {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.",
+ (int)name_len, name, lydctx->ext->def->name);
+ }
+ } else {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
+ (int)name_len, name, mod->name);
+ }
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
+ /* skip element with children */
+ ret = lydjson_data_skip(lydctx->jsonctx);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = LY_ENOT;
+ goto cleanup;
+ }
+ } else {
+ /* check that schema node is valid and can be used */
+ ret = lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode_p);
+ }
+ }
+
+cleanup:
+ LOG_LOCBACK(0, parent ? 1 : 0, 0, 0);
+ return ret;
+}
+
+/**
* @brief Check that the input data are parseable as the @p list.
*
* Checks for all the list's keys. Function does not revert the context state.
@@ -592,7 +700,7 @@
lydjson_parse_name(meta_container->name.name, strlen(meta_container->name.name), &name, &name_len,
&prefix, &prefix_len, &is_attr);
assert(is_attr);
- ret = lydjson_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, (*first_p)->parent, &snode);
+ ret = lydjson_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, lyd_parent(*first_p), &snode);
assert(ret == LY_SUCCESS);
if (snode != node->schema) {
@@ -641,7 +749,8 @@
if (match != instance) {
/* there is no corresponding data node for the metadata */
if (instance > 1) {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing JSON data instance #%" PRIu64 " to be coupled with %s metadata.",
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE,
+ "Missing JSON data instance #%" PRIu64 " to be coupled with %s metadata.",
instance, meta_container->name.name);
} else {
LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing JSON data instance to be coupled with %s metadata.",
@@ -766,7 +875,7 @@
while (status != LYJSON_OBJECT_CLOSED) {
lydjson_parse_name(lydctx->jsonctx->value, lydctx->jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
- lyjson_ctx_submit_dynamic_value(lydctx->jsonctx, &dynamic_prefname);
+ lyjson_ctx_give_dynamic_value(lydctx->jsonctx, &dynamic_prefname);
if (!name_len) {
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Metadata in JSON found with an empty name, followed by: %.10s", name);
@@ -844,10 +953,6 @@
goto next_entry;
}
- /* move after the metadata */
- ret = lyjson_ctx_next(lydctx->jsonctx, NULL);
- LY_CHECK_GOTO(ret, cleanup);
-
/* success */
goto cleanup;
@@ -875,14 +980,14 @@
* @param[in] last If set, always insert at the end.
*/
static void
-lydjson_maintain_children(struct lyd_node_inner *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last)
+lydjson_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last)
{
if (*node_p) {
/* insert, keep first pointer correct */
- lyd_insert_node(parent ? &parent->node : NULL, first_p, *node_p, last);
+ lyd_insert_node(parent, first_p, *node_p, last);
if (first_p) {
if (parent) {
- *first_p = parent->child;
+ *first_p = lyd_child(parent);
} else {
while ((*first_p)->prev->next) {
*first_p = (*first_p)->prev;
@@ -910,7 +1015,7 @@
*/
static LY_ERR
lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len,
- struct lyd_node_inner *parent, enum LYJSON_PARSER_STATUS *status_inner_p, struct lyd_node **node_p)
+ struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_inner_p, struct lyd_node **node_p)
{
LY_ERR ret = LY_SUCCESS;
const char *value = NULL, *module_name;
@@ -929,7 +1034,7 @@
}
/* create node */
- lydjson_get_node_prefix(parent ? &parent->node : NULL, prefix, prefix_len, &module_name, &module_name_len);
+ lydjson_get_node_prefix(parent, prefix, prefix_len, &module_name, &module_name_len);
ret = lyd_create_opaq(lydctx->jsonctx->ctx, name, name_len, prefix, prefix_len, module_name, module_name_len, value,
value_len, &dynamic, LY_VALUE_JSON, NULL, type_hint, node_p);
if (dynamic) {
@@ -964,13 +1069,10 @@
*/
static LY_ERR
lydjson_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len,
- struct lyd_node_inner *parent, enum LYJSON_PARSER_STATUS *status_p,
- enum LYJSON_PARSER_STATUS *status_inner_p, struct lyd_node **first_p, struct lyd_node **node_p)
+ struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p, enum LYJSON_PARSER_STATUS *status_inner_p,
+ struct lyd_node **first_p, struct lyd_node **node_p)
{
- LY_ERR ret = LY_SUCCESS;
-
- ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p));
if ((*status_p == LYJSON_ARRAY) && (*status_inner_p == LYJSON_NULL)) {
/* special array null value */
@@ -1011,8 +1113,7 @@
/* continue with the next instance */
assert(node_p);
lydjson_maintain_children(parent, first_p, node_p, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
- ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p));
}
if ((*status_p == LYJSON_OBJECT) || (*status_p == LYJSON_OBJECT_EMPTY)) {
@@ -1025,10 +1126,7 @@
finish:
/* finish linking metadata */
- LY_CHECK_RET(lydjson_metadata_finish(lydctx, lyd_node_child_p(*node_p)));
-
- /* move after the item */
- return lyjson_ctx_next(lydctx->jsonctx, status_p);
+ return lydjson_metadata_finish(lydctx, lyd_node_child_p(*node_p));
}
/**
@@ -1053,7 +1151,7 @@
*/
static LY_ERR
lydjson_ctx_next_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len,
- const char *prefix, size_t prefix_len, struct lyd_node_inner *parent, enum LYJSON_PARSER_STATUS *status_p,
+ const char *prefix, size_t prefix_len, struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p,
struct lyd_node **first_p, struct lyd_node **node_p)
{
enum LYJSON_PARSER_STATUS status_inner = 0;
@@ -1074,7 +1172,8 @@
}
/* parse opaq node from the input */
- LY_CHECK_RET(lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_p, &status_inner, first_p, node_p));
+ LY_CHECK_RET(lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_p, &status_inner,
+ first_p, node_p));
return LY_SUCCESS;
}
@@ -1102,7 +1201,9 @@
const char *name, size_t name_len, const char *prefix, size_t prefix_len, struct lyd_node *parent,
enum LYJSON_PARSER_STATUS *status_p, struct lyd_node **first_p, struct lyd_node **node_p)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR r;
+ const char *opaq_name;
+ size_t opaq_name_len;
/* parse as an attribute to a node */
if (!attr_node && snode) {
@@ -1125,16 +1226,18 @@
lydctx->parse_opts &= ~LYD_PARSE_STRICT;
lydctx->parse_opts |= LYD_PARSE_OPAQ;
- ret = lydjson_ctx_next_parse_opaq(lydctx, prefix ? prefix - 1 : name - 1, prefix ? prefix_len + name_len + 2 : name_len + 1,
- NULL, 0, (struct lyd_node_inner *)parent, status_p, first_p, node_p);
+ opaq_name = prefix ? prefix - 1 : name - 1;
+ opaq_name_len = prefix ? prefix_len + name_len + 2 : name_len + 1;
+ r = lydjson_ctx_next_parse_opaq(lydctx, opaq_name, opaq_name_len, NULL, 0, parent, status_p, first_p, node_p);
/* restore the parser options */
lydctx->parse_opts = prev_opts;
+ LY_CHECK_RET(r);
} else {
- ret = lydjson_metadata(lydctx, snode, attr_node);
+ LY_CHECK_RET(lydjson_metadata(lydctx, snode, attr_node));
}
- return ret;
+ return LY_SUCCESS;
}
/**
@@ -1152,11 +1255,11 @@
* @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_ENOT 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,
+lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *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)
{
@@ -1170,7 +1273,7 @@
assert(snode->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER | LYD_NODE_ANY));
if (snode->nodetype & LYD_NODE_TERM) {
LY_CHECK_RET((*status != LYJSON_ARRAY) && (*status != LYJSON_NUMBER) && (*status != LYJSON_STRING) &&
- (*status != LYJSON_FALSE) && (*status != LYJSON_TRUE) && (*status != LYJSON_NULL), LY_EINVAL);
+ (*status != LYJSON_FALSE) && (*status != LYJSON_TRUE) && (*status != LYJSON_NULL), LY_ENOT);
/* create terminal node */
ret = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, lydctx->jsonctx->value,
@@ -1187,7 +1290,7 @@
assert(*status == LYJSON_ARRAY_CLOSED);
}
} else if (snode->nodetype == LYS_ANYXML) {
- LY_CHECK_RET((*status != LYJSON_STRING) && (*status != LYJSON_NULL), LY_EINVAL);
+ LY_CHECK_RET((*status != LYJSON_STRING) && (*status != LYJSON_NULL), LY_ENOT);
/* create anyxml node */
if (*status == LYJSON_NULL) {
@@ -1208,7 +1311,7 @@
}
} else if (snode->nodetype & LYD_NODE_INNER) {
/* create inner node */
- LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY), LY_EINVAL);
+ LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY), LY_ENOT);
ret = lyd_create_inner(snode, node);
LY_CHECK_RET(ret);
@@ -1246,7 +1349,7 @@
LOG_LOCBACK(1, 1, 0, 0);
} else if (snode->nodetype == LYS_ANYDATA) {
/* create any node */
- LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY), LY_EINVAL);
+ LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY), LY_ENOT);
/* parse any data tree with correct options */
/* first backup the current options and then make the parser to process data as opaq nodes */
@@ -1275,15 +1378,11 @@
LY_CHECK_RET(ret);
}
- /* move JSON parser */
- LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status));
-
/* add/correct flags */
lyd_parse_set_data_flags(*node, &lydctx->node_when, &(*node)->meta, lydctx->parse_opts);
} 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);
+ 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_LIST) {
@@ -1293,7 +1392,7 @@
}
}
- return ret;
+ return LY_SUCCESS;
}
/**
@@ -1308,11 +1407,11 @@
static LY_ERR
lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR ret = LY_SUCCESS, r;
enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(lydctx->jsonctx, 0);
const char *name, *prefix = NULL, *expected = NULL;
size_t name_len, prefix_len = 0;
- ly_bool is_meta = 0;
+ ly_bool is_meta = 0, parse_subtree;
const struct lysc_node *snode = NULL;
struct lyd_node *node = NULL, *attr_node = NULL;
const struct ly_ctx *ctx = lydctx->jsonctx->ctx;
@@ -1321,22 +1420,22 @@
assert(parent || first_p);
assert(status == LYJSON_OBJECT);
+ parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0;
+ /* all descendants should be parsed */
+ lydctx->parse_opts &= ~LYD_PARSE_SUBTREE;
+
/* process the node name */
lydjson_parse_name(lydctx->jsonctx->value, lydctx->jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_meta);
- lyjson_ctx_submit_dynamic_value(lydctx->jsonctx, &value);
+ lyjson_ctx_give_dynamic_value(lydctx->jsonctx, &value);
if (!is_meta || name_len || prefix_len) {
/* get the schema node */
- ret = lydjson_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, (struct lyd_node_inner *)parent, &snode);
- if (ret == LY_ENOT) {
- /* skip element with children */
- ret = lydjson_data_skip(lydctx->jsonctx);
- LY_CHECK_GOTO(ret, cleanup);
- status = lyjson_ctx_status(lydctx->jsonctx, 0);
- /* nothing for now, continue with another call of lydjson_subtree_r() */
+ r = lydjson_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, parent, &snode);
+ if (r == LY_ENOT) {
+ /* data parsed */
goto cleanup;
}
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
if (!snode) {
/* we will not be parsing it as metadata */
@@ -1372,15 +1471,13 @@
}
/* move to the second item in the name/X pair and parse opaq */
- ret = lydjson_ctx_next_parse_opaq(lydctx, name, name_len, prefix, prefix_len,
- (struct lyd_node_inner *)parent, &status, first_p, &node);
+ ret = lydjson_ctx_next_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, &status, first_p, &node);
LY_CHECK_GOTO(ret, cleanup);
} else {
/* 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);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
/* first check the expected representation according to the nodetype and then continue with the content */
switch (snode->nodetype) {
@@ -1400,21 +1497,19 @@
/* process all the values/objects */
do {
- lydjson_maintain_children((struct lyd_node_inner *)parent, first_p, &node,
- lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
+ lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
- ret = lydjson_parse_instance(lydctx, (struct lyd_node_inner *)parent, first_p, snode, name, name_len,
- prefix, prefix_len, &status, &node);
- if (ret == LY_EINVAL) {
+ ret = lydjson_parse_instance(lydctx, parent, first_p, snode, name, name_len, prefix, prefix_len,
+ &status, &node);
+ if (ret == LY_ENOT) {
goto representation_error;
} else if (ret) {
goto cleanup;
}
- } while (status != LYJSON_ARRAY_CLOSED);
- /* move after the array */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
+ /* move after the item(s) */
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ } while (status != LYJSON_ARRAY_CLOSED);
break;
case LYS_LEAF:
@@ -1435,9 +1530,8 @@
}
/* process the value/object */
- ret = lydjson_parse_instance(lydctx, (struct lyd_node_inner *)parent, first_p, snode, name, name_len,
- prefix, prefix_len, &status, &node);
- if (ret == LY_EINVAL) {
+ ret = lydjson_parse_instance(lydctx, parent, first_p, snode, name, name_len, prefix, prefix_len, &status, &node);
+ if (ret == LY_ENOT) {
goto representation_error;
} else if (ret) {
goto cleanup;
@@ -1447,20 +1541,23 @@
/* rememeber the RPC/action/notification */
lydctx->op_node = node;
}
-
break;
}
}
/* finally connect the parsed node */
- lydjson_maintain_children((struct lyd_node_inner *)parent, first_p, &node,
- lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
+ lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
/* rememeber a successfully parsed node */
if (parsed) {
ly_set_add(parsed, node, 1, NULL);
}
+ if (!parse_subtree) {
+ /* move after the item(s) */
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ }
+
/* success */
goto cleanup;
@@ -1495,6 +1592,7 @@
LY_ERR ret = LY_SUCCESS;
struct lyd_json_ctx *lydctx;
size_t i;
+ ly_bool subtree;
assert(lydctx_p);
assert(status);
@@ -1514,15 +1612,16 @@
}
}
- LY_CHECK_ERR_RET(ret = lyjson_ctx_new(ctx, in, &lydctx->jsonctx), free(lydctx), ret);
+ subtree = (parse_opts & LYD_PARSE_SUBTREE) ? 1 : 0;
+ LY_CHECK_ERR_RET(ret = lyjson_ctx_new(ctx, in, subtree, &lydctx->jsonctx), free(lydctx), ret);
*status = lyjson_ctx_status(lydctx->jsonctx, 0);
+
if ((*status == LYJSON_END) || (*status == LYJSON_OBJECT_EMPTY) || (*status == LYJSON_OBJECT)) {
*lydctx_p = lydctx;
return LY_SUCCESS;
} else {
/* expecting top-level object */
- LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected top-level JSON object, but %s found.",
- lyjson_token2str(*status));
+ LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected top-level JSON object, but %s found.", lyjson_token2str(*status));
*lydctx_p = NULL;
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
return LY_EVALID;
@@ -1537,7 +1636,7 @@
LY_ERR rc = LY_SUCCESS;
struct lyd_json_ctx *lydctx = NULL;
enum LYJSON_PARSER_STATUS status;
- uint32_t int_opts;
+ uint32_t int_opts = 0;
rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx, &status);
LY_CHECK_GOTO(rc || status == LYJSON_END || status == LYJSON_OBJECT_EMPTY, cleanup);
@@ -1546,7 +1645,9 @@
switch (data_type) {
case LYD_TYPE_DATA_YANG:
- int_opts = LYD_INTOPT_WITH_SIBLINGS;
+ if (!(parse_opts & LYD_PARSE_SUBTREE)) {
+ int_opts = LYD_INTOPT_WITH_SIBLINGS;
+ }
break;
case LYD_TYPE_RPC_YANG:
int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NO_SIBLINGS;
@@ -1596,6 +1697,19 @@
rc = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
LY_CHECK_GOTO(rc, cleanup);
+ if (parse_opts & LYD_PARSE_SUBTREE) {
+ /* check for a sibling object */
+ assert(subtree_sibling);
+ if (lydctx->jsonctx->in->current[0] == ',') {
+ *subtree_sibling = 1;
+
+ /* move to the next object */
+ ly_in_skip(lydctx->jsonctx->in, 1);
+ } else {
+ *subtree_sibling = 0;
+ }
+ }
+
cleanup:
/* there should be no unresolved types stored */
assert(!(parse_opts & LYD_PARSE_ONLY) || !lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count &&