parser data UPDATE parsing RESTCONF messages directly
diff --git a/src/common.h b/src/common.h
index 3c8f78c..8364b21 100644
--- a/src/common.h
+++ b/src/common.h
@@ -59,6 +59,7 @@
 #define GETMACRO4(_1, _2, _3, _4, NAME, ...) NAME
 #define GETMACRO5(_1, _2, _3, _4, _5, NAME, ...) NAME
 #define GETMACRO6(_1, _2, _3, _4, _5, _6, NAME, ...) NAME
+#define GETMACRO7(_1, _2, _3, _4, _5, _6, _7, NAME, ...) NAME
 
 /******************************************************************************
  * Logger
@@ -220,8 +221,10 @@
     LY_CHECK_ARG_RET1(CTX, ARG4, RETVAL)
 #define LY_CHECK_ARG_RET5(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL) LY_CHECK_ARG_RET4(CTX, ARG1, ARG2, ARG3, ARG4, RETVAL);\
     LY_CHECK_ARG_RET1(CTX, ARG5, RETVAL)
-#define LY_CHECK_ARG_RET(CTX, ...) GETMACRO6(__VA_ARGS__, LY_CHECK_ARG_RET5, LY_CHECK_ARG_RET4, LY_CHECK_ARG_RET3, \
-    LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1, DUMMY) (CTX, __VA_ARGS__)
+#define LY_CHECK_ARG_RET6(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, RETVAL) LY_CHECK_ARG_RET5(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL);\
+    LY_CHECK_ARG_RET1(CTX, ARG6, RETVAL)
+#define LY_CHECK_ARG_RET(CTX, ...) GETMACRO7(__VA_ARGS__, LY_CHECK_ARG_RET6, LY_CHECK_ARG_RET5, LY_CHECK_ARG_RET4, \
+    LY_CHECK_ARG_RET3, LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1, DUMMY) (CTX, __VA_ARGS__)
 
 #define LY_CHECK_CTX_EQUAL_RET2(CTX1, CTX2, RETVAL) if ((CTX1) && (CTX2) && ((CTX1) != (CTX2))) \
     {LOGERR(CTX1, LY_EINVAL, "Different contexts mixed in a single function call."); return RETVAL;}
diff --git a/src/parser_common.c b/src/parser_common.c
index 0a035a1..693ca65 100644
--- a/src/parser_common.c
+++ b/src/parser_common.c
@@ -46,6 +46,7 @@
 #include "parser_data.h"
 #include "path.h"
 #include "plugins_exts/metadata.h"
+#include "schema_compile_node.h"
 #include "schema_features.h"
 #include "set.h"
 #include "tree.h"
@@ -65,6 +66,50 @@
 }
 
 LY_ERR
+lyd_parser_notif_eventtime_validate(const struct lyd_node *node)
+{
+    LY_ERR rc = LY_SUCCESS;
+    struct ly_ctx *ctx = (struct ly_ctx *)LYD_CTX(node);
+    struct lysc_ctx cctx;
+    const struct lys_module *mod;
+    LY_ARRAY_COUNT_TYPE u;
+    struct ly_err_item *err = NULL;
+    struct lysp_type *type_p = NULL;
+    struct lysc_pattern **patterns = NULL;
+    const char *value;
+
+    LYSC_CTX_INIT_CTX(cctx, ctx);
+
+    /* get date-and-time parsed type */
+    mod = ly_ctx_get_module_latest(ctx, "ietf-yang-types");
+    assert(mod);
+    LY_ARRAY_FOR(mod->parsed->typedefs, u) {
+        if (!strcmp(mod->parsed->typedefs[u].name, "date-and-time")) {
+            type_p = &mod->parsed->typedefs[u].type;
+            break;
+        }
+    }
+    assert(type_p);
+
+    /* compile patterns */
+    assert(type_p->patterns);
+    LY_CHECK_GOTO(rc = lys_compile_type_patterns(&cctx, type_p->patterns, NULL, &patterns), cleanup);
+
+    /* validate */
+    value = lyd_get_value(node);
+    rc = lyplg_type_validate_patterns(patterns, value, strlen(value), &err);
+
+cleanup:
+    FREE_ARRAY(&cctx.free_ctx, patterns, lysc_pattern_free);
+    if (rc && err) {
+        LOGVAL_ERRITEM(ctx, err);
+        ly_err_free(err);
+        LOGVAL(ctx, LYVE_DATA, "Invalid \"eventTime\" in the notification.");
+    }
+    return rc;
+}
+
+LY_ERR
 lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op)
 {
     const struct lyd_node *iter;
diff --git a/src/parser_data.h b/src/parser_data.h
index c72cf4d..1bad5b2 100644
--- a/src/parser_data.h
+++ b/src/parser_data.h
@@ -310,21 +310,29 @@
  * @{
  */
 enum lyd_type {
-    LYD_TYPE_DATA_YANG = 0, /* generic YANG instance data */
-    LYD_TYPE_RPC_YANG,      /* instance of a YANG RPC/action request with only "input" data children,
-                               including all parents in case of an action */
-    LYD_TYPE_NOTIF_YANG,    /* instance of a YANG notification, including all parents in case of a nested one */
-    LYD_TYPE_REPLY_YANG,    /* instance of a YANG RPC/action reply with only "output" data children,
-                               including all parents in case of an action */
+    LYD_TYPE_DATA_YANG = 0,     /* generic YANG instance data */
+    LYD_TYPE_RPC_YANG,          /* instance of a YANG RPC/action request with only "input" data children,
+                                   including all parents in case of an action */
+    LYD_TYPE_NOTIF_YANG,        /* instance of a YANG notification, including all parents in case of a nested one */
+    LYD_TYPE_REPLY_YANG,        /* instance of a YANG RPC/action reply with only "output" data children,
+                                   including all parents in case of an action */
 
-    LYD_TYPE_RPC_NETCONF,   /* complete NETCONF RPC invocation as defined for
-                               [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
-                               [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
-    LYD_TYPE_NOTIF_NETCONF, /* complete NETCONF notification message as defined for
-                               [notification](https://tools.ietf.org/html/rfc7950#section-7.16.2) */
-    LYD_TYPE_REPLY_NETCONF  /* complete NETCONF RPC reply as defined for
-                               [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
-                               [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
+    LYD_TYPE_RPC_NETCONF,       /* complete NETCONF RPC invocation as defined for
+                                   [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
+                                   [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
+    LYD_TYPE_NOTIF_NETCONF,     /* complete NETCONF notification message as defined for
+                                   [notification](https://tools.ietf.org/html/rfc7950#section-7.16.2) */
+    LYD_TYPE_REPLY_NETCONF,     /* complete NETCONF RPC reply as defined for
+                                   [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
+                                   [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
+
+    LYD_TYPE_RPC_RESTCONF,      /* message-body of a RESTCONF operation input parameters
+                                   ([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-3.6.1)) */
+    LYD_TYPE_NOTIF_RESTCONF,    /* RESTCONF JSON notification data
+                                   ([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-6.4)), to parse
+                                   a notification in XML, use ::LYD_TYPE_NOTIF_NETCONF */
+    LYD_TYPE_REPLY_RESTCONF     /* message-body of a RESTCONF operation output parameters
+                                   ([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-3.6.2)) */
 };
 /** @} datatype */
 
@@ -357,6 +365,28 @@
  *   - @p op - must be NULL, the reply is appended to the RPC;
  *   Note that there are 3 kinds of NETCONF replies - ok, error, and data. Only data reply appends any nodes to the RPC.
  *
+ * - ::LYD_TYPE_RPC_RESTCONF:
+ *   - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
+ *   - @p format - can be both ::LYD_JSON and ::LYD_XML;
+ *   - @p tree - must be provided, all the RESTCONF-specific JSON objects will be returned here as
+ *               a separate opaque data tree, even if the function fails, this may be returned;
+ *   - @p op - must be NULL, @p parent points to the operation;
+ *
+ * - ::LYD_TYPE_NOTIF_RESTCONF:
+ *   - @p parent - must be NULL, the whole notification is expected;
+ *   - @p format - must be ::LYD_JSON, XML-formatted notifications are parsed using ::LYD_TYPE_NOTIF_NETCONF;
+ *   - @p tree - must be provided, all the RESTCONF-specific JSON objects will be returned here as
+ *               a separate opaque data tree, even if the function fails, this may be returned;
+ *   - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation;
+ *
+ * - ::LYD_TYPE_REPLY_RESTCONF:
+ *   - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
+ *   - @p format - can be both ::LYD_JSON and ::LYD_XML;
+ *   - @p tree - must be provided, all the RESTCONF-specific JSON objects will be returned here as
+ *               a separate opaque data tree, even if the function fails, this may be returned;
+ *   - @p op - must be NULL, @p parent points to the operation;
+ *   Note that error reply should be parsed as 'yang-data' extension data.
+ *
  * @param[in] ctx libyang context.
  * @param[in] parent Optional parent to connect the parsed nodes to.
  * @param[in] in Input handle to read the input from.
diff --git a/src/parser_internal.h b/src/parser_internal.h
index f284bca..419d68e 100644
--- a/src/parser_internal.h
+++ b/src/parser_internal.h
@@ -298,6 +298,27 @@
         struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p);
 
 /**
+ * @brief Parse JSON string as a RESTCONF message.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
+ * @param[in] in Input structure.
+ * @param[in] parse_opts Options for parser, see @ref dataparseroptions.
+ * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] data_type Expected RESTCONF data type of the data.
+ * @param[out] envp Individual parsed envelopes tree, may be returned possibly even on an error.
+ * @param[out] parsed Set to add all the parsed siblings into.
+ * @param[out] subtree_sibling Set if ::LYD_PARSE_SUBTREE is used and another subtree is following in @p in.
+ * @param[out] lydctx_p Data parser context to finish validation.
+ * @return LY_ERR value.
+ */
+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,
+        struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p);
+
+/**
  * @brief Parse binary LYB data as a YANG data tree.
  *
  * @param[in] ctx libyang context.
@@ -318,6 +339,15 @@
         struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p);
 
 /**
+ * @brief Validate eventTime date-and-time value.
+ *
+ * @param[in] node Opaque eventTime node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+LY_ERR lyd_parser_notif_eventtime_validate(const struct lyd_node *node);
+
+/**
  * @brief Search all the parents for an operation node, check validity based on internal parser flags.
  *
  * @param[in] parent Parent to connect the parsed nodes to.
diff --git a/src/parser_json.c b/src/parser_json.c
index ea9551a..4c93e86 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1862,3 +1862,255 @@
     }
     return rc;
 }
+
+/**
+ * @brief Parse a specific JSON object into an opaque node.
+ *
+ * @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)
+{
+    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;
+    ly_bool is_meta;
+
+    assert(status == LYJSON_OBJECT);
+
+    *envp = NULL;
+
+    r = lyjson_ctx_next(jsonctx, &status);
+    LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+    if (status == LYJSON_OBJECT_CLOSED) {
+        LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Empty JSON object.");
+        rc = LY_EVALID;
+        goto cleanup;
+    }
+
+    /* process the name */
+    assert(status == LYJSON_OBJECT_NAME);
+    lydjson_parse_name(jsonctx->value, jsonctx->value_len, &nam, &nam_len, &prefix, &prefix_len, &is_meta);
+    if (is_meta) {
+        LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected metadata.");
+        rc = LY_EVALID;
+        goto cleanup;
+    } else if (module && ly_strncmp(module, prefix, prefix_len)) {
+        LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected module \"%.*s\" instead of \"%s\".", (int)prefix_len, prefix, module);
+        rc = LY_EVALID;
+        goto cleanup;
+    } else if (ly_strncmp(name, nam, nam_len)) {
+        LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected object \"%.*s\" instead of \"%s\".", (int)nam_len, nam, name);
+        rc = LY_EVALID;
+        goto cleanup;
+    }
+
+    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);
+    LY_CHECK_GOTO(rc, cleanup);
+
+cleanup:
+    if (rc) {
+        lyd_free_tree(*envp);
+        *envp = NULL;
+    }
+    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,
+        struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p)
+{
+    LY_ERR rc = LY_SUCCESS, r;
+    struct lyd_json_ctx *lydctx;
+    enum LYJSON_PARSER_STATUS status;
+    uint32_t i, int_opts = 0, close_elem = 0;
+    ly_bool parsed_data_nodes = 0;
+
+    assert(ctx && in && lydctx_p);
+    assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
+    assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
+
+    assert((data_type == LYD_TYPE_RPC_RESTCONF) || (data_type == LYD_TYPE_NOTIF_RESTCONF) ||
+            (data_type == LYD_TYPE_REPLY_RESTCONF));
+    assert(!(parse_opts & LYD_PARSE_SUBTREE));
+
+    /* init context */
+    rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx);
+    LY_CHECK_GOTO(rc, cleanup);
+    lydctx->ext = ext;
+
+    switch (data_type) {
+    case LYD_TYPE_RPC_RESTCONF:
+        assert(parent);
+
+        /* parse "input" */
+        rc = lydjson_envelope(lydctx->jsonctx, "input", lyd_owner_module(parent)->name, 0, envp);
+        LY_CHECK_GOTO(rc, cleanup);
+
+        int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_RPC | LYD_INTOPT_ACTION;
+        close_elem = 1;
+        break;
+    case LYD_TYPE_NOTIF_RESTCONF:
+        assert(!parent);
+        rc = lydjson_env_restconf_notif(lydctx->jsonctx, envp, &int_opts, &close_elem);
+        LY_CHECK_GOTO(rc, cleanup);
+        break;
+    case LYD_TYPE_REPLY_RESTCONF:
+        assert(parent);
+
+        /* parse "output" */
+        rc = lydjson_envelope(lydctx->jsonctx, "output", lyd_owner_module(parent)->name, 0, envp);
+        LY_CHECK_GOTO(rc, cleanup);
+
+        int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_REPLY;
+        close_elem = 1;
+        break;
+    default:
+        LOGINT(ctx);
+        rc = LY_EINT;
+        goto cleanup;
+    }
+
+    lydctx->int_opts = int_opts;
+
+    /* find the operation node if it exists already */
+    LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
+
+    /* read subtree(s) */
+    do {
+        r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
+        LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+        parsed_data_nodes = 1;
+
+        status = lyjson_ctx_status(lydctx->jsonctx);
+
+        if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
+            break;
+        }
+    } while (status == LYJSON_OBJECT_NEXT);
+    assert((status == LYJSON_END) || (status == LYJSON_OBJECT_CLOSED));
+
+    /* close all opened elements */
+    for (i = 0; i < close_elem; ++i) {
+        if (status != LYJSON_OBJECT_CLOSED) {
+            LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_OBJECT_CLOSED),
+                    lyjson_token2str(status));
+            rc = LY_EVALID;
+            goto cleanup;
+        }
+
+        r = lyjson_ctx_next(lydctx->jsonctx, &status);
+        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);
+    }
+
+    /* finish linking metadata */
+    r = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
+    LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+    if (!parsed_data_nodes) {
+        /* no data nodes were parsed */
+        lydctx->op_node = NULL;
+    }
+
+cleanup:
+    /* there should be no unres stored if validation should be skipped */
+    assert(!(parse_opts & LYD_PARSE_ONLY) || (!lydctx->node_types.count && !lydctx->meta_types.count &&
+            !lydctx->node_when.count));
+
+    if (rc) {
+        lyd_json_ctx_free((struct lyd_ctx *)lydctx);
+    } else {
+        *lydctx_p = (struct lyd_ctx *)lydctx;
+
+        /* the JSON context is no more needed, freeing it also stops logging line numbers which would be confusing now */
+        lyjson_ctx_free(lydctx->jsonctx);
+        lydctx->jsonctx = NULL;
+    }
+    return rc;
+}
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 0f86a68..54e6c8b 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -1278,57 +1278,6 @@
 }
 
 /**
- * @brief Validate eventTime date-and-time value.
- *
- * @param[in] node Opaque eventTime node.
- * @return LY_SUCCESS on success.
- * @return LY_ERR value on error.
- */
-static LY_ERR
-lydxml_env_netconf_eventtime_validate(const struct lyd_node *node)
-{
-    LY_ERR rc = LY_SUCCESS;
-    struct ly_ctx *ctx = (struct ly_ctx *)LYD_CTX(node);
-    struct lysc_ctx cctx;
-    const struct lys_module *mod;
-    LY_ARRAY_COUNT_TYPE u;
-    struct ly_err_item *err = NULL;
-    struct lysp_type *type_p = NULL;
-    struct lysc_pattern **patterns = NULL;
-    const char *value;
-
-    LYSC_CTX_INIT_CTX(cctx, ctx);
-
-    /* get date-and-time parsed type */
-    mod = ly_ctx_get_module_latest(ctx, "ietf-yang-types");
-    assert(mod);
-    LY_ARRAY_FOR(mod->parsed->typedefs, u) {
-        if (!strcmp(mod->parsed->typedefs[u].name, "date-and-time")) {
-            type_p = &mod->parsed->typedefs[u].type;
-            break;
-        }
-    }
-    assert(type_p);
-
-    /* compile patterns */
-    assert(type_p->patterns);
-    LY_CHECK_GOTO(rc = lys_compile_type_patterns(&cctx, type_p->patterns, NULL, &patterns), cleanup);
-
-    /* validate */
-    value = lyd_get_value(node);
-    rc = lyplg_type_validate_patterns(patterns, value, strlen(value), &err);
-
-cleanup:
-    FREE_ARRAY(&cctx.free_ctx, patterns, lysc_pattern_free);
-    if (rc && err) {
-        LOGVAL_ERRITEM(ctx, err);
-        ly_err_free(err);
-        LOGVAL(ctx, LYVE_DATA, "Invalid \"eventTime\" in the notification.");
-    }
-    return rc;
-}
-
-/**
  * @brief Parse all expected non-data XML elements of a NETCONF notification message.
  *
  * @param[in] xmlctx XML parser context.
@@ -1369,7 +1318,7 @@
     lyd_insert_node(*envp, NULL, child, 0);
 
     /* validate value */
