parsers UPDATE notification children order irrelevant
Fixes #2080
diff --git a/src/parser_internal.h b/src/parser_internal.h
index 5b632a2..92412e2 100644
--- a/src/parser_internal.h
+++ b/src/parser_internal.h
@@ -61,6 +61,7 @@
#define LYD_INTOPT_ANY 0x10 /**< Anydata/anyxml content is being parsed, there can be anything. */
#define LYD_INTOPT_WITH_SIBLINGS 0x20 /**< Parse the whole input with any siblings. */
#define LYD_INTOPT_NO_SIBLINGS 0x40 /**< If there are any siblings, return an error. */
+#define LYD_INTOPT_EVENTTIME 0x80 /**< Parse notification eventTime node. */
/**
* @brief Internal (common) context for YANG data parsers.
diff --git a/src/parser_json.c b/src/parser_json.c
index f5851e9..71544fa 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1594,7 +1594,30 @@
lydjson_parse_name(lydctx->jsonctx->value, lydctx->jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_meta);
lyjson_ctx_give_dynamic_value(lydctx->jsonctx, &value);
- if (!is_meta || name_len || prefix_len) {
+ if ((lydctx->int_opts & LYD_INTOPT_EVENTTIME) && !parent && !is_meta && name_len && !prefix_len &&
+ !ly_strncmp("eventTime", name, name_len)) {
+ /* parse eventTime */
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ if (status != LYJSON_STRING) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_STRING),
+ lyjson_token2str(status));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* create node */
+ r = lyd_create_opaq(lydctx->jsonctx->ctx, name, name_len, prefix, prefix_len, prefix, prefix_len,
+ lydctx->jsonctx->value, lydctx->jsonctx->value_len, NULL, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, &node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* validate the value */
+ r = lyd_parser_notif_eventtime_validate(node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ goto node_parsed;
+ } else 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, &ext);
if (r == LY_ENOT) {
@@ -1748,6 +1771,7 @@
}
}
+node_parsed:
/* finally connect the parsed node */
lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, ext);
@@ -1906,19 +1930,18 @@
* @param[in] jsonctx JSON parser context.
* @param[in] name Name of the object.
* @param[in] module Module name of the object, NULL if none expected.
- * @param[in] value Whether a value is expected.
* @param[out] evnp Parsed envelope (opaque node).
* @return LY_SUCCESS on success.
* @return LY_ENOT if the specified object did not match.
* @return LY_ERR value on error.
*/
static LY_ERR
-lydjson_envelope(struct lyjson_ctx *jsonctx, const char *name, const char *module, ly_bool value, struct lyd_node **envp)
+lydjson_envelope(struct lyjson_ctx *jsonctx, const char *name, const char *module, struct lyd_node **envp)
{
LY_ERR rc = LY_SUCCESS, r;
enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx);
- const char *nam, *prefix, *val = NULL;
- size_t nam_len, prefix_len, val_len = 0;
+ const char *nam, *prefix;
+ size_t nam_len, prefix_len;
ly_bool is_meta;
assert(status == LYJSON_OBJECT);
@@ -1953,24 +1976,9 @@
r = lyjson_ctx_next(jsonctx, &status);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
- if (value) {
- if (status != LYJSON_STRING) {
- LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_STRING),
- lyjson_token2str(status));
- rc = LY_EVALID;
- goto cleanup;
- }
-
- val = jsonctx->value;
- val_len = jsonctx->value_len;
-
- r = lyjson_ctx_next(jsonctx, &status);
- LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
- }
-
/* create node */
- rc = lyd_create_opaq(jsonctx->ctx, name, strlen(name), prefix, prefix_len, prefix, prefix_len, val, val_len,
- NULL, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, envp);
+ rc = lyd_create_opaq(jsonctx->ctx, name, strlen(name), prefix, prefix_len, prefix, prefix_len, NULL, 0, NULL,
+ LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, envp);
LY_CHECK_GOTO(rc, cleanup);
cleanup:
@@ -1981,51 +1989,6 @@
return rc;
}
-/**
- * @brief Parse all expected non-data JSON objects of a RESTCONF notification message.
- *
- * @param[in] jsonctx JSON parser context.
- * @param[out] evnp Parsed envelope(s) (opaque node).
- * @param[out] int_opts Internal options for parsing the rest of YANG data.
- * @param[out] close_elem Number of parsed opened objects that need to be closed.
- * @return LY_SUCCESS on success.
- * @return LY_ERR value on error.
- */
-static LY_ERR
-lydjson_env_restconf_notif(struct lyjson_ctx *jsonctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem)
-{
- LY_ERR rc = LY_SUCCESS, r;
- struct lyd_node *child;
-
- assert(envp && !*envp);
-
- /* parse "notification" */
- r = lydjson_envelope(jsonctx, "notification", "ietf-restconf", 0, envp);
- LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
-
- /* parse "eventTime" */
- r = lydjson_envelope(jsonctx, "eventTime", NULL, 1, &child);
- LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
-
- /* insert */
- lyd_insert_node(*envp, NULL, child, 0);
-
- /* validate value */
- r = lyd_parser_notif_eventtime_validate(child);
- LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
-
- /* RESTCONF notification */
- *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_NOTIF;
- *close_elem = 1;
-
-cleanup:
- if (rc) {
- lyd_free_tree(*envp);
- *envp = NULL;
- }
- return rc;
-}
-
LY_ERR
lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type,
@@ -2033,7 +1996,7 @@
{
LY_ERR rc = LY_SUCCESS, r;
struct lyd_json_ctx *lydctx = NULL;
- enum LYJSON_PARSER_STATUS status;
+ struct lyd_node *node;
uint32_t i, int_opts = 0, close_elem = 0;
assert(ctx && in && lydctx_p);
@@ -2054,7 +2017,7 @@
assert(parent);
/* parse "input" */
- rc = lydjson_envelope(lydctx->jsonctx, "input", lyd_node_module(parent)->name, 0, envp);
+ rc = lydjson_envelope(lydctx->jsonctx, "input", lyd_node_module(parent)->name, envp);
LY_CHECK_GOTO(rc, cleanup);
int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_RPC | LYD_INTOPT_ACTION;
@@ -2062,14 +2025,20 @@
break;
case LYD_TYPE_NOTIF_RESTCONF:
assert(!parent);
- rc = lydjson_env_restconf_notif(lydctx->jsonctx, envp, &int_opts, &close_elem);
+
+ /* parse "notification" */
+ rc = lydjson_envelope(lydctx->jsonctx, "notification", "ietf-restconf", envp);
LY_CHECK_GOTO(rc, cleanup);
+
+ /* RESTCONF notification and eventTime */
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_NOTIF | LYD_INTOPT_EVENTTIME;
+ close_elem = 1;
break;
case LYD_TYPE_REPLY_RESTCONF:
assert(parent);
/* parse "output" */
- rc = lydjson_envelope(lydctx->jsonctx, "output", lyd_node_module(parent)->name, 0, envp);
+ rc = lydjson_envelope(lydctx->jsonctx, "output", lyd_node_module(parent)->name, envp);
LY_CHECK_GOTO(rc, cleanup);
int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_REPLY;
@@ -2090,38 +2059,41 @@
do {
r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
-
- status = lyjson_ctx_status(lydctx->jsonctx);
-
- if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
- break;
- }
- } while (status == LYJSON_OBJECT_NEXT);
+ } while (lyjson_ctx_status(lydctx->jsonctx) == LYJSON_OBJECT_NEXT);
/* close all opened elements */
for (i = 0; i < close_elem; ++i) {
- if (status != LYJSON_OBJECT_CLOSED) {
+ if (lyjson_ctx_status(lydctx->jsonctx) != LYJSON_OBJECT_CLOSED) {
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_OBJECT_CLOSED),
- lyjson_token2str(status));
+ lyjson_token2str(lyjson_ctx_status(lydctx->jsonctx)));
rc = LY_EVALID;
goto cleanup;
}
- r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ r = lyjson_ctx_next(lydctx->jsonctx, NULL);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
- if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] &&
- (lyjson_ctx_status(lydctx->jsonctx) != LYJSON_OBJECT_CLOSED)) {
- LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
- r = LY_EVALID;
- LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
- }
if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
r = LY_EVALID;
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
+ if (int_opts & LYD_INTOPT_EVENTTIME) {
+ /* parse as a child of the envelope */
+ node = (*first_p)->prev;
+ if (node->schema) {
+ LOGVAL(ctx, LYVE_DATA, "Missing notification \"eventTime\" node.");
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ } else {
+ /* can be the only opaque node and an operation had to be parsed */
+ assert(!strcmp(LYD_NAME(node), "eventTime") && (*first_p)->next);
+ lyd_unlink_tree(node);
+ assert(*envp);
+ lyd_insert_child(*envp, node);
+ }
+ }
/* finish linking metadata */
r = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
diff --git a/src/parser_xml.c b/src/parser_xml.c
index d6de91d..93d2e84 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -980,8 +980,8 @@
const struct ly_ctx *ctx;
struct lyd_meta *meta = NULL;
struct lyd_attr *attr = NULL;
- const struct lysc_node *snode;
- struct lysc_ext_instance *ext;
+ const struct lysc_node *snode = NULL;
+ struct lysc_ext_instance *ext = NULL;
uint32_t orig_parse_opts;
struct lyd_node *node = NULL, *insert_anchor = NULL;
ly_bool parse_subtree;
@@ -1008,6 +1008,32 @@
rc = lyxml_ctx_next(xmlctx);
LY_CHECK_GOTO(rc, cleanup);
+ if ((lydctx->int_opts & LYD_INTOPT_EVENTTIME) && !parent && name_len && !prefix_len &&
+ !ly_strncmp("eventTime", name, name_len)) {
+ /* parse eventTime, create node */
+ assert(xmlctx->status == LYXML_ELEM_CONTENT);
+ rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len,
+ "urn:ietf:params:xml:ns:netconf:notification:1.0", 47, xmlctx->value,
+ xmlctx->ws_only ? 0 : xmlctx->value_len, NULL, LY_VALUE_XML, NULL, LYD_HINT_DATA, &node);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* validate the value */
+ r = lyd_parser_notif_eventtime_validate(node);
+ LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
+
+ /* parser next */
+ r = lyxml_ctx_next(xmlctx);
+ LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
+ if (xmlctx->status != LYXML_ELEM_CLOSE) {
+ LOGVAL(ctx, LYVE_DATA, "Unexpected notification \"eventTime\" node children.");
+ rc = LY_EVALID;
+ lyd_free_tree(node);
+ goto cleanup;
+ }
+
+ goto node_parsed;
+ }
+
/* get the schema node */
r = lydxml_subtree_get_snode(lydctx, parent, prefix, prefix_len, name, name_len, &snode, &ext);
if (r) {
@@ -1057,6 +1083,7 @@
}
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+node_parsed:
if (node && snode) {
/* add/correct flags */
r = lyd_parse_set_data_flags(node, &meta, (struct lyd_ctx *)lydctx, ext);
@@ -1345,66 +1372,6 @@
}
/**
- * @brief Parse all expected non-data XML elements of a NETCONF notification message.
- *
- * @param[in] xmlctx XML parser context.
- * @param[out] evnp Parsed envelope(s) (opaque node).
- * @param[out] int_opts Internal options for parsing the rest of YANG data.
- * @param[out] close_elem Number of parsed opened elements that need to be closed.
- * @return LY_SUCCESS on success.
- * @return LY_ERR value on error.
- */
-static LY_ERR
-lydxml_env_netconf_notif(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem)
-{
- LY_ERR rc = LY_SUCCESS, r;
- struct lyd_node *child;
-
- assert(envp && !*envp);
-
- /* parse "notification" */
- r = lydxml_envelope(xmlctx, "notification", "urn:ietf:params:xml:ns:netconf:notification:1.0", 0, envp);
- LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
-
- /* parse "eventTime" */
- r = lydxml_envelope(xmlctx, "eventTime", "urn:ietf:params:xml:ns:netconf:notification:1.0", 1, &child);
- if (r == LY_ENOT) {
- LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unexpected element \"%.*s\" instead of \"eventTime\".",
- (int)xmlctx->name_len, xmlctx->name);
- r = LY_EVALID;
- }
- LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
-
- /* insert */
- lyd_insert_node(*envp, NULL, child, 0);
-
- /* validate value */
- r = lyd_parser_notif_eventtime_validate(child);
- LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
-
- /* finish child parsing */
- if (xmlctx->status != LYXML_ELEM_CLOSE) {
- assert(xmlctx->status == LYXML_ELEMENT);
- LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"eventTime\".",
- (int)xmlctx->name_len, xmlctx->name);
- rc = LY_EVALID;
- goto cleanup;
- }
- LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
-
- /* NETCONF notification */
- *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_NOTIF;
- *close_elem = 1;
-
-cleanup:
- if (rc) {
- lyd_free_tree(*envp);
- *envp = NULL;
- }
- return rc;
-}
-
-/**
* @brief Parse an XML element as an opaque node subtree.
*
* @param[in] xmlctx XML parser context.
@@ -1898,6 +1865,7 @@
{
LY_ERR rc = LY_SUCCESS;
struct lyd_xml_ctx *lydctx;
+ struct lyd_node *node;
uint32_t i, int_opts = 0, close_elem = 0;
ly_bool parsed_data_nodes = 0;
@@ -1926,11 +1894,17 @@
break;
case LYD_TYPE_NOTIF_NETCONF:
assert(!parent);
- rc = lydxml_env_netconf_notif(lydctx->xmlctx, envp, &int_opts, &close_elem);
+
+ /* parse "notification" */
+ rc = lydxml_envelope(lydctx->xmlctx, "notification", "urn:ietf:params:xml:ns:netconf:notification:1.0", 0, envp);
if (rc == LY_ENOT) {
LOGVAL(ctx, LYVE_DATA, "Missing NETCONF <notification> envelope or in incorrect namespace.");
}
LY_CHECK_GOTO(rc, cleanup);
+
+ /* NETCONF notification */
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_NOTIF | LYD_INTOPT_EVENTTIME;
+ close_elem = 1;
break;
case LYD_TYPE_REPLY_NETCONF:
assert(parent);
@@ -2011,6 +1985,21 @@
rc = LY_EVALID;
goto cleanup;
}
+ if (int_opts & LYD_INTOPT_EVENTTIME) {
+ /* parse as a child of the envelope */
+ node = (*first_p)->prev;
+ if (node->schema) {
+ LOGVAL(ctx, LYVE_DATA, "Missing notification \"eventTime\" node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ } else {
+ /* can be the only opaque node and an operation had to be parsed */
+ assert(!strcmp(LYD_NAME(node), "eventTime") && (*first_p)->next);
+ lyd_unlink_tree(node);
+ assert(*envp);
+ lyd_insert_child(*envp, node);
+ }
+ }
if (!parsed_data_nodes) {
/* no data nodes were parsed */
diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c
index edc4372..2153a38 100644
--- a/tests/utests/data/test_parser_json.c
+++ b/tests/utests/data/test_parser_json.c
@@ -860,6 +860,15 @@
lyd_free_all(tree);
lyd_free_all(ntf);
+
+ /* wrong order */
+ data = "{\"ietf-restconf:notification\":{\"a:n2\":{},\"eventTime\":\"2013-12-21T00:01:00Z\"}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_RESTCONF, &tree, &ntf));
+ ly_in_free(in, 0);
+
+ lyd_free_all(tree);
+ lyd_free_all(ntf);
}
static void
diff --git a/tests/utests/data/test_parser_xml.c b/tests/utests/data/test_parser_xml.c
index 889e502..a2cd30f 100644
--- a/tests/utests/data/test_parser_xml.c
+++ b/tests/utests/data/test_parser_xml.c
@@ -753,6 +753,22 @@
lyd_free_all(tree);
lyd_free_all(op2);
+ /* notification with a different order */
+ data = "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n"
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <n1>\n"
+ " <nl>value</nl>\n"
+ " </n1>\n"
+ "</c>\n"
+ "<eventTime>2010-12-06T08:00:01Z</eventTime>\n"
+ "</notification>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_NOTIF_NETCONF, &tree, &op2));
+ ly_in_free(in, 0);
+
+ lyd_free_all(tree);
+ lyd_free_all(op2);
+
/* parse a data reply */
data = "<rpc-reply message-id=\"55\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
" <al xmlns=\"urn:tests:a\">25</al>\n"