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"