-    r = lydxml_env_netconf_eventtime_validate(child);
+    r = lyd_parser_notif_eventtime_validate(child);
     LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
 
     /* finish child parsing */
@@ -1868,9 +1817,6 @@
     assert(ctx && in && lydctx_p);
     assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
     assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
-
-    assert((data_type == LYD_TYPE_RPC_NETCONF) || (data_type == LYD_TYPE_NOTIF_NETCONF) ||
-            (data_type == LYD_TYPE_REPLY_NETCONF));
     assert(!(parse_opts & LYD_PARSE_SUBTREE));
 
     /* init context */
@@ -1907,6 +1853,32 @@
         }
         LY_CHECK_GOTO(rc, cleanup);
         break;
+    case LYD_TYPE_RPC_RESTCONF:
+        assert(parent);
+
+        /* parse "input" */
+        rc = lydxml_envelope(lydctx->xmlctx, "input", lyd_owner_module(parent)->ns, 0, envp);
+        if (rc == LY_ENOT) {
+            LOGVAL(ctx, LYVE_DATA, "Missing RESTCONF \"input\" object or in incorrect namespace.");
+        }
+        LY_CHECK_GOTO(rc, cleanup);
+
+        int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_RPC | LYD_INTOPT_ACTION;
+        close_elem = 1;
+        break;
+    case LYD_TYPE_REPLY_RESTCONF:
+        assert(parent);
+
+        /* parse "output" */
+        rc = lydxml_envelope(lydctx->xmlctx, "output", lyd_owner_module(parent)->ns, 0, envp);
+        if (rc == LY_ENOT) {
+            LOGVAL(ctx, LYVE_DATA, "Missing RESTCONF \"output\" object or in incorrect namespace.");
+        }
+        LY_CHECK_GOTO(rc, cleanup);
+
+        int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_REPLY;
+        close_elem = 1;
+        break;
     default:
         LOGINT(ctx);
         rc = LY_EINT;
