schema mount UPDATE support for creating nested data
Not just parsing it directly.
diff --git a/src/parser_json.c b/src/parser_json.c
index c967063..7b780ba 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -183,80 +183,6 @@
}
/**
- * @brief Try to parse data with a parent based on an extension instance.
- *
- * @param[in] lydctx JSON data parser context.
- * @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_nested_ext(struct lyd_json_ctx *lydctx, struct lyd_node *parent)
-{
- 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;
-
- /* backup current input */
- in_bck = *lydctx->jsonctx->in;
-
- /* 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;
- }
- if (in_start.current == in_start.start) {
- /* invalid JSON */
- return LY_ENOT;
- }
- } while (quot_count < 2);
-
- /* check if there are any nested extension instances */
- if (parent && parent->schema) {
- nested_exts = parent->schema->exts;
- }
- 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));
- }
-
- /* 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 */
- }
-
- /* no extensions or none matched, restore input */
- *lydctx->jsonctx->in = in_bck;
- return LY_ENOT;
-}
-
-/**
* @brief Skip the currently open JSON object/array
* @param[in] jsonctx JSON context with the input data to skip.
* @return LY_ERR value.
@@ -303,27 +229,28 @@
* @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.
+ * @param[out] snode Found schema node corresponding to the input parameters. If NULL, parse as an opaque node.
+ * @param[out] ext Extension instance that provided @p snode, if any.
* @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)
+ size_t name_len, struct lyd_node *parent, const struct lysc_node **snode, struct lysc_ext_instance **ext)
{
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;
+ *snode = NULL;
+ *ext = NULL;
LOG_LOCSET(NULL, parent, NULL, NULL);
- /* get the element module */
+ /* get the element module, prefer parent context because of extensions */
if (prefix_len) {
- mod = ly_ctx_get_module_implemented2(lydctx->jsonctx->ctx, prefix, prefix_len);
+ mod = ly_ctx_get_module_implemented2(parent ? LYD_CTX(parent) : lydctx->jsonctx->ctx, prefix, prefix_len);
} else if (parent) {
if (parent->schema) {
mod = parent->schema->module;
@@ -336,13 +263,9 @@
}
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 */
+ r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_JSON, NULL, name, name_len, snode, ext);
+ if (r != LY_ENOT) {
+ /* success or error */
ret = r;
goto cleanup;
}
@@ -366,19 +289,15 @@
/* 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);
+ *snode = 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);
+ *snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
}
- if (!*snode_p) {
+ if (!*snode) {
/* 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 */
+ r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_JSON, NULL, name, name_len, snode, ext);
+ if (r != LY_ENOT) {
+ /* success or error */
ret = r;
goto cleanup;
}
@@ -413,7 +332,7 @@
}
} else {
/* check that schema node is valid and can be used */
- ret = lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode_p);
+ ret = lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode);
}
}
@@ -634,6 +553,7 @@
{
LY_ERR ret = LY_SUCCESS;
struct lyd_node *node, *attr, *next, *meta_iter;
+ struct lysc_ext_instance *ext;
uint64_t instance = 0;
const char *prev = NULL;
uint32_t log_location_items = 0;
@@ -704,8 +624,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, lyd_parent(*first_p), &snode);
- assert(ret == LY_SUCCESS);
+ lydjson_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, lyd_parent(*first_p), &snode, &ext);
if (snode != node->schema) {
continue;
@@ -742,10 +661,10 @@
goto cleanup;
}
}
- /* add/correct flags */
- lyd_parse_set_data_flags(node, &lydctx->node_when, &node->meta, lydctx->parse_opts);
- /* done */
+ /* add/correct flags */
+ ret = lyd_parse_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, ext);
+ LY_CHECK_GOTO(ret, cleanup);
break;
}
}
@@ -929,7 +848,8 @@
LY_CHECK_GOTO(ret, cleanup);
/* add/correct flags */
- lyd_parse_set_data_flags(node, &lydctx->node_when, &meta, lydctx->parse_opts);
+ ret = lyd_parse_set_data_flags(node, &meta, (struct lyd_ctx *)lydctx, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
} else {
/* create attribute */
const char *module_name;
@@ -982,13 +902,19 @@
* @param[in,out] first_p Pointer to the first sibling node in case of top-level.
* @param[in,out] node_p pointer to the new node to insert, after the insert is done, pointer is set to NULL.
* @param[in] last If set, always insert at the end.
+ * @param[in] ext Extension instance of @p node_p, if any.
*/
static void
-lydjson_maintain_children(struct lyd_node *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,
+ struct lysc_ext_instance *ext)
{
if (*node_p) {
/* insert, keep first pointer correct */
- lyd_insert_node(parent, first_p, *node_p, last);
+ if (ext) {
+ lyd_insert_ext(parent, *node_p);
+ } else {
+ lyd_insert_node(parent, first_p, *node_p, last);
+ }
if (first_p) {
if (parent) {
*first_p = lyd_child(parent);
@@ -1116,7 +1042,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);
+ lydjson_maintain_children(parent, first_p, node_p, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, NULL);
LY_CHECK_RET(lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p));
}
@@ -1250,6 +1176,7 @@
* @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] snode Schema node corresponding to the member currently being processed in the context.
+ * @param[in] ext Extension instance of @p snode, if any.
* @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,
@@ -1257,8 +1184,8 @@
* @return LY_ERR on other errors.
*/
static LY_ERR
-lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, enum LYJSON_PARSER_STATUS *status,
- struct lyd_node **node)
+lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext,
+ enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
{
LY_ERR r;
uint32_t prev_parse_opts, prev_int_opts;
@@ -1294,7 +1221,7 @@
* process data as opaq nodes */
prev_parse_opts = lydctx->parse_opts;
lydctx->parse_opts &= ~LYD_PARSE_STRICT;
- lydctx->parse_opts |= LYD_PARSE_OPAQ;
+ lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
prev_int_opts = lydctx->int_opts;
lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
@@ -1382,6 +1309,7 @@
* @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] ext Extension instance of @p snode, if any.
* @param[in] name Parsed JSON node name.
* @param[in] name_len Lenght of @p name.
* @param[in] prefix Parsed JSON node prefix.
@@ -1394,11 +1322,12 @@
*/
static LY_ERR
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)
+ const struct lysc_node *snode, struct lysc_ext_instance *ext, 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;
uint32_t type_hints = 0;
+ uint32_t prev_parse_opts;
ret = lydjson_data_check_opaq(lydctx, snode, &type_hints);
if (ret == LY_SUCCESS) {
@@ -1429,6 +1358,12 @@
LOG_LOCSET(snode, *node, NULL, NULL);
+ prev_parse_opts = lydctx->parse_opts;
+ if (ext) {
+ /* only parse these extension data and validate afterwards */
+ lydctx->parse_opts |= LYD_PARSE_ONLY;
+ }
+
/* process children */
while ((*status != LYJSON_OBJECT_CLOSED) && (*status != LYJSON_OBJECT_EMPTY)) {
ret = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
@@ -1436,6 +1371,9 @@
*status = lyjson_ctx_status(lydctx->jsonctx, 0);
}
+ /* restore options */
+ lydctx->parse_opts = prev_parse_opts;
+
/* finish linking metadata */
ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node));
LY_CHECK_ERR_RET(ret, LOG_LOCBACK(1, 1, 0, 0), ret);
@@ -1460,11 +1398,11 @@
LOG_LOCBACK(1, 1, 0, 0);
} else {
/* create any node */
- LY_CHECK_RET(lydjson_parse_any(lydctx, snode, status, node));
+ LY_CHECK_RET(lydjson_parse_any(lydctx, snode, ext, status, node));
}
/* add/correct flags */
- lyd_parse_set_data_flags(*node, &lydctx->node_when, &(*node)->meta, lydctx->parse_opts);
+ lyd_parse_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext);
} 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);
@@ -1498,6 +1436,7 @@
size_t name_len, prefix_len = 0;
ly_bool is_meta = 0, parse_subtree;
const struct lysc_node *snode = NULL;
+ struct lysc_ext_instance *ext;
struct lyd_node *node = NULL, *attr_node = NULL;
const struct ly_ctx *ctx = lydctx->jsonctx->ctx;
char *value = NULL;
@@ -1515,7 +1454,7 @@
if (!is_meta || name_len || prefix_len) {
/* get the schema node */
- r = lydjson_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, parent, &snode);
+ r = lydjson_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, parent, &snode, &ext);
if (r == LY_ENOT) {
/* data parsed */
goto cleanup;
@@ -1582,15 +1521,14 @@
/* process all the values/objects */
do {
- lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
-
- ret = lydjson_parse_instance(lydctx, parent, first_p, snode, name, name_len, prefix, prefix_len,
+ ret = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
&status, &node);
if (ret == LY_ENOT) {
goto representation_error;
} else if (ret) {
goto cleanup;
}
+ lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, ext);
/* move after the item(s) */
LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
@@ -1615,7 +1553,8 @@
}
/* process the value/object */
- ret = lydjson_parse_instance(lydctx, parent, first_p, snode, name, name_len, prefix, prefix_len, &status, &node);
+ ret = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
+ &status, &node);
if (ret == LY_ENOT) {
goto representation_error;
} else if (ret) {
@@ -1631,7 +1570,7 @@
}
/* finally connect the parsed node */
- lydjson_maintain_children(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, ext);
/* rememeber a successfully parsed node */
if (parsed) {