diff --git a/src/tree_data.c b/src/tree_data.c
index 45f944b..d38acf2 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -261,28 +261,7 @@
  *
  * At least one of @p parent, @p tree, or @p op must always be set.
  *
- * Specific @p data_type values have different parameter meaning as follows:
- * - ::LYD_TYPE_RPC_NETCONF:
- *   - @p parent - must be NULL, the whole RPC is expected;
- *   - @p format - must be ::LYD_XML, NETCONF supports only this format;
- *   - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
- *               a separate opaque data tree, even if the function fails, this may be returned;
- *   - @p op - must be provided, the RPC/action data tree itself will be returned here, pointing to the operation;
- *
- * - ::LYD_TYPE_NOTIF_NETCONF:
- *   - @p parent - must be NULL, the whole notification is expected;
- *   - @p format - must be ::LYD_XML, NETCONF supports only this format;
- *   - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
- *               a separate opaque data tree, even if the function fails, this may be returned;
- *   - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation;
- *
- * - ::LYD_TYPE_REPLY_NETCONF:
- *   - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
- *   - @p format - must be ::LYD_XML, NETCONF supports only this format;
- *   - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
- *               a separate opaque data tree, even if the function fails, this may be returned;
- *   - @p op - must be NULL, the reply is appended to the RPC;
- *   Note that there are 3 kinds of NETCONF replies - ok, error, and data. Only data reply appends any nodes to the RPC.
+ * Specific @p data_type values have different parameter meaning as mentioned for ::lyd_parse_op().
  *
  * @param[in] ctx libyang context.
  * @param[in] ext Extension instance providing the specific schema tree to match with the data being parsed.
@@ -304,6 +283,7 @@
     struct ly_set parsed = {0};
     struct lyd_node *first = NULL, *envp = NULL;
     uint32_t i, parse_opts, val_opts, int_opts = 0;
+    ly_bool proto_msg = 0;
 
     if (!ctx) {
         ctx = LYD_CTX(parent);
@@ -325,20 +305,51 @@
     val_opts = 0;
 
     switch (data_type) {
-
-    /* special XML NETCONF data types */
     case LYD_TYPE_RPC_NETCONF:
     case LYD_TYPE_NOTIF_NETCONF:
         LY_CHECK_ARG_RET(ctx, format == LYD_XML, !parent, tree, op, LY_EINVAL);
-    /* fallthrough */
+        proto_msg = 1;
+        break;
     case LYD_TYPE_REPLY_NETCONF:
-        if (data_type == LYD_TYPE_REPLY_NETCONF) {
-            LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, !op,
-                    LY_EINVAL);
-        }
+        LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema, parent->schema->nodetype & (LYS_RPC | LYS_ACTION),
+                tree, !op, LY_EINVAL);
+        proto_msg = 1;
+        break;
+    case LYD_TYPE_RPC_RESTCONF:
+    case LYD_TYPE_REPLY_RESTCONF:
+        LY_CHECK_ARG_RET(ctx, parent, parent->schema, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, !op, LY_EINVAL);
+        proto_msg = 1;
+        break;
+    case LYD_TYPE_NOTIF_RESTCONF:
+        LY_CHECK_ARG_RET(ctx, format == LYD_JSON, !parent, tree, op, LY_EINVAL);
+        proto_msg = 1;
+        break;
 
-        /* parse the NETCONF message */
-        rc = lyd_parse_xml_netconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+    /* set internal opts */
+    case LYD_TYPE_RPC_YANG:
+        int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NO_SIBLINGS;
+        break;
+    case LYD_TYPE_NOTIF_YANG:
+        int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS;
+        break;
+    case LYD_TYPE_REPLY_YANG:
+        int_opts = LYD_INTOPT_REPLY | LYD_INTOPT_NO_SIBLINGS;
+        break;
+    case LYD_TYPE_DATA_YANG:
+        LOGINT(ctx);
+        rc = LY_EINT;
+        goto cleanup;
+    }
+
+    /* parse a full protocol message */
+    if (proto_msg) {
+        if (format == LYD_XML) {
+            /* parse the NETCONF (or RESTCONF XML) message */
+            rc = lyd_parse_xml_netconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+        } else {
+            /* parse the RESTCONF message */
+            rc = lyd_parse_json_restconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+        }
         if (rc) {
             if (envp) {
                 /* special situation when the envelopes were parsed successfully */
@@ -358,21 +369,6 @@
             *op = lydctx->op_node;
         }
         goto cleanup;
-
-    /* set internal opts */
-    case LYD_TYPE_RPC_YANG:
-        int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NO_SIBLINGS;
-        break;
-    case LYD_TYPE_NOTIF_YANG:
-        int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS;
-        break;
-    case LYD_TYPE_REPLY_YANG:
-        int_opts = LYD_INTOPT_REPLY | LYD_INTOPT_NO_SIBLINGS;
-        break;
-    case LYD_TYPE_DATA_YANG:
-        LOGINT(ctx);
-        rc = LY_EINT;
-        goto cleanup;
     }
 
     /* parse the data */
diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c
index 7c142f7..d2c40e6 100644
--- a/tests/utests/data/test_parser_json.c
+++ b/tests/utests/data/test_parser_json.c
@@ -777,6 +777,82 @@
     /* TODO */
 }
 
+static void
+test_restconf_rpc(void **state)
+{
+    const char *data;
+    struct ly_in *in;
+    struct lyd_node *tree, *envp;
+
+    assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-nmda", "2019-01-07", NULL)));
+
+    assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/ietf-netconf-nmda:edit-data", NULL, 0, &tree));
+
+    data = "{\"ietf-netconf-nmda:input\":{"
+            "\"datastore\":\"ietf-datastores:running\","
+            "\"config\":{\"a:cp\":{\"z\":[null],\"@z\":{\"ietf-netconf:operation\":\"replace\"}},"
+            "\"a:l1\":[{\"@\":{\"ietf-netconf:operation\":\"replace\"},\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}"
+            "}}";
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+    assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, tree, in, LYD_JSON, LYD_TYPE_RPC_RESTCONF, &envp, NULL));
+    ly_in_free(in, 0);
+
+    /* the same just connected to the edit-data RPC */
+    data = "{\"ietf-netconf-nmda:edit-data\":{"
+            "\"datastore\":\"ietf-datastores:running\","
+            "\"config\":{\"a:cp\":{\"z\":[null],\"@z\":{\"ietf-netconf:operation\":\"replace\"}},"
+            "\"a:l1\":[{\"@\":{\"ietf-netconf:operation\":\"replace\"},\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}"
+            "}}";
+    CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+    lyd_free_all(tree);
+    lyd_free_all(envp);
+}
+
+static void
+test_restconf_notification(void **state)
+{
+    const char *data;
+    struct ly_in *in;
+    struct lyd_node *tree, *ntf;
+
+    data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2013-12-21T00:01:00Z\",\"a:c\":{\"n1\":{\"nl\":\"value\"}}}}";
+    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);
+
+    /* envelopes separately */
+    data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2013-12-21T00:01:00Z\"}}";
+    CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+
+    /* notification with the parent node */
+    data = "{\"a:c\":{\"n1\":{\"nl\":\"value\"}}}";
+    CHECK_LYD_STRING(lyd_parent(ntf), LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+
+    lyd_free_all(tree);
+    lyd_free_all(ntf);
+}
+
+static void
+test_restconf_reply(void **state)
+{
+    const char *data;
+    struct ly_in *in;
+    struct lyd_node *tree, *envp;
+
+    assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:c/act", NULL, 0, &tree));
+
+    data = "{\"a:output\":{\"al\":25}}";
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+    assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, lyd_child(tree), in, LYD_JSON, LYD_TYPE_REPLY_RESTCONF, &envp, NULL));
+    ly_in_free(in, 0);
+
+    /* connected to the RPC with the parent */
+    data = "{\"a:c\":{\"act\":{\"al\":25}}}";
+    CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+    lyd_free_all(tree);
+    lyd_free_all(envp);
+}
+
 int
 main(void)
 {
@@ -792,6 +868,9 @@
         UTEST(test_action, setup),
         UTEST(test_notification, setup),
         UTEST(test_reply, setup),
+        UTEST(test_restconf_rpc, setup),
+        UTEST(test_restconf_notification, setup),
+        UTEST(test_restconf_reply, setup),
     };
 
     return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/data/test_parser_xml.c b/tests/utests/data/test_parser_xml.c
index d63fafa..04397ae 100644
--- a/tests/utests/data/test_parser_xml.c
+++ b/tests/utests/data/test_parser_xml.c
@@ -789,6 +789,64 @@
 }
 
 static void
+test_restconf_rpc(void **state)
+{
+    const char *data;
+    struct ly_in *in;
+    struct lyd_node *tree, *envp;
+
+    assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-nmda", "2019-01-07", NULL)));
+
+    assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/ietf-netconf-nmda:edit-data", NULL, 0, &tree));
+
+    data = "<input xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-nmda\">"
+            "<datastore xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">ds:running</datastore>"
+            "<config>"
+            "<cp xmlns=\"urn:tests:a\"><z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\"/></cp>"
+            "<l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">"
+            "<a>val_a</a><b>val_b</b><c>val_c</c>"
+            "</l1>"
+            "</config></input>";
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+    assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, tree, in, LYD_XML, LYD_TYPE_RPC_RESTCONF, &envp, NULL));
+    ly_in_free(in, 0);
+
+    /* the same just connected to the edit-data RPC */
+    data = "<edit-data xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-nmda\">"
+            "<datastore xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">ds:running</datastore>"
+            "<config>"
+            "<cp xmlns=\"urn:tests:a\"><z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\"/></cp>"
+            "<l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">"
+            "<a>val_a</a><b>val_b</b><c>val_c</c>"
+            "</l1>"
+            "</config></edit-data>";
+    CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+    lyd_free_all(tree);
+    lyd_free_all(envp);
+}
+
+static void
+test_restconf_reply(void **state)
+{
+    const char *data;
+    struct ly_in *in;
+    struct lyd_node *tree, *envp;
+
+    assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:c/act", NULL, 0, &tree));
+
+    data = "<output xmlns=\"urn:tests:a\"><al>25</al></output>";
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+    assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, lyd_child(tree), in, LYD_XML, LYD_TYPE_REPLY_RESTCONF, &envp, NULL));
+    ly_in_free(in, 0);
+
+    /* connected to the RPC with the parent */
+    data = "<c xmlns=\"urn:tests:a\"><act><al>25</al></act></c>";
+    CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+    lyd_free_all(tree);
+    lyd_free_all(envp);
+}
+
+static void
 test_filter_attributes(void **state)
 {
     const char *data;
@@ -879,6 +937,8 @@
         UTEST(test_netconf_rpc, setup),
         UTEST(test_netconf_action, setup),
         UTEST(test_netconf_reply_or_notification, setup),
+        UTEST(test_restconf_rpc, setup),
+        UTEST(test_restconf_reply, setup),
         UTEST(test_filter_attributes, setup),
         UTEST(test_data_skip, setup),
     };