data FEATURE parser for YANG data in JSON format
diff --git a/src/common.h b/src/common.h
index 8010c0b..f7faa86 100644
--- a/src/common.h
+++ b/src/common.h
@@ -343,6 +343,11 @@
 int ly_strncmp(const char *refstr, const char *str, size_t str_len);
 
 /**
+ * @brief Wrapper around strlen() to handle NULL strings.
+ */
+#define ly_strlen(STR) (STR ? strlen(STR) : 0)
+
+/**
  * @brief Get UTF8 code point of the next character in the input string.
  *
  * @param[in,out] input Input string to process, updated according to the processed/read data.
@@ -454,7 +459,7 @@
                                    const char **value, size_t *value_len, const char **errmsg);
 
 /**
- * @brief ly_clb_get_prefix implementation for JSON. For its simplicity, this implementation is used
+ * @brief ly_get_prefix_clb implementation for JSON. For its simplicity, this implementation is used
  * internally for various purposes.
  *
  * Implemented in printer_json.c
diff --git a/src/json.h b/src/json.h
index 8373284..7b2221e 100644
--- a/src/json.h
+++ b/src/json.h
@@ -52,6 +52,9 @@
 };
 
 struct lyjson_ctx {
+    const struct ly_ctx *ctx;
+    uint64_t line;          /* current line */
+    struct ly_in *in;       /* input structure */
 
     struct ly_set status;   /* stack of LYJSON_PARSER_STATUS values corresponding to the JSON items being processed */
 
diff --git a/src/log.h b/src/log.h
index e71608f..9176a04 100644
--- a/src/log.h
+++ b/src/log.h
@@ -174,6 +174,7 @@
     LYVE_XPATH,        /**< invalid XPath expression */
     LYVE_SEMANTICS,    /**< generic semantic error */
     LYVE_SYNTAX_XML,   /**< XML-related syntax error */
+    LYVE_SYNTAX_JSON,  /**< JSON-related syntax error */
     LYVE_DATA,         /**< YANG data does not reflect some of the module restrictions */
 
     LYVE_OTHER         /**< Unknown error */
diff --git a/src/lyb.h b/src/lyb.h
index bc159f3..000fab3 100644
--- a/src/lyb.h
+++ b/src/lyb.h
@@ -18,6 +18,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "parser_internal.h"
 #include "set.h"
 #include "tree.h"
 
@@ -26,10 +27,13 @@
 struct lyd_node;
 struct lysc_node;
 
-/**
- * @brief Internal structure for LYB parser/printer.
- */
-struct lyd_lyb_ctx {
+struct lylyb_ctx {
+    const struct ly_ctx *ctx;
+    uint64_t line;             /* current line */
+    struct ly_in *in;          /* input structure */
+
+    const struct lys_module **models;
+
     struct lyd_lyb_subtree {
         size_t written;
         size_t position;
@@ -37,24 +41,6 @@
     } *subtrees;
     LY_ARRAY_COUNT_TYPE subtree_size;
 
-    const struct ly_ctx *ctx;
-    union {
-        struct {
-            int parse_options;
-            int validate_options;
-        };
-        int print_options;
-    };
-
-    /* LYB parser only */
-    struct ly_in *in;
-    int int_opts;
-    const struct lys_module **models;
-    struct ly_set unres_node_type;
-    struct ly_set unres_meta_type;
-    struct ly_set when_check;
-    struct lyd_node *op_ntf;
-
     /* LYB printer only */
     struct lyd_lyb_sib_ht {
         struct lysc_node *first_sibling;
@@ -63,6 +49,39 @@
 };
 
 /**
+ * @brief Internal structure for LYB parser/printer.
+ *
+ * Note that the structure maps to the lyd_ctx which is common for all the data parsers
+ */
+struct lyd_lyb_ctx {
+    union {
+        struct {
+            uint32_t parse_options;        /**< various @ref dataparseroptions. */
+            uint32_t validate_options;     /**< various @ref datavalidationoptions. */
+        };
+        uint32_t print_options;
+    };
+    uint32_t int_opts;             /**< internal data parser options */
+    uint32_t path_len;             /**< used bytes in the path buffer */
+    char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
+    struct ly_set unres_node_type; /**< set of nodes validated with LY_EINCOMPLETE result */
+    struct ly_set unres_meta_type; /**< set of metadata validated with LY_EINCOMPLETE result */
+    struct ly_set when_check;      /**< set of nodes with "when" conditions */
+    struct lyd_node *op_node;      /**< if an RPC/action/notification is being parsed, store the pointer to it */
+
+    /* callbacks */
+    lyd_ctx_free_clb free;           /* destructor */
+    ly_resolve_prefix_clb resolve_prefix;
+
+    struct lylyb_ctx *lybctx;      /* lyb format context */
+};
+
+/**
+ * @brief Destructor for the lylyb_ctx structure
+ */
+void lyd_lyb_ctx_free(struct lyd_ctx *lydctx);
+
+/**
  * LYB format
  *
  * Unlike XML or JSON, it is binary format so most data are represented in similar way but in binary.
diff --git a/src/parser.c b/src/parser.c
index fe8528b..e84dc58 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -28,7 +28,9 @@
 #include "compat.h"
 #include "dict.h"
 #include "log.h"
+#include "parser_data.h"
 #include "parser_internal.h"
+#include "tree_data_internal.h"
 #include "tree_schema_internal.h"
 
 API LY_IN_TYPE
@@ -353,3 +355,83 @@
     in->current += count;
     return LY_SUCCESS;
 }
+
+void
+lyd_ctx_free(struct lyd_ctx *lydctx)
+{
+    ly_set_erase(&lydctx->unres_node_type, NULL);
+    ly_set_erase(&lydctx->unres_meta_type, NULL);
+    ly_set_erase(&lydctx->when_check, NULL);
+}
+
+LY_ERR
+lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode)
+{
+    /* alternatively, we could provide line for the error messages, but it doesn't work for the LYB format */
+
+    if ((lydctx->parse_options & LYD_PARSE_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
+        LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LY_VCODE_INNODE, "state", snode->name);
+        return LY_EVALID;
+    }
+
+    if (snode->nodetype & (LYS_RPC | LYS_ACTION)) {
+        if (lydctx->int_opts & LYD_INTOPT_RPC) {
+            if (lydctx->op_node) {
+                LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
+                       lys_nodetype2str(snode->nodetype), snode->name,
+                       lys_nodetype2str(lydctx->op_node->schema->nodetype), lydctx->op_node->schema->name);
+                return LY_EVALID;
+            }
+        } else {
+            LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\".",
+                   lys_nodetype2str(snode->nodetype), snode->name);
+            return LY_EVALID;
+        }
+    } else if (snode->nodetype == LYS_NOTIF) {
+        if (lydctx->int_opts & LYD_INTOPT_NOTIF) {
+            if (lydctx->op_node) {
+                LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
+                       lys_nodetype2str(snode->nodetype), snode->name,
+                       lys_nodetype2str(lydctx->op_node->schema->nodetype), lydctx->op_node->schema->name);
+                return LY_EVALID;
+            }
+        } else {
+            LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\".",
+                   lys_nodetype2str(snode->nodetype), snode->name);
+            return LY_EVALID;
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const char *value, size_t value_len,
+                       int *dynamic, int value_hints, ly_resolve_prefix_clb get_prefix, void *prefix_data,
+                       LYD_FORMAT format, struct lyd_node **node)
+{
+    LY_ERR ret;
+
+    ret = lyd_create_term(schema, value, value_len, dynamic, value_hints, get_prefix, prefix_data, format, node);
+    if (ret == LY_EINCOMPLETE) {
+        if (!(lydctx->parse_options & LYD_PARSE_ONLY)) {
+            ly_set_add(&lydctx->unres_node_type, *node, LY_SET_OPT_USEASLIST);
+        }
+        ret = LY_SUCCESS;
+    }
+    return ret;
+}
+
+LY_ERR
+lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod,
+                       const char *name, size_t name_len, const char *value, size_t value_len, int *dynamic, int value_hints,
+                       ly_resolve_prefix_clb resolve_prefix, void *prefix_data, LYD_FORMAT format, const struct lysc_node *ctx_snode)
+{
+    LY_ERR ret;
+    ret = lyd_create_meta(parent, meta, mod, name, name_len, value, value_len, dynamic, value_hints, resolve_prefix, prefix_data, format, ctx_snode);
+    if (ret == LY_EINCOMPLETE) {
+        ly_set_add(&lydctx->unres_meta_type, *meta, LY_SET_OPT_USEASLIST);
+        ret = LY_SUCCESS;
+    }
+    return ret;
+}
diff --git a/src/parser_data.h b/src/parser_data.h
index de9f992..a2bac8b 100644
--- a/src/parser_data.h
+++ b/src/parser_data.h
@@ -220,8 +220,9 @@
  * @param[in] format Format of the input data to be parsed.
  * @param[out] tree Resulting full RPC/action reply tree built from the input data. The returned data are expected to be freed using lyd_free_all().
  * The reply tree always includes duplicated operation node (and its parents) of the @p request, so in contrast to YANG data tree,
- * the result of parsing RPC/action reply cannot be NULL until an error occurs.
- * @param[out] op Optional pointer to the actual operation node inside the full action reply @p tree, useful only for action.
+ * the result of parsing RPC/action reply cannot be NULL until an error occurs. At least one of the @p tree and @p op output variables must be provided.
+ * @param[out] op Pointer to the actual operation node inside the full action reply @p tree, useful only for action. At least one of the @p op
+ * and @p tree output variables must be provided.
  * @return LY_SUCCESS in case of successful parsing (and validation).
  * @return LY_ERR value in case of error. Additional error information can be obtained from the request's context using ly_err* functions.
  */
diff --git a/src/parser_internal.h b/src/parser_internal.h
index f60e5bd..b2c4f15 100644
--- a/src/parser_internal.h
+++ b/src/parser_internal.h
@@ -28,6 +28,40 @@
  */
 #define LYD_VALIDATE_OPTS_MASK 0x0000FFFF
 
+struct lyd_ctx;
+/**
+ * @brief Callback for lyd_ctx to free the structure
+ *
+ * @param[in] ctx Data parser context to free.
+ */
+typedef void (*lyd_ctx_free_clb)(struct lyd_ctx *ctx);
+
+/**
+ * @brief Internal (common) context for YANG data parsers.
+ */
+struct lyd_ctx {
+    uint32_t parse_options;        /**< various @ref dataparseroptions. */
+    uint32_t validate_options;     /**< various @ref datavalidationoptions. */
+    uint32_t int_opts;             /**< internal data parser options */
+    uint32_t path_len;             /**< used bytes in the path buffer */
+#define LYD_PARSER_BUFSIZE 4078
+    char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
+    struct ly_set unres_node_type; /**< set of nodes validated with LY_EINCOMPLETE result */
+    struct ly_set unres_meta_type; /**< set of metadata validated with LY_EINCOMPLETE result */
+    struct ly_set when_check;      /**< set of nodes with "when" conditions */
+    struct lyd_node *op_node;      /**< if an RPC/action/notification is being parsed, store the pointer to it */
+
+    /* callbacks */
+    lyd_ctx_free_clb free;             /* destructor */
+    ly_resolve_prefix_clb resolve_prefix;
+
+    struct {
+        const struct ly_ctx *ctx;
+        uint64_t line;             /* current line */
+        struct ly_in *in;          /* input structure */
+    } *data_ctx;                   /* generic pointer supposed to map to and access (common part of) XML/JSON/... parser contexts */
+};
+
 /**
  * @brief Parser input structure specifying where the data are read.
  */
@@ -48,6 +82,11 @@
 };
 
 /**
+ * @brief Common part of the lyd_ctx_free_t callbacks.
+ */
+void lyd_ctx_free(struct lyd_ctx *);
+
+/**
  * @brief Read bytes from an input.
  *
  * @param[in] in Input structure.
@@ -115,4 +154,33 @@
 LY_ERR yin_parse_submodule(struct lys_yin_parser_ctx **yin_ctx, struct ly_ctx *ctx, struct lys_parser_ctx *main_ctx,
                            struct ly_in *in, struct lysp_submodule **submod);
 
+/**
+ * @brief Check that a data node representing the @p snode is suitable based on options.
+ *
+ * @param[in] lydctx Common data parsers context.
+ * @param[in] snode Schema node to check.
+ * @return LY_SUCCESS or LY_EVALID
+ */
+LY_ERR lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode);
+
+/**
+ * @brief Wrapper around lyd_create_term() for data parsers.
+ *
+ * @param[in] lydctx Data parser context.
+ * @param[in] value_hints Data parser's hint for the value's type.
+ */
+LY_ERR lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const char *value, size_t value_len,
+                              int *dynamic, int value_hints, ly_resolve_prefix_clb get_prefix, void *prefix_data,
+                              LYD_FORMAT format, struct lyd_node **node);
+
+/**
+ * @brief Wrapper around lyd_create_meta() for data parsers.
+ *
+ * @param[in] lydctx Data parser context.
+ * @param[in] value_hints [Value hint](@ref lydvalueparseopts) from the parser regarding the value type.
+ */
+LY_ERR lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod,
+                              const char *name, size_t name_len, const char *value, size_t value_len, int *dynamic, int value_hints,
+                              ly_resolve_prefix_clb resolve_prefix, void *prefix_data, LYD_FORMAT format, const struct lysc_node *ctx_snode);
+
 #endif /* LY_PARSER_INTERNAL_H_ */
diff --git a/src/parser_json.c b/src/parser_json.c
index 3163693..37ee765 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -3,7 +3,7 @@
  * @author Radek Krejci <rkrejci@cesnet.cz>
  * @brief JSON data parser for libyang
  *
- * Copyright (c) 2019 CESNET, z.s.p.o.
+ * Copyright (c) 2020 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.
@@ -14,6 +14,7 @@
 
 #define _GNU_SOURCE
 
+#include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -21,23 +22,1846 @@
 #include "compat.h"
 #include "config.h"
 #include "context.h"
+#include "json.h"
+#include "parser_internal.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+#include "validation.h"
 
 /**
- * @brief JSON-parser's implementation of ly_type_resolve_prefix() callback to provide mapping between prefixes used
- * in the values to the schema via context module names.
+ * @brief Internal context for JSON YANG data parser.
+ *
+ * Note that the structure maps to the lyd_ctx which is common for all the data parsers
+ */
+struct lyd_json_ctx {
+    uint32_t parse_options;        /**< various @ref dataparseroptions. */
+    uint32_t validate_options;     /**< various @ref datavalidationoptions. */
+    uint32_t int_opts;             /**< internal data parser options */
+    uint32_t path_len;             /**< used bytes in the path buffer */
+    char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
+    struct ly_set unres_node_type; /**< set of nodes validated with LY_EINCOMPLETE result */
+    struct ly_set unres_meta_type; /**< set of metadata validated with LY_EINCOMPLETE result */
+    struct ly_set when_check;      /**< set of nodes with "when" conditions */
+    struct lyd_node *op_node;      /**< if an RPC/action/notification is being parsed, store the pointer to it */
+
+    /* callbacks */
+    lyd_ctx_free_clb free;           /* destructor */
+    ly_resolve_prefix_clb resolve_prefix;
+
+    struct lyjson_ctx *jsonctx;    /**< JSON context */
+};
+
+/**
+ * @brief Free the JSON data parser context.
+ *
+ * JSON implementation of lyd_ctx_free_clb().
+ */
+static void
+lyd_json_ctx_free(struct lyd_ctx *lydctx)
+{
+    struct lyd_json_ctx *ctx = (struct lyd_json_ctx *)lydctx;
+
+    if (lydctx) {
+        lyd_ctx_free(lydctx);
+        lyjson_ctx_free(ctx->jsonctx);
+        free(ctx);
+    }
+}
+
+/**
+ * @brief JSON implementation of ly_resolve_prefix_clb.
  */
 const struct lys_module *
 lydjson_resolve_prefix(const struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *UNUSED(parser))
 {
-    const struct lys_module *mod;
-    char *name;
+    return ly_ctx_get_module_implemented2(ctx, prefix, prefix_len);
+}
 
-    name = strndup(prefix, prefix_len);
-    if (!name) {
-        return NULL;
+/**
+ * @brief Parse JSON member-name as [\@][prefix:][name]
+ *
+ * \@ - metadata flag, maps to 1 in @p is_attr_p
+ * prefix - name of the module of the data node
+ * name - name of the data node
+ *
+ * All the output parameter are mandatory. Function only parse the member-name, all the appropriate checks are up to the caller.
+ *
+ * @param[in] value String to parse
+ * @param[in] value_len Length of the @p str.
+ * @param[out] name_p Pointer to the beginning of the parsed name.
+ * @param[out] name_len_p Pointer to the length of the parsed name.
+ * @param[out] prefix_p Pointer to the beginning of the parsed prefix. If the member-name does not contain prefix, result is NULL.
+ * @param[out] prefix_len_p Pointer to the length of the parsed prefix. If the member-name does not contain prefix, result is 0.
+ * @param[out] is_attr_p Pointer to the metadata flag, set to 1 if the member-name contains \@, 0 otherwise.
+ */
+static void
+lydjson_parse_name(const char *value, size_t value_len, const char **name_p, size_t *name_len_p, const char **prefix_p, size_t *prefix_len_p, int *is_attr_p)
+{
+    const char *name, *prefix = NULL;
+    size_t name_len, prefix_len = 0;
+    int is_attr = 0;
+
+    name = memchr(value, ':', value_len);
+    if (name != NULL) {
+        prefix = value;
+        if (*prefix == '@') {
+            is_attr = 1;
+            prefix++;
+        }
+        prefix_len = name - prefix;
+        name++;
+        name_len = value_len - (prefix_len + 1) - is_attr;
+    } else {
+        name = value;
+        if (name[0] == '@') {
+            is_attr = 1;
+            name++;
+        }
+        name_len = value_len - is_attr;
     }
 
-    mod = ly_ctx_get_module_implemented(ctx, name);
-    free(name);
-    return mod;
+    *name_p = name;
+    *name_len_p = name_len;
+    *prefix_p = prefix;
+    *prefix_len_p = prefix_len;
+    *is_attr_p = is_attr;
+}
+
+/**
+ * @brief Get correct prefix (module_name) inside the @p node.
+ *
+ * @param[in] node Data node to get inherited prefix.
+ * @param[in] local_prefix Local prefix to replace the inherited prefix.
+ * @param[in] local_prefix_len Length of the @p local_prefix string. In case of 0, the inherited prefix is taken.
+ * @param[out] prefix_p Pointer to the resulting prefix string, Note that the result can be NULL in case of no local prefix
+ * and no context @p node to get inherited prefix.
+ * @param[out] prefix_len_p Pointer to the length of the resulting @p prefix_p string. Note that the result can be 0 in case
+ * of no local prefix and no context @p node to get inherited prefix.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydjson_get_node_prefix(struct lyd_node *node, const char *local_prefix, size_t local_prefix_len, const char **prefix_p, size_t *prefix_len_p)
+{
+    struct lyd_node_opaq *onode;
+    const char *module_name = NULL;
+
+    assert(prefix_p && prefix_len_p);
+
+    if (local_prefix_len) {
+        *prefix_p = local_prefix;
+        *prefix_len_p = local_prefix_len;
+        return LY_SUCCESS;
+    }
+
+    *prefix_p = NULL;
+    while (node) {
+        if (node->schema) {
+            *prefix_p = node->schema->module->name;
+            break;
+        }
+        onode = (struct lyd_node_opaq*)node;
+        if (onode->prefix.module_name) {
+            *prefix_p = onode->prefix.module_name;
+            break;
+        } else if (onode->prefix.id) {
+            *prefix_p = onode->prefix.id;
+            break;
+        }
+        node = (struct lyd_node*)node->parent;
+    }
+    *prefix_len_p = ly_strlen(module_name);
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @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 beeing processed, can be NULL in case of top-level. Also the opaq node with LYD_NODE_OPAQ_ISENVELOPE hint
+ * is accepted for searching top-level nodes.
+ * @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
+ */
+static LY_ERR
+lydjson_get_snode(const struct lyd_json_ctx *lydctx, int 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)
+{
+    struct lys_module *mod = NULL;
+
+    /* leave if-feature check for validation */
+    int getnext_opts = LYS_GETNEXT_NOSTATECHECK | (lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0);
+
+    /* init return value */
+    *snode_p = NULL;
+
+    /* get the element module */
+    if (prefix_len || (parent && !parent->schema && (((struct lyd_node_opaq*)parent)->hint & LYD_NODE_OPAQ_ISENVELOPE))) {
+        if (!prefix_len) {
+            /* opaq parent (envelope) - the second part of the condition */
+            lydjson_get_node_prefix((struct lyd_node*)parent, NULL, 0, &prefix, &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, LY_VLOG_LYD, parent, LYVE_SYNTAX_JSON, "Top-level JSON object member \"%.*s\" must be namespace-qualified.",
+               is_attr ? name_len + 1 : name_len, is_attr ? name - 1 : name);
+        return LY_EVALID;
+    }
+    if (!mod) {
+        if (lydctx->parse_options & LYD_PARSE_STRICT) {
+            LOGVAL(lydctx->jsonctx->ctx, LY_VLOG_LYD, parent, LYVE_REFERENCE, "No module named \"%.*s\" in the context.", prefix_len, prefix);
+            return LY_EVALID;
+        }
+        if (!(lydctx->parse_options & LYD_PARSE_OPAQ)) {
+            return LY_ENOT;
+        }
+    }
+
+    if (parent && !parent->schema && (((struct lyd_node_opaq*)parent)->hint & LYD_NODE_OPAQ_ISENVELOPE)) {
+        /* ignore the envelope parent when searchinf for the schema node */
+        parent = NULL;
+    }
+
+    /* get the schema node */
+    if (mod && (!parent || parent->schema)) {
+        *snode_p = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
+        if (!*snode_p) {
+            if (lydctx->parse_options & LYD_PARSE_STRICT) {
+                LOGVAL(lydctx->jsonctx->ctx, LY_VLOG_LYD, parent, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
+                    name_len, name, mod->name);
+                return LY_EVALID;
+            } else if (!(lydctx->parse_options & LYD_PARSE_OPAQ)) {
+                /* skip element with children */
+                return LY_ENOT;
+            }
+        } else {
+            /* check that schema node is valid and can be used */
+            LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode_p));
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Skip the currently open JSON object/array
+ * @param[in] jsonctx JSON context with the input data to skip.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydjson_data_skip(struct lyjson_ctx *jsonctx)
+{
+    enum LYJSON_PARSER_STATUS status, current;
+    size_t sublevels = 1;
+
+    status = lyjson_ctx_status(jsonctx, 0);
+
+    /* skip after the content */
+    do {
+        LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
+        if (current == status) {
+            sublevels++;
+        } else if (current == status + 1) {
+            sublevels--;
+        }
+    } while (current != status + 1 && sublevels);
+    /* open the next sibling */
+    LY_CHECK_RET(lyjson_ctx_next(jsonctx, NULL));
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Go through the @p value and find all possible prefixes and store them in @p val_prefs_p [sized array](@ref sizedarrays).
+ *
+ * @param[in] ctx libyang context
+ * @param[in] value Pointer to the beginning of the value to check.
+ * @param[in] value_len Length of the string to examine in @p value.
+ * @param[out] val_prefs_p Pointer to the resulting [sized array](@ref sizedarrays) of found prefixes. NULL in case there are no prefixes.
+ * @return LY_EMEM on memory allocation failure.
+ * @return LY_SUCCESS on success, empty @p val_prefs_p (NULL) is valid result if there are no possible prefixes
+ */
+static LY_ERR
+lydjson_get_value_prefixes(const struct ly_ctx *ctx, const char *value, size_t value_len, struct ly_prefix **val_prefs_p)
+{
+    LY_ERR ret;
+    LY_ARRAY_COUNT_TYPE u;
+    uint32_t c;
+    const char *start, *stop;
+    struct ly_prefix *prefixes = NULL;
+    size_t len;
+
+    for (stop = start = value; (size_t)(stop - value) < value_len; start = stop) {
+        size_t bytes;
+        ly_getutf8(&stop, &c, &bytes);
+        if (is_yangidentstartchar(c)) {
+            for (ly_getutf8(&stop, &c, &bytes);
+                    is_yangidentchar(c) && (size_t)(stop - value) < value_len;
+                    ly_getutf8(&stop, &c, &bytes));
+            stop = stop - bytes;
+            if (*stop == ':') {
+                /* we have a possible prefix */
+                struct ly_prefix *p = NULL;
+
+                len = stop - start;
+
+                /* check whether we do not already have this prefix stored */
+                LY_ARRAY_FOR(prefixes, u) {
+                    if (!ly_strncmp(prefixes[u].id, start, len)) {
+                        p = &prefixes[u];
+                        break;
+                    }
+                }
+                if (!p) {
+                    LY_ARRAY_NEW_GOTO(ctx, prefixes, p, ret, error);
+                    p->id = lydict_insert(ctx, start, len);
+                    p->module_name = lydict_insert(ctx, start, len);
+                } /* else the prefix already present */
+            }
+            stop = stop + bytes;
+        }
+    }
+
+    *val_prefs_p = prefixes;
+    return LY_SUCCESS;
+
+error:
+    LY_ARRAY_FOR(prefixes, u) {
+        lydict_remove(ctx, prefixes[u].id);
+        lydict_remove(ctx, prefixes[u].module_name);
+    }
+    LY_ARRAY_FREE(prefixes);
+    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.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @param[in] list List schema node corresponding to the input data object.
+ * @return LY_SUCCESS in case the data are ok for the @p list
+ * @return LY_ENOT in case the input data are not sufficient to fully parse the list instance.
+ */
+static LY_ERR
+lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list)
+{
+    LY_ERR ret = LY_SUCCESS;
+    enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx, 0);
+    struct ly_set key_set = {0};
+    const struct lysc_node *snode;
+    uint32_t i, status_count;
+
+    assert(list && (list->nodetype == LYS_LIST));
+    assert(status == LYJSON_OBJECT);
+
+    /* get all keys into a set (keys do not have if-features or anything) */
+    snode = NULL;
+    while ((snode = lys_getnext(snode, list, NULL, LYS_GETNEXT_NOSTATECHECK)) && (snode->flags & LYS_KEY)) {
+        ly_set_add(&key_set, (void *)snode, LY_SET_OPT_USEASLIST);
+    }
+
+    if (status != LYJSON_OBJECT_EMPTY) {
+        status_count = jsonctx->status.count;
+
+        while (key_set.count && status != LYJSON_OBJECT_CLOSED) {
+            const char *name, *prefix;
+            size_t name_len, prefix_len;
+            int is_attr;
+
+            /* match the key */
+            snode = NULL;
+            lydjson_parse_name(jsonctx->value, jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
+
+            if (!is_attr && !prefix) {
+                for (i = 0; i < key_set.count; ++i) {
+                    snode = (const struct lysc_node *)key_set.objs[i];
+                    if (!ly_strncmp(snode->name, name, name_len)) {
+                        break;
+                    }
+                }
+                /* go into the item to a) process it as a key or b) start skipping it as another list child */
+                ret = lyjson_ctx_next(jsonctx, &status);
+                LY_CHECK_GOTO(ret, cleanup);
+
+                if (snode) {
+                    /* we have the key, validate the value */
+                    if (status < LYJSON_NUMBER) {
+                        /* not a terminal */
+                        ret = LY_ENOT;
+                        goto cleanup;
+                    }
+
+                    ret = _lys_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, lydjson_resolve_prefix, jsonctx, LYD_JSON);
+                    LY_CHECK_GOTO(ret, cleanup);
+
+                    /* key with a valid value, remove from the set */
+                    ly_set_rm_index(&key_set, i, NULL);
+                }
+            } else {
+                /* start skipping the member we are not interested in */
+                ret = lyjson_ctx_next(jsonctx, &status);
+                LY_CHECK_GOTO(ret, cleanup);
+            }
+            /* move to the next child */
+            while (status_count < jsonctx->status.count) {
+                ret = lyjson_ctx_next(jsonctx, &status);
+                LY_CHECK_GOTO(ret, cleanup);
+            }
+        }
+    }
+
+    if (key_set.count) {
+        /* some keys are missing/did not validate */
+        ret = LY_ENOT;
+    }
+
+cleanup:
+    ly_set_erase(&key_set, NULL);
+    return ret;
+}
+
+/**
+ * @brief Get the hint for the data type parsers according to the current JSON parser context.
+ *
+ * @param[in] lydctx JSON data parser context. The context is supposed to be on a value.
+ * @param[in,out] status Pointer to the current context status,
+ * in some circumstances the function manipulates with the context so the status is updated.
+ * @param[out] type_hint_p Pointer to the variable to store the result.
+ * @return LY_SUCCESS in case of success.
+ * @return LY_EINVAL in case of invalid context status not referring to a value.
+ */
+static LY_ERR
+lydjson_value_type_hint(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status_p, int *type_hint_p)
+{
+    *type_hint_p = 0;
+
+    if (*status_p == LYJSON_ARRAY) {
+        /* only [null] */
+        LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_p));
+        LY_CHECK_RET(*status_p != LYJSON_NULL, LY_EINVAL);
+
+        LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, NULL));
+        LY_CHECK_RET(lyjson_ctx_status(lydctx->jsonctx, 0) != LYJSON_ARRAY_CLOSED, LY_EINVAL);
+
+        *type_hint_p = LYD_NODE_OPAQ_ISEMPTY;
+    } else if (*status_p == LYJSON_STRING) {
+        *type_hint_p = LYD_NODE_OPAQ_ISSTRING;
+    } else if (*status_p == LYJSON_NUMBER) {
+        *type_hint_p = LYD_NODE_OPAQ_ISNUMBER;
+    } else if (*status_p == LYJSON_FALSE || *status_p == LYJSON_TRUE) {
+        *type_hint_p = LYD_NODE_OPAQ_ISBOOLEAN;
+    } else if (*status_p == LYJSON_NULL) {
+        *type_hint_p = 0;
+    } else {
+        return LY_EINVAL;
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Check in advance if the input data are parsable according to the provided @p snode.
+ *
+ * Note that the checks are done only in case the LYD_PARSE_OPAQ is allowed. Otherwise the same checking
+ * is naturally done when the data are really parsed.
+ *
+ * @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[out] type_hint_p Pointer to a variable to store detected value type hint in case of leaf or leaf-list.
+ * @return LY_SUCCESS in case the data are ok for the @p snode or the LYD_PARSE_OPAQ is not enabled.
+ * @return LY_ENOT in case the input data are not sufficient to fully parse the list instance
+ * @return LY_EINVAL in case of invalid leaf JSON encoding
+ * and they are expected to be parsed as opaq nodes.
+ */
+static LY_ERR
+lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, int *type_hint_p)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyjson_ctx *jsonctx = lydctx->jsonctx;
+    enum LYJSON_PARSER_STATUS status;
+
+    assert(snode);
+    assert(snode->nodetype & (LYD_NODE_TERM | LYS_LIST));
+
+    if ((lydctx->parse_options & LYD_PARSE_OPAQ)) {
+        /* backup parser */
+        lyjson_ctx_backup(jsonctx);
+        status = lyjson_ctx_status(jsonctx, 0);
+
+        /* check if the node is parseable. if not, NULL the snode to announce that it is supposed to be parsed as an opaq node */
+        switch (snode->nodetype) {
+        case LYS_LEAFLIST:
+        case LYS_LEAF:
+            /* value may not be valid in which case we parse it as an opaque node */
+            ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
+            if (ret) {
+                break;
+            }
+
+            if (_lys_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, lydjson_resolve_prefix, jsonctx, LYD_JSON)) {
+                ret = LY_ENOT;
+            }
+            break;
+        case LYS_LIST:
+            /* lists may not have all its keys */
+            if (lydjson_check_list(jsonctx, snode)) {
+                /* invalid list, parse as opaque if it missing/has invalid some keys */
+                ret = LY_ENOT;
+            }
+        }
+
+        /* restore parser */
+        lyjson_ctx_restore(jsonctx);
+    } else if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+        status = lyjson_ctx_status(jsonctx, 0);
+        ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Join the forward-referencing metadata with their target data nodes.
+ *
+ * Note that JSON encoding for YANG data allows forward-referencing metadata only for leafs/leaf-lists.
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in,out] first_p Pointer to the first sibling node variable (top-level or in a particular parent node)
+ * as a starting point to search for the metadata's target data node
+ * @return LY_SUCCESS on success
+ * @return LY_EVALID in case there are some metadata with unresolved target data node instance
+ */
+static LY_ERR
+lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_node *node, *attr, *next, *start = *first_p, *meta_iter;
+    unsigned int instance = 0;
+    const char *prev = NULL;
+
+    /* finish linking metadata */
+    LY_LIST_FOR_SAFE(*first_p, next, attr) {
+        struct lyd_node_opaq *meta_container = (struct lyd_node_opaq*)attr;
+        unsigned int match = 0;
+        int is_attr;
+        const char *name, *prefix;
+        size_t name_len, prefix_len;
+        const struct lysc_node *snode;
+
+        if (attr->schema || meta_container->name[0] != '@') {
+            /* not an opaq metadata node */
+            continue;
+        }
+
+        if (prev != meta_container->name) {
+            /* metas' names are stored in dictionary, so checking pointers must works */
+            lydict_remove(lydctx->jsonctx->ctx, prev);
+            prev = lydict_insert(lydctx->jsonctx->ctx, meta_container->name, 0);
+            instance = 1;
+        } else {
+            instance++;
+        }
+
+        /* find the correspnding data node */
+        LY_LIST_FOR(start, node) {
+            if (!node->schema) {
+                /* opaq node - we are going to put into it just a generic attribute. */
+                if (strcmp(&meta_container->name[1], ((struct lyd_node_opaq*)node)->name)) {
+                    continue;
+                }
+
+                if (((struct lyd_node_opaq*)node)->hint & LYD_NODE_OPAQ_ISLIST) {
+                    LOGVAL(lydctx->jsonctx->ctx, LY_VLOG_LYD, node, LYVE_SYNTAX,
+                           "Metadata container references a sibling list node %s.", ((struct lyd_node_opaq*)node)->name);
+                    ret = LY_EVALID;
+                    goto cleanup;
+                }
+
+                /* match */
+                match++;
+                if (match != instance) {
+                    continue;
+                }
+
+                LY_LIST_FOR(meta_container->child, meta_iter) {
+                    /* convert opaq node to a attribute of the opaq node */
+                    struct lyd_node_opaq *meta = (struct lyd_node_opaq*)meta_iter;
+                    struct ly_prefix *val_prefs = NULL;
+                    int dynamic = 0;
+
+                    /* get value prefixes */
+                    LY_CHECK_GOTO(ret = lydjson_get_value_prefixes(lydctx->jsonctx->ctx, lydctx->jsonctx->value, lydctx->jsonctx->value_len, &val_prefs), cleanup);
+
+                    ret = lyd_create_attr(node, NULL, lydctx->jsonctx->ctx, meta->name, strlen(meta->name), meta->value, ly_strlen(meta->value),
+                                          &dynamic, meta->hint, LYD_JSON, val_prefs, meta->prefix.id, ly_strlen(meta->prefix.id),
+                                          meta->prefix.module_name, ly_strlen(meta->prefix.module_name));
+                    LY_CHECK_GOTO(ret, cleanup);
+                }
+
+                /* done */
+                break;
+            } else {
+                /* this is the second time we are resolving the schema node, so it must succeed,
+                 * but remember that snode can be still NULL */
+                lydjson_parse_name(meta_container->name, strlen(meta_container->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);
+                assert(ret == LY_SUCCESS);
+
+                if (snode != node->schema) {
+                    continue;
+                }
+
+                /* match */
+                match++;
+                if (match != instance) {
+                    continue;
+                }
+
+                LY_LIST_FOR(meta_container->child, meta_iter) {
+                    /* convert opaq node to a metadata of the node */
+                    struct lyd_node_opaq *meta = (struct lyd_node_opaq*)meta_iter;
+                    struct lys_module *mod = NULL;
+                    int dynamic = 0;
+
+                    mod = ly_ctx_get_module_implemented(lydctx->jsonctx->ctx, meta->prefix.id);
+                    if (mod) {
+                        ret = lyd_parser_create_meta((struct lyd_ctx*)lydctx, node, NULL, mod,
+                                                     meta->name, strlen(meta->name), meta->value, ly_strlen(meta->value),
+                                                     &dynamic, meta->hint, lydjson_resolve_prefix, NULL, LYD_JSON, snode);
+                        LY_CHECK_GOTO(ret, cleanup);
+                    } else if (lydctx->parse_options & LYD_PARSE_STRICT) {
+                        LOGVAL(lydctx->jsonctx->ctx, LY_VLOG_LYD, node, LYVE_REFERENCE,
+                               "Unknown (or not implemented) YANG module \"%s\" for metadata \"%s%s%s\".",
+                               meta->prefix.id, meta->prefix.id, ly_strlen(meta->prefix.id) ? ":" : "", meta->name);
+                        ret = LY_EVALID;
+                        goto cleanup;
+                    }
+                }
+                /* add/correct flags */
+                lyd_parse_set_data_flags(node, &lydctx->when_check, &node->meta, lydctx->parse_options);
+
+                /* done */
+                break;
+            }
+        }
+
+        if (match != instance) {
+            /* there is no corresponding data node for the metadata */
+            if (instance > 1) {
+                LOGVAL(lydctx->jsonctx->ctx, LY_VLOG_LYD, *first_p ? (*first_p)->parent : NULL, LYVE_REFERENCE,
+                       "Missing %d%s JSON data instance to be coupled with %s metadata.", instance,
+                       instance == 2 ? "nd" : (instance == 3 ? "rd" : "th"), meta_container->name);
+            } else {
+                LOGVAL(lydctx->jsonctx->ctx, LY_VLOG_LYD, *first_p ? (*first_p)->parent : NULL, LYVE_REFERENCE,
+                       "Missing JSON data instance to be coupled with %s metadata.", meta_container->name);
+            }
+            ret = LY_EVALID;
+        } else {
+            /* remove the opaq attr */
+            if (attr == (*first_p)) {
+                *first_p = attr->next;
+            }
+            lyd_free_tree(attr);
+        }
+    }
+
+cleanup:
+    lydict_remove(lydctx->jsonctx->ctx, prev);
+
+    return ret;
+}
+
+/**
+ * @brief Parse a metadata member.
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in] snode Schema node of the metadata parent.
+ * @param[in] node Parent node in case the metadata is not forward-referencing (only LYD_NODE_TERM)
+ * so the data node does not exists. In such a case the metadata is stored in the context for the later
+ * processing by lydjson_metadata_finish().
+ * @return LY_SUCCESS on success
+ * @return Various LY_ERR values in case of failure.
+ */
+static LY_ERR
+lydjson_metadata(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lyd_node *node)
+{
+    LY_ERR ret = LY_SUCCESS;
+    enum LYJSON_PARSER_STATUS status;
+    const char *expected;
+    int in_parent = 0;
+    const char *name, *prefix = NULL;
+    size_t name_len, prefix_len = 0;
+    struct lys_module *mod;
+    struct lyd_meta *meta = NULL;
+    const struct ly_ctx *ctx = lydctx->jsonctx->ctx;
+    int is_attr = 0;
+    struct lyd_node *prev = node;
+    int instance = 0;
+    uint16_t nodetype;
+
+    assert(snode || node);
+
+    nodetype = snode ? snode->nodetype : LYS_CONTAINER;
+
+    /* move to the second item in the name/X pair */
+    ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* check attribute encoding */
+    switch (nodetype) {
+    case LYS_LEAFLIST:
+        expected = "@name/array of objects/nulls";
+
+        LY_CHECK_GOTO(status != LYJSON_ARRAY, representation_error);
+
+next_entry:
+        instance++;
+
+        /* move into array / next entry */
+        ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        if (status == LYJSON_ARRAY_CLOSED) {
+            /* we are done, move after the array */
+            ret = lyjson_ctx_next(lydctx->jsonctx, NULL);
+            goto cleanup;
+        }
+        LY_CHECK_GOTO(status != LYJSON_OBJECT && status != LYJSON_NULL, representation_error);
+
+        if (!node || node->schema != prev->schema) {
+            LOGVAL(lydctx->jsonctx->ctx, LY_VLOG_LYD, prev->parent, LYVE_REFERENCE,
+                   "Missing JSON data instance no. %d of %s:%s to be coupled with metadata.",
+                   instance, prev->schema->module->name, prev->schema->name);
+            ret = LY_EVALID;
+            goto cleanup;
+        }
+
+        if (status == LYJSON_NULL) {
+            /* continue with the next entry in the leaf-list array */
+            prev = node;
+            node = node->next;
+            goto next_entry;
+        }
+        break;
+    case LYS_LEAF:
+    case LYS_ANYXML:
+        expected = "@name/object";
+
+        LY_CHECK_GOTO(status != LYJSON_OBJECT && (nodetype != LYS_LEAFLIST || status != LYJSON_NULL), representation_error);
+        break;
+    case LYS_CONTAINER:
+    case LYS_LIST:
+    case LYS_ANYDATA:
+    case LYS_NOTIF:
+    case LYS_ACTION:
+    case LYS_RPC:
+        in_parent = 1;
+        expected = "@/object";
+        LY_CHECK_GOTO(status != LYJSON_OBJECT, representation_error);
+        break;
+    }
+
+    /* process all the members inside a single metadata object */
+    assert(status == LYJSON_OBJECT);
+
+    while (status != LYJSON_OBJECT_CLOSED) {
+        lydjson_parse_name(lydctx->jsonctx->value, lydctx->jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
+        if (!prefix) {
+            LOGVAL(ctx, LY_VLOG_LYD, (void*)node, LYVE_SYNTAX_JSON,
+                   "Metadata in JSON must be namespace-qualified, missing prefix for \"%.*s\".",
+                   lydctx->jsonctx->value_len, lydctx->jsonctx->value);
+            ret = LY_EVALID;
+            goto cleanup;
+        } else if (is_attr) {
+            LOGVAL(ctx, LY_VLOG_LYD, (void*)node, LYVE_SYNTAX_JSON,
+                   "Invalid format of the Metadata identifier in JSON, unexpected '@' in \"%.*s\"",
+                   lydctx->jsonctx->value_len, lydctx->jsonctx->value);
+            ret = LY_EVALID;
+            goto cleanup;
+        }
+
+        /* get the element module */
+        mod = ly_ctx_get_module_implemented2(ctx, prefix, prefix_len);
+        if (!mod) {
+            if (lydctx->parse_options & LYD_PARSE_STRICT) {
+                LOGVAL(ctx, LY_VLOG_LYD, (void*)node, LYVE_REFERENCE,
+                       "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.",
+                       prefix_len, prefix, name_len, name);
+                ret = LY_EVALID;
+                goto cleanup;
+            }
+            if (!(lydctx->parse_options & LYD_PARSE_OPAQ)) {
+                /* skip element with children */
+                ret = lydjson_data_skip(lydctx->jsonctx);
+                LY_CHECK_GOTO(ret, cleanup);
+                status = lyjson_ctx_status(lydctx->jsonctx, 0);
+                /* end of the item */
+                continue;
+            }
+        }
+
+        /* get the value */
+        ret = lyjson_ctx_next(lydctx->jsonctx, NULL);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        if (node->schema) {
+            /* create metadata */
+            meta = NULL;
+            ret = lyd_parser_create_meta((struct lyd_ctx*)lydctx, node, &meta, mod, name, name_len, lydctx->jsonctx->value, lydctx->jsonctx->value_len,
+                                         &lydctx->jsonctx->dynamic, 0, lydjson_resolve_prefix, lydctx->jsonctx, LYD_JSON, snode);
+            LY_CHECK_GOTO(ret, cleanup);
+
+            /* add/correct flags */
+            lyd_parse_set_data_flags(node, &lydctx->when_check, &meta, lydctx->parse_options);
+        } else {
+            /* create attribute */
+            struct ly_prefix *val_prefs = NULL;
+            const char *module_name;
+            size_t module_name_len;
+
+            lydjson_get_node_prefix(node, prefix, prefix_len, &module_name, &module_name_len);
+
+            /* get value prefixes */
+            LY_CHECK_GOTO(ret = lydjson_get_value_prefixes(lydctx->jsonctx->ctx, lydctx->jsonctx->value, lydctx->jsonctx->value_len, &val_prefs), cleanup);
+
+            /* attr2 is always changed to the created attribute */
+            ret = lyd_create_attr(node, NULL, lydctx->jsonctx->ctx, name, name_len, lydctx->jsonctx->value, lydctx->jsonctx->value_len,
+                                  &lydctx->jsonctx->dynamic, 0, LYD_JSON, val_prefs, prefix, prefix_len, module_name, module_name_len);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+        /* next member */
+        ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    if (nodetype == LYS_LEAFLIST) {
+        /* continue by processing another metadata object for the following
+         * leaf-list instance since they are allways instantiated in JSON array */
+        prev = node;
+        node = node->next;
+        goto next_entry;
+    }
+
+    /* move after the metadata */
+    ret = lyjson_ctx_next(lydctx->jsonctx, NULL);
+    LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+    return ret;
+
+representation_error:
+    LOGVAL(ctx, LY_VLOG_LYD, (void*)node, LYVE_SYNTAX_JSON,
+           "The attribute(s) of %s \"%s\" is expected to be represented as JSON %s, but input data contains @%s/%s.",
+           lys_nodetype2str(nodetype), node->schema ? node->schema->name : ((struct lyd_node_opaq*)node)->name,
+           expected, lyjson_token2str(status), in_parent ? "" : "name");
+
+    return LY_EVALID;
+}
+
+/**
+ * @brief Eat the node pointed by @p node_p by inserting it into @p parent and maintain the @p first_p pointing to the first child node.
+ *
+ * @param[in] parent Parent node to insert to, can be NULL in case of top-level (or provided first_p).
+ * @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.
+ */
+static void
+lydjson_maintain_children(struct lyd_node_inner *parent, struct lyd_node **first_p, struct lyd_node **node_p)
+{
+    if (*node_p) {
+        /* insert, keep first pointer correct */
+        lyd_insert_node((struct lyd_node *)parent, first_p, *node_p);
+        if (first_p) {
+            if (parent) {
+                *first_p = parent->child;
+            } else {
+                while ((*first_p)->prev->next) {
+                    *first_p = (*first_p)->prev;
+                }
+            }
+        }
+        *node_p = NULL;
+    }
+}
+
+static LY_ERR lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node_inner *parent, struct lyd_node **first_p);
+
+/**
+ * @brief Parse opaq node from the input.
+ *
+ * In case of processing array, the whole array is being processed and the resulting @p node_p is the last item of the array.
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in] name Name of the opaq node to create.
+ * @param[in] name_len Length of the @p name string.
+ * @param[in] prefix Prefix of the opaq node to create.
+ * @param[in] prefix_len Length of the @p prefx string.
+ * @param[in] parent Data parent of the opaq node to create, can be NULL in case of top level,
+ * but must be set if @p first is not.
+ * @param[in,out] status_p Pointer to the current status of the parser context,
+ * since the function manipulates with the context and process the input, the status can be updated.
+ * @param[in,out] status_inner_p In case of processing JSON array, this parameter points to a standalone
+ * context status of the array content. Otherwise, it is supposed to be the same as @p status_p.
+ * @param[in,out] first_p First top-level/parent sibling, must be set if @p parent is not.
+ * @param[out] node_p Pointer to the created opaq node.
+ * @return LY_ERR value.
+ */
+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)
+{
+    LY_ERR ret = LY_SUCCESS;
+    const char *value = NULL, *module_name;
+    size_t value_len = 0, module_name_len = 0;
+    struct ly_prefix *val_prefs = NULL;
+    int dynamic = 0;
+    int type_hint = 0;
+
+    if (*status_inner_p != LYJSON_OBJECT && *status_inner_p != LYJSON_OBJECT_EMPTY) {
+        /* prepare for creating opaq node with a value */
+        value = lydctx->jsonctx->value;
+        value_len = lydctx->jsonctx->value_len;
+        dynamic = lydctx->jsonctx->dynamic;
+        lydctx->jsonctx->dynamic = 0;
+
+        LY_CHECK_RET(lydjson_value_type_hint(lydctx, status_inner_p, &type_hint));
+
+        if (value) {
+            /* get value prefixes */
+            LY_CHECK_RET(lydjson_get_value_prefixes(lydctx->jsonctx->ctx, value, value_len, &val_prefs));
+        }
+    }
+
+    /* create node */
+    lydjson_get_node_prefix((struct lyd_node*)parent, prefix, prefix_len, &module_name, &module_name_len);
+    ret = lyd_create_opaq(lydctx->jsonctx->ctx, name, name_len, value, value_len, &dynamic, type_hint,
+                          LYD_JSON, val_prefs, prefix, prefix_len, module_name, module_name_len, node_p);
+    if (dynamic) {
+        free((char*)value);
+        dynamic = 0;
+    }
+    value = NULL;
+    value_len = 0;
+    val_prefs = NULL;
+    LY_CHECK_RET(ret);
+
+    if (*status_p == LYJSON_OBJECT || *status_p == LYJSON_OBJECT_EMPTY) {
+        /* process children */
+        while (*status_p != LYJSON_OBJECT_CLOSED && *status_p != LYJSON_OBJECT_EMPTY) {
+            LY_CHECK_RET(lydjson_subtree_r(lydctx, (struct lyd_node_inner *)(*node_p), lyd_node_children_p(*node_p)));
+            *status_p = lyjson_ctx_status(lydctx->jsonctx, 0);
+        }
+    } else if (*status_p == LYJSON_ARRAY || *status_p == LYJSON_ARRAY_EMPTY) {
+        /* process another instance of the same node */
+        /* but first mark the node to be expected a list or a leaf-list */
+        ((struct lyd_node_opaq*)*node_p)->hint |= LYD_NODE_OPAQ_ISLIST;
+
+        if (*status_inner_p == LYJSON_OBJECT || *status_inner_p == LYJSON_OBJECT_EMPTY) {
+            /* but first process children of the object in the array */
+            while (*status_inner_p != LYJSON_OBJECT_CLOSED && *status_inner_p != LYJSON_OBJECT_EMPTY) {
+                LY_CHECK_RET(lydjson_subtree_r(lydctx, (struct lyd_node_inner *)(*node_p), lyd_node_children_p(*node_p)));
+                *status_inner_p = lyjson_ctx_status(lydctx->jsonctx, 0);
+            }
+        }
+
+        LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_inner_p));
+
+        /* continue with the next instance */
+        if (*status_inner_p != LYJSON_ARRAY_CLOSED) {
+            assert(node_p);
+            lydjson_maintain_children(parent, first_p, node_p);
+            return lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_p, status_inner_p, first_p, node_p);
+        }
+    }
+
+    /* finish linking metadata */
+    LY_CHECK_RET(lydjson_metadata_finish(lydctx, lyd_node_children_p(*node_p)));
+
+    /* move after the item */
+    return lyjson_ctx_next(lydctx->jsonctx, status_p);
+}
+
+/**
+ * @brief Process the attribute container (starting by @)
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @param[in] attr_node The data node referenced by the attribute container, if already known.
+ * @param[in] snode The schema node of the data node referenced by the attribute container, if known.
+ * @param[in] name Name of the opaq node to create.
+ * @param[in] name_len Length of the @p name string.
+ * @param[in] prefix Prefix of the opaq node to create.
+ * @param[in] prefix_len Length of the @p prefx string.
+ * @param[in] parent Data parent of the opaq node to create, can be NULL in case of top level,
+ * but must be set if @p first is not.
+ * @param[in,out] status_p Pointer to the current status of the parser context,
+ * since the function manipulates with the context and process the input, the status can be updated.
+ * @param[in,out] first_p First top-level/parent sibling, must be set if @p parent is not.
+ * @param[out] node_p Pointer to the created opaq node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydjson_parse_attribute(struct lyd_json_ctx *lydctx, struct lyd_node *attr_node, const struct lysc_node *snode,
+                        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,
+                        struct lyd_node **first_p, struct lyd_node **node_p)
+{
+    LY_ERR ret = LY_SUCCESS;
+    enum LYJSON_PARSER_STATUS status_inner;
+
+    /* parse as an attribute to a node */
+    if (!attr_node && snode) {
+        /* try to find the instance */
+        for (struct lyd_node *iter = *first_p; iter; iter = iter->next) {
+            if (iter->schema == snode) {
+                attr_node = iter;
+                break;
+            }
+        }
+    }
+    if (!attr_node) {
+        /* parse just as an opaq node with the name beginning with @,
+         * later we have to check that it belongs to a standard node
+         * and it is supposed to be converted to a metadata */
+        uint32_t prev_opts;
+
+        /* move into metadata */
+        LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_p));
+
+        if (*status_p == LYJSON_ARRAY) {
+            /* move into the array */
+            LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, &status_inner));
+        } else {
+            /* just a flag to pass correct parameters into lydjson_parse_opaq() */
+            status_inner = LYJSON_ERROR;
+        }
+
+        /* backup parser options to parse unknown metadata as opaq nodes and try to resolve them later */
+        prev_opts = lydctx->parse_options;
+        lydctx->parse_options &= ~LYD_PARSE_STRICT;
+        lydctx->parse_options |= LYD_PARSE_OPAQ;
+
+        ret = lydjson_parse_opaq(lydctx, prefix ? prefix - 1 : name - 1, prefix ? prefix_len + name_len + 2 : name_len + 1,
+                                 NULL, 0, parent, status_p, status_inner == LYJSON_ERROR ? status_p : &status_inner, first_p, node_p);
+
+        /* restore the parser options */
+        lydctx->parse_options = prev_opts;
+    } else {
+        ret = lydjson_metadata(lydctx, snode, attr_node);
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Parse JSON subtree.
+ *
+ * @param[in] lydctx JSON data parser context.
+ * @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.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node_inner *parent, struct lyd_node **first_p)
+{
+    LY_ERR ret = LY_SUCCESS;
+    enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(lydctx->jsonctx, 0);
+    enum LYJSON_PARSER_STATUS status_inner = 0;
+    const char *name, *prefix = NULL;
+    size_t name_len, prefix_len = 0;
+    int is_attr = 0;
+    const struct lysc_node *snode, *snode_backup = NULL;
+    struct lyd_node *node = NULL, *attr_node = NULL, *anchor = NULL;
+    const struct ly_ctx *ctx = lydctx->jsonctx->ctx;
+    const char *expected = NULL;
+    int type_hint = 0;
+    uint32_t prev_opts;
+
+    assert(parent || first_p);
+    assert(status == LYJSON_OBJECT);
+
+    /* process the node name */
+    lydjson_parse_name(lydctx->jsonctx->value, lydctx->jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
+
+    if (is_attr && !name_len && !prefix_len) {
+        /* parent's attribute without a name - skip schema node detection and get the schema from the parent */
+        if (!parent) {
+            LOGVAL(ctx, LY_VLOG_LYD, NULL, LYVE_SYNTAX_JSON, "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries.");
+            ret = LY_EVALID;
+            goto cleanup;
+        }
+        attr_node = (struct lyd_node*)parent;
+        snode = parent->schema;
+        goto attribute;
+    }
+
+    /* get the schema node */
+    ret = lydjson_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, 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() */
+        goto cleanup;
+    }
+    LY_CHECK_GOTO(ret, cleanup);
+
+    if (!snode) {
+        /* parse as an opaq node */
+        assert((lydctx->parse_options & LYD_PARSE_OPAQ) || (lydctx->int_opts));
+
+        /* move to the second item in the name/X pair */
+        ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        if (status == LYJSON_ARRAY) {
+            /* move into the array */
+            ret = lyjson_ctx_next(lydctx->jsonctx, &status_inner);
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            /* just a flag to pass correct parameters into lydjson_parse_opaq() */
+            status_inner = LYJSON_ERROR;
+        }
+
+        ret = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len,
+                                 parent, &status, status_inner == LYJSON_ERROR ? &status : &status_inner, first_p, &node);
+        LY_CHECK_GOTO(ret, cleanup);
+        is_attr = 1;
+    } else if (!is_attr) {
+        /* parse as a standard lyd_node */
+
+        /* move to the second item in the name/X pair */
+        ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* first check the expected representation according to the nodetype and then continue with the content */
+        switch (snode->nodetype) {
+        case LYS_LEAFLIST:
+            expected = "name/array of values";
+
+            LY_CHECK_GOTO(status != LYJSON_ARRAY, representation_error);
+
+            /* move into array */
+            ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+            LY_CHECK_GOTO(ret, cleanup);
+
+            /* process the value */
+            goto next_term;
+        case LYS_LEAF:
+            if (status == LYJSON_ARRAY) {
+                expected = "name/[null]";
+            } else {
+                expected = "name/value";
+            }
+
+next_term:
+            ret = lydjson_data_check_opaq(lydctx, snode, &type_hint);
+            if (ret == LY_SUCCESS) {
+                /* create terminal node */
+                ret = lyd_parser_create_term((struct lyd_ctx*)lydctx, snode, lydctx->jsonctx->value, lydctx->jsonctx->value_len,
+                                             &lydctx->jsonctx->dynamic, type_hint, lydjson_resolve_prefix, lydctx->jsonctx, LYD_JSON, &node);
+                LY_CHECK_GOTO(ret, cleanup);
+
+                ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+                LY_CHECK_GOTO(ret, cleanup);
+            } 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);
+                LY_CHECK_GOTO(ret, cleanup);
+
+                if (snode->nodetype == LYS_LEAFLIST) {
+                    ((struct lyd_node_opaq*)node)->hint |= LYD_NODE_OPAQ_ISLIST;
+                }
+            } else if (ret == LY_EINVAL) {
+                goto representation_error;
+            } else {
+                goto cleanup;
+            }
+
+            if (snode->nodetype == LYS_LEAFLIST) {
+                /* continue with the next instance of the leaf-list */
+                if(status != LYJSON_ARRAY_CLOSED) {
+                    assert(node);
+                    lydjson_maintain_children(parent, first_p, &node);
+                    goto next_term;
+                }
+
+                /* move after the array */
+                ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+                LY_CHECK_GOTO(ret, cleanup);
+            }
+
+            break;
+        case LYS_LIST:
+            expected = "name/array of objects";
+
+            LY_CHECK_GOTO(status != LYJSON_ARRAY, representation_error);
+
+            /* move into array */
+            ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+            LY_CHECK_GOTO(ret, cleanup);
+
+next_list:
+            if (lydjson_data_check_opaq(lydctx, snode, &type_hint)) {
+                snode_backup = snode;
+                snode = NULL;
+                /* parse it again as an opaq node */
+                ret = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent,
+                                         &status, &status, first_p, &node);
+                LY_CHECK_GOTO(ret, cleanup);
+                ((struct lyd_node_opaq*)node)->hint |= LYD_NODE_OPAQ_ISLIST;
+
+                snode = snode_backup;
+                snode_backup = NULL;
+                goto list_loop;
+            }
+            /* process children - no break */
+        case LYS_CONTAINER:
+        case LYS_NOTIF:
+        case LYS_ACTION:
+        case LYS_RPC:
+            if (snode->nodetype != LYS_LIST) {
+                expected = "name/object";
+            }
+
+            LY_CHECK_GOTO(status != LYJSON_OBJECT && status != LYJSON_OBJECT_EMPTY, representation_error);
+
+            /* create inner node */
+            ret = lyd_create_inner(snode, &node);
+            LY_CHECK_GOTO(ret, cleanup);
+
+            /* process children */
+            while (status != LYJSON_OBJECT_CLOSED && status != LYJSON_OBJECT_EMPTY) {
+                ret = lydjson_subtree_r(lydctx, (struct lyd_node_inner *)node, lyd_node_children_p(node));
+                LY_CHECK_GOTO(ret, cleanup);
+                status = lyjson_ctx_status(lydctx->jsonctx, 0);
+            }
+
+            /* finish linking metadata */
+            ret = lydjson_metadata_finish(lydctx, lyd_node_children_p(node));
+            LY_CHECK_GOTO(ret, cleanup);
+
+            if (snode->nodetype == LYS_LIST) {
+                /* check all keys exist */
+                ret = lyd_parse_check_keys(node);
+                LY_CHECK_GOTO(ret, cleanup);
+            }
+
+            if (!(lydctx->parse_options & LYD_PARSE_ONLY)) {
+                /* new node validation, autodelete CANNOT occur, all nodes are new */
+                ret = lyd_validate_new(lyd_node_children_p(node), snode, NULL, NULL);
+                LY_CHECK_GOTO(ret, cleanup);
+
+                /* add any missing default children */
+                ret = lyd_new_implicit_r(node, lyd_node_children_p(node), NULL, NULL, &lydctx->unres_node_type, &lydctx->when_check,
+                                         (lydctx->validate_options & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+                LY_CHECK_GOTO(ret, cleanup);
+            }
+
+            ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+            LY_CHECK_GOTO(ret, cleanup);
+            if (snode->nodetype == LYS_LIST) {
+list_loop:
+                /* continue with the next instance of the list */
+                if(status != LYJSON_ARRAY_CLOSED) {
+                    assert(node);
+                    lydjson_maintain_children(parent, first_p, &node);
+                    goto next_list;
+                }
+
+                /* move after the array */
+                ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+                LY_CHECK_GOTO(ret, cleanup);
+
+            } else if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+                /* rememeber the RPC/action/notification */
+                lydctx->op_node = node;
+            }
+
+            break;
+        case LYS_ANYDATA:
+        case LYS_ANYXML:
+            expected = "name/object";
+            LY_CHECK_GOTO(status != LYJSON_OBJECT && status != LYJSON_OBJECT_EMPTY, representation_error);
+
+            /* parse any data tree with correct options */
+            /* first backup the current options and then make the parser to process data as opaq nodes */
+            prev_opts = lydctx->parse_options;
+            lydctx->parse_options &= ~LYD_PARSE_STRICT;
+            lydctx->parse_options |= LYD_PARSE_OPAQ;
+
+            /* process the anydata content */
+            while (status != LYJSON_OBJECT_CLOSED && status != LYJSON_OBJECT_EMPTY) {
+                ret = lydjson_subtree_r(lydctx, NULL, &anchor);
+                LY_CHECK_GOTO(ret, cleanup);
+                status = lyjson_ctx_status(lydctx->jsonctx, 0);
+            }
+
+            /* reset parser options */
+            lydctx->parse_options = prev_opts;
+            LY_CHECK_GOTO(ret, cleanup);
+
+            /* finish linking metadata */
+            ret = lydjson_metadata_finish(lydctx, &anchor);
+            LY_CHECK_GOTO(ret, cleanup);
+
+            /* create any node */
+            ret = lyd_create_any(snode, anchor, LYD_ANYDATA_DATATREE, &node);
+            LY_CHECK_GOTO(ret, cleanup);
+
+            break;
+        }
+
+    } else {
+attribute:
+        ret = lydjson_parse_attribute(lydctx, attr_node, snode, name, name_len, prefix, prefix_len, parent, &status, first_p, &node);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    lydjson_maintain_children(parent, first_p, &node);
+
+cleanup:
+    lyd_free_tree(node);
+    return ret;
+
+representation_error:
+    LOGVAL(ctx, LY_VLOG_LYD, parent, LYVE_SYNTAX_JSON,
+           "The %s \"%s\" is expected to be represented as JSON %s, but input data contains name/%s.",
+           lys_nodetype2str(snode->nodetype), snode->name, expected, lyjson_token2str(status));
+
+    ret = LY_EVALID;
+    goto cleanup;
+}
+
+/**
+ * @brief Common start of JSON parser processing different types of the input data.
+ *
+ * @param[in] ctx libyang context
+ * @param[in] in Input structure.
+ * @param[in] parse_options Options for parser, see @ref dataparseroptions.
+ * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
+ * @param[out] lydctx_p Data parser context to finish validation.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, int parse_options, int validate_options, struct lyd_json_ctx **lydctx_p)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_json_ctx *lydctx;
+    size_t i, line = 1;
+
+    /* starting top-level */
+    for (i = 0; in->current[i] != '\0' && is_jsonws(in->current[i]); i++) {
+        if (in->current[i] == 0x0a) { /* new line */
+            line++;
+        };
+    }
+    if (in->current[i] == '\0') {
+        /* empty data input */
+        return LY_SUCCESS;
+    }
+    if (in->current[i] != '{') {
+        LOGVAL(ctx, LY_VLOG_LINE, &line, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(&in->current[i]), &in->current[i],
+               "a top-level JSON object ('{') holding encoded YANG data");
+        return LY_EVALID;
+    }
+
+    /* init context */
+    lydctx = calloc(1, sizeof *lydctx);
+    LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM);
+    lydctx->parse_options = parse_options;
+    lydctx->validate_options = validate_options;
+    lydctx->free = lyd_json_ctx_free;
+    lydctx->resolve_prefix = lydjson_resolve_prefix;
+
+    LY_CHECK_ERR_RET(ret = lyjson_ctx_new(ctx, in, &lydctx->jsonctx), free(lydctx), ret);
+
+    *lydctx_p = lydctx;
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_parse_json_data(const struct ly_ctx *ctx, struct ly_in *in, int parse_options, int validate_options, struct lyd_node **tree_p, struct lyd_ctx **lydctx_p)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_json_ctx *lydctx = NULL;
+    enum LYJSON_PARSER_STATUS status;
+
+    assert(tree_p);
+    *tree_p = NULL;
+
+    ret = lyd_parse_json_init(ctx, in, parse_options, validate_options, &lydctx);
+    LY_CHECK_GOTO(ret || !lydctx, cleanup);
+
+    status = lyjson_ctx_status(lydctx->jsonctx, 0);
+    assert(status == LYJSON_OBJECT);
+
+    /* read subtree(s) */
+    while (lydctx->jsonctx->in->current[0] && status != LYJSON_OBJECT_CLOSED) {
+        ret = lydjson_subtree_r(lydctx, NULL, tree_p);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        status = lyjson_ctx_status(lydctx->jsonctx, 0);
+    }
+
+    /* finish linking metadata */
+    ret = lydjson_metadata_finish(lydctx, tree_p);
+    LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+    /* there should be no unresolved types stored */
+    assert(!(parse_options & LYD_PARSE_ONLY) || (!lydctx->unres_node_type.count && !lydctx->unres_meta_type.count
+           && !lydctx->when_check.count));
+
+    if (ret) {
+        lyd_json_ctx_free((struct lyd_ctx *)lydctx);
+        lyd_free_all(*tree_p);
+        *tree_p = NULL;
+    } else {
+        *lydctx_p = (struct lyd_ctx *)lydctx;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Parse optional JSON envelope around the Notification data, including the eventTime data.
+ *
+ * @param[in] jsonctx JSON parser context
+ * @param[out] envp_p Pointer to the created envelope opaq container.
+ * @return LY_SUCCESS if the envelope present and successfully parsed.
+ * @return LY_ENOT in case there is not the expected envelope.
+ * @return LY_ERR in case of parsing failure.
+ */
+static LY_ERR
+lydjson_notif_envelope(struct lyjson_ctx *jsonctx, struct lyd_node **envp_p)
+{
+    LY_ERR ret = LY_ENOT, r;
+    const char *name, *prefix, *value = NULL;
+    size_t name_len, prefix_len, value_len;
+    int is_attr, dynamic = 0;
+    enum LYJSON_PARSER_STATUS status;
+    struct lyd_node *et;
+
+    *envp_p = NULL;
+
+    /* backup the context */
+    lyjson_ctx_backup(jsonctx);
+
+    /* "notification" envelope */
+    lydjson_parse_name(jsonctx->value, jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
+    LY_CHECK_GOTO(is_attr, cleanup);
+    LY_CHECK_GOTO(prefix_len != 13 || strncmp(prefix, "ietf-restconf", 13), cleanup);
+    LY_CHECK_GOTO(name_len != 12 || strncmp(name, "notification", name_len), cleanup);
+
+    r = lyjson_ctx_next(jsonctx, &status);
+    LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+    LY_CHECK_GOTO(status != LYJSON_OBJECT, cleanup);
+
+    /* "eventTime" child */
+    lydjson_parse_name(jsonctx->value, jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
+    LY_CHECK_GOTO(prefix_len || is_attr, cleanup);
+    LY_CHECK_GOTO(name_len != 9 || strncmp(name, "eventTime", name_len), cleanup);
+
+    /* go for the data */
+    r = lyjson_ctx_next(jsonctx, &status);
+    LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+    LY_CHECK_GOTO(status != LYJSON_STRING, cleanup);
+
+    value = jsonctx->value;
+    value_len = jsonctx->value_len;
+    dynamic = jsonctx->dynamic;
+    jsonctx->dynamic = 0;
+
+    r = lyjson_ctx_next(jsonctx, &status);
+    LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+    LY_CHECK_GOTO(status != LYJSON_OBJECT, cleanup);
+    /* now the notificationContent is expected, which will be parsed by the caller */
+
+    /* create notification envelope */
+    ret = lyd_create_opaq(jsonctx->ctx, "notification", 12, "", 0, NULL, LYD_NODE_OPAQ_ISENVELOPE,
+                          LYD_JSON, NULL, NULL, 0, "ietf-restconf", 13, envp_p);
+    LY_CHECK_GOTO(ret, cleanup);
+    /* create notification envelope */
+    ret = lyd_create_opaq(jsonctx->ctx, "eventTime", 9, value, value_len, &dynamic, LYD_NODE_OPAQ_ISSTRING,
+                          LYD_JSON, NULL, NULL, 0, "ietf-restconf", 13, &et);
+    LY_CHECK_GOTO(ret, cleanup);
+    /* insert eventTime into notification */
+    lyd_insert_node(*envp_p, NULL, et);
+
+    ret = LY_SUCCESS;
+cleanup:
+    if (ret) {
+        /* restore the context */
+        lyjson_ctx_restore(jsonctx);
+        if (dynamic) {
+            free((char*)value);
+        }
+    }
+    return ret;
+}
+
+LY_ERR
+lyd_parse_json_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **ntf_p)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_json_ctx *lydctx = NULL;
+    struct lyd_node *ntf_e = NULL;
+    struct lyd_node *tree = NULL;
+    enum LYJSON_PARSER_STATUS status;
+
+    /* init */
+    ret = lyd_parse_json_init(ctx, in, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &lydctx);
+    LY_CHECK_GOTO(ret || !lydctx, cleanup);
+    lydctx->int_opts = LYD_INTOPT_NOTIF;
+
+    status = lyjson_ctx_status(lydctx->jsonctx, 0);
+    assert(status == LYJSON_OBJECT);
+
+    /* parse "notification" and "eventTime", if present */
+    ret = lydjson_notif_envelope(lydctx->jsonctx, &ntf_e);
+    if (ret == LY_ENOT) {
+        ret = LY_SUCCESS;
+    } else if (ret) {
+        goto cleanup;
+    }
+
+    /* read subtree(s) */
+    while (lydctx->jsonctx->in->current[0] && status != LYJSON_OBJECT_CLOSED) {
+        ret = lydjson_subtree_r(lydctx, NULL, &tree);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        status = lyjson_ctx_status(lydctx->jsonctx, 0);
+    }
+
+    /* finish linking metadata */
+    ret = lydjson_metadata_finish(lydctx, &tree);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* make sure we have parsed some notification */
+    if (!lydctx->op_node) {
+        LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_DATA, "Missing the \"notification\" node.");
+        ret = LY_EVALID;
+        goto cleanup;
+    }
+
+    if (ntf_e) {
+        /* finish notification envelope */
+        ret = lyjson_ctx_next(lydctx->jsonctx, &status);
+        LY_CHECK_GOTO(ret, cleanup);
+        if (status == LYJSON_END) {
+            LOGVAL(ctx, LY_VLOG_LINE, &lydctx->jsonctx->line, LY_VCODE_EOF);
+            ret = LY_EVALID;
+            goto cleanup;
+        } else if (status != LYJSON_OBJECT_CLOSED) {
+            LOGVAL(ctx, LY_VLOG_LINE, &lydctx->jsonctx->line, LYVE_SYNTAX, "Unexpected sibling member \"%.*s\" of \"notification\".",
+                   lydctx->jsonctx->value_len, lydctx->jsonctx->value);
+            ret = LY_EVALID;
+            goto cleanup;
+        }
+    }
+
+    if (ntf_p) {
+        *ntf_p = lydctx->op_node;
+    }
+    assert(tree);
+    if (tree_p) {
+        *tree_p = tree;
+    }
+    if (ntf_e) {
+        /* connect to the notification */
+        lyd_insert_node(ntf_e, NULL, *tree_p);
+        if (tree_p) {
+            *tree_p = ntf_e;
+        }
+    }
+
+cleanup:
+    /* we have used parse_only flag */
+    assert(!lydctx || (!lydctx->unres_node_type.count && !lydctx->unres_meta_type.count && !lydctx->when_check.count));
+
+    lyd_json_ctx_free((struct lyd_ctx *)lydctx);
+    if (ret) {
+        lyd_free_all(tree);
+        lyd_free_tree(ntf_e);
+    }
+    return ret;
+}
+
+/**
+ * @brief Parse optional JSON envelope around the processed content.
+ *
+ * @param[in] jsonctx JSON parser context
+ * @param[in] parent Parent node (some other envelope).
+ * @param[in] module_key Name of the module where the envelope element is expected.
+ * @param[in] object_id Name of the envelope object.
+ * @param[out] envp_p Pointer to the created envelope opaq container.
+ * @return LY_SUCCESS if the envelope present and successfully parsed.
+ * @return LY_ENOT in case there is not the expected envelope.
+ * @return LY_ERR in case of parsing failure.
+ */
+static LY_ERR
+lydjson_object_envelope(struct lyjson_ctx *jsonctx, struct lyd_node *parent, const char *module_key, const char *object_id, struct lyd_node **envp_p)
+{
+    LY_ERR ret = LY_ENOT, r;
+    const char *name, *prefix;
+    size_t name_len, prefix_len;
+    int is_attr;
+    enum LYJSON_PARSER_STATUS status;
+
+    *envp_p = NULL;
+
+    /* "id" envelope */
+    lydjson_parse_name(jsonctx->value, jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
+    LY_CHECK_GOTO(is_attr, cleanup);
+    LY_CHECK_GOTO(lydjson_get_node_prefix(parent, prefix, prefix_len, &prefix, &prefix_len), cleanup);
+    LY_CHECK_GOTO(prefix_len != strlen(module_key) || strncmp(prefix, module_key, prefix_len), cleanup);
+    LY_CHECK_GOTO(name_len != strlen(object_id) || strncmp(name, object_id, name_len), cleanup);
+
+    r = lyjson_ctx_next(jsonctx, &status);
+    LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+    LY_CHECK_GOTO(status != LYJSON_OBJECT, cleanup);
+
+    /* create the object envelope */
+    ret = lyd_create_opaq(jsonctx->ctx, object_id, strlen(object_id), "", 0, NULL,
+                          LYD_NODE_OPAQ_ISENVELOPE, LYD_JSON, NULL, NULL, 0,
+                          module_key, ly_strlen(module_key), envp_p);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    if (parent) {
+        lyd_insert_node(parent, NULL, *envp_p);
+    }
+
+    ret = LY_SUCCESS;
+cleanup:
+    return ret;
+}
+
+static LY_ERR
+lydjson_object_envelope_close(struct lyjson_ctx *jsonctx, const char *object_id)
+{
+    enum LYJSON_PARSER_STATUS status;
+
+    LY_CHECK_RET(lyjson_ctx_next(jsonctx, &status));
+    if (status == LYJSON_END) {
+        LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LY_VCODE_EOF);
+        return LY_EVALID;
+    } else if (status != LYJSON_OBJECT_CLOSED) {
+        LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LYVE_SYNTAX, "Unexpected sibling member \"%.*s\" of \"%s\".",
+               jsonctx->value_len, jsonctx->value, object_id);
+        return LY_EVALID;
+    }
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_parse_json_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_json_ctx *lydctx = NULL;
+    struct lyd_node *rpc_e = NULL, *act_e = NULL;
+    struct lyd_node *tree = NULL;
+    enum LYJSON_PARSER_STATUS status;
+
+    /* init */
+    ret = lyd_parse_json_init(ctx, in, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &lydctx);
+    LY_CHECK_GOTO(ret || !lydctx, cleanup);
+    lydctx->int_opts = LYD_INTOPT_RPC;
+
+    /* process envelope(s), if present */
+
+    /* process rpc */
+    ret = lydjson_object_envelope(lydctx->jsonctx, NULL, "ietf-netconf", "rpc", &rpc_e);
+    if (ret == LY_ENOT) {
+        ret = LY_SUCCESS;
+        goto parse_content;
+    } else if (ret) {
+        goto cleanup;
+    }
+    /* process action */
+    ret = lydjson_object_envelope(lydctx->jsonctx, rpc_e, "yang", "action", &act_e);
+    if (ret == LY_ENOT) {
+        ret = LY_SUCCESS;
+        goto parse_content;
+    } else if (ret) {
+        goto cleanup;
+    }
+
+parse_content:
+
+    status = lyjson_ctx_status(lydctx->jsonctx, 0);
+    assert(status == LYJSON_OBJECT);
+
+    /* read subtree(s) */
+    while (lydctx->jsonctx->in->current[0] && status != LYJSON_OBJECT_CLOSED) {
+        ret = lydjson_subtree_r(lydctx, act_e ? (struct lyd_node_inner*)act_e : (struct lyd_node_inner*)rpc_e, &tree);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        status = lyjson_ctx_status(lydctx->jsonctx, 0);
+    }
+
+    /* finish linking metadata */
+    ret = lydjson_metadata_finish(lydctx, &tree);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* make sure we have parsed some operation */
+    if (!lydctx->op_node) {
+        LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_DATA, "Missing the %s node.",
+               act_e ? "action" : (rpc_e ? "rpc" : "rpc/action"));
+        ret = LY_EVALID;
+        goto cleanup;
+    }
+
+    if (act_e) {
+        /* finish action envelope */
+        ret = lydjson_object_envelope_close(lydctx->jsonctx, "action");
+        LY_CHECK_GOTO(ret, cleanup);
+        if (lydctx->op_node->schema->nodetype != LYS_ACTION) {
+            LOGVAL(ctx, LY_VLOG_LYD, lydctx->op_node, LYVE_DATA, "Unexpected %s element, an \"action\" expected.",
+                   lys_nodetype2str(lydctx->op_node->schema->nodetype));
+            ret = LY_EVALID;
+            goto cleanup;
+        }
+    }
+    if (rpc_e) {
+        /* finish rpc envelope */
+        ret = lydjson_object_envelope_close(lydctx->jsonctx, "rpc");
+        LY_CHECK_GOTO(ret, cleanup);
+        if (!act_e && lydctx->op_node->schema->nodetype != LYS_RPC) {
+            LOGVAL(ctx, LY_VLOG_LYD, lydctx->op_node, LYVE_DATA, "Unexpected %s element, an \"rpc\" expected.",
+                   lys_nodetype2str(lydctx->op_node->schema->nodetype));
+            ret = LY_EVALID;
+            goto cleanup;
+        }
+    }
+
+    if (op_p) {
+        *op_p = lydctx->op_node;
+    }
+    assert(tree);
+    if (tree_p) {
+        if (rpc_e) {
+            *tree_p = rpc_e;
+        } else if (act_e) {
+            *tree_p = act_e;
+        } else {
+            *tree_p = tree;
+        }
+    }
+
+cleanup:
+    /* we have used parse_only flag */
+    assert(!lydctx || (!lydctx->unres_node_type.count && !lydctx->unres_meta_type.count && !lydctx->when_check.count));
+
+    lyd_json_ctx_free((struct lyd_ctx *)lydctx);
+    if (ret) {
+        lyd_free_all(tree);
+        lyd_free_tree(act_e);
+        lyd_free_tree(rpc_e);
+    }
+    return ret;
+}
+
+LY_ERR
+lyd_parse_json_reply(const struct lyd_node *request, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_json_ctx *lydctx = NULL;
+    struct lyd_node *rpcr_e = NULL, *iter, *req_op, *rep_op = NULL;
+    enum LYJSON_PARSER_STATUS status;
+
+    /* init */
+    ret = lyd_parse_json_init(LYD_NODE_CTX(request), in, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &lydctx);
+    LY_CHECK_GOTO(ret || !lydctx, cleanup);
+    lydctx->int_opts = LYD_INTOPT_REPLY;
+
+    /* find request OP */
+    LYD_TREE_DFS_BEGIN((struct lyd_node *)request, req_op) {
+        if (req_op->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
+            break;
+        }
+        LYD_TREE_DFS_END(request, req_op);
+    }
+    if (!(req_op->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
+        LOGERR(LYD_NODE_CTX(request), LY_EINVAL, "No RPC/action in the request found.");
+        ret = LY_EINVAL;
+        goto cleanup;
+    }
+
+    /* duplicate request OP with parents */
+    LY_CHECK_GOTO(ret = lyd_dup_single(req_op, NULL, LYD_DUP_WITH_PARENTS, &rep_op), cleanup);
+
+    /* parse "rpc-reply", if any */
+    ret = lydjson_object_envelope(lydctx->jsonctx, NULL, "ietf-netconf", "rpc-reply", &rpcr_e);
+    if (ret == LY_ENOT) {
+        ret = LY_SUCCESS;
+    } else if (ret) {
+        goto cleanup;
+    }
+
+    status = lyjson_ctx_status(lydctx->jsonctx, 0);
+    assert(status == LYJSON_OBJECT);
+
+    /* read subtree(s) */
+    while (lydctx->jsonctx->in->current[0] && status != LYJSON_OBJECT_CLOSED) {
+        ret = lydjson_subtree_r(lydctx, (struct lyd_node_inner*)rep_op, lyd_node_children_p(rep_op));
+        LY_CHECK_GOTO(ret, cleanup);
+
+        status = lyjson_ctx_status(lydctx->jsonctx, 0);
+    }
+
+    /* finish linking metadata */
+    ret = lydjson_metadata_finish(lydctx, lyd_node_children_p(rep_op));
+    LY_CHECK_GOTO(ret, cleanup);
+
+    if (rpcr_e) {
+        /* finish rpc-reply envelope */
+        ret = lydjson_object_envelope_close(lydctx->jsonctx, "rpc-reply");
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    if (op_p) {
+        *op_p = rep_op;
+    }
+    for (iter = rep_op; iter->parent; iter = (struct lyd_node *)iter->parent);
+    if (tree_p) {
+        *tree_p = iter;
+    }
+    if (rpcr_e) {
+        /* connect to the operation */
+        lyd_insert_node(rpcr_e, NULL, iter);
+        if (tree_p) {
+            *tree_p = rpcr_e;
+        }
+    }
+
+cleanup:
+    /* we have used parse_only flag */
+    assert(!lydctx || (!lydctx->unres_node_type.count && !lydctx->unres_meta_type.count && !lydctx->when_check.count));
+
+    lyd_json_ctx_free((struct lyd_ctx *)lydctx);
+    if (ret) {
+        lyd_free_all(rep_op);
+        lyd_free_tree(rpcr_e);
+    }
+    return ret;
 }
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index fc74236..3c8fa76 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -33,6 +33,32 @@
 #include "tree_schema.h"
 #include "validation.h"
 
+void
+lylyb_ctx_free(struct lylyb_ctx *ctx)
+{
+    LY_ARRAY_COUNT_TYPE u;
+
+    LY_ARRAY_FREE(ctx->subtrees);
+    LY_ARRAY_FREE(ctx->models);
+
+    LY_ARRAY_FOR(ctx->sib_hts, u) {
+        lyht_free(ctx->sib_hts[u].ht);
+    }
+    LY_ARRAY_FREE(ctx->sib_hts);
+
+    free(ctx);
+}
+
+void
+lyd_lyb_ctx_free(struct lyd_ctx *lydctx)
+{
+    struct lyd_lyb_ctx *ctx = (struct lyd_lyb_ctx *)lydctx;
+
+    lyd_ctx_free(lydctx);
+    lylyb_ctx_free(ctx->lybctx);
+    free(ctx);
+}
+
 /**
  * @brief Read YANG data from LYB input. Metadata are handled transparently and not returned.
  *
@@ -41,7 +67,7 @@
  * @param[in] lybctx LYB context.
  */
 static void
-lyb_read(uint8_t *buf, size_t count, struct lyd_lyb_ctx *lybctx)
+lyb_read(uint8_t *buf, size_t count, struct lylyb_ctx *lybctx)
 {
     LY_ARRAY_COUNT_TYPE u;
     struct lyd_lyb_subtree *empty;
@@ -109,7 +135,7 @@
  * @param[in] lybctx LYB context.
  */
 static void
-lyb_read_number(void *num, size_t num_size, size_t bytes, struct lyd_lyb_ctx *lybctx)
+lyb_read_number(void *num, size_t num_size, size_t bytes, struct lylyb_ctx *lybctx)
 {
     uint64_t buf = 0;
 
@@ -145,7 +171,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_read_string(char **str, int with_length, struct lyd_lyb_ctx *lybctx)
+lyb_read_string(char **str, int with_length, struct lylyb_ctx *lybctx)
 {
     int next_chunk = 0;
     size_t len = 0, cur_len;
@@ -194,7 +220,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_read_stop_subtree(struct lyd_lyb_ctx *lybctx)
+lyb_read_stop_subtree(struct lylyb_ctx *lybctx)
 {
     if (LYB_LAST_SUBTREE(lybctx).written) {
         LOGINT_RET(lybctx->ctx);
@@ -211,7 +237,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_read_start_subtree(struct lyd_lyb_ctx *lybctx)
+lyb_read_start_subtree(struct lylyb_ctx *lybctx)
 {
     uint8_t meta_buf[LYB_META_BYTES];
     LY_ARRAY_COUNT_TYPE u;
@@ -245,7 +271,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_parse_model(struct lyd_lyb_ctx *lybctx, const struct lys_module **mod)
+lyb_parse_model(struct lylyb_ctx *lybctx, uint32_t parse_options, const struct lys_module **mod)
 {
     LY_ERR ret = LY_SUCCESS;
     char *mod_name = NULL, mod_rev[11];
@@ -267,7 +293,7 @@
     if (rev) {
         sprintf(mod_rev, "%04u-%02u-%02u", ((rev & 0xFE00) >> 9) + 2000, (rev & 0x01E0) >> 5, rev & 0x001Fu);
         *mod = ly_ctx_get_module(lybctx->ctx, mod_name, mod_rev);
-        if ((lybctx->parse_options & LYD_PARSE_LYB_MOD_UPDATE) && !(*mod)) {
+        if ((parse_options & LYD_PARSE_LYB_MOD_UPDATE) && !(*mod)) {
             /* try to use an updated module */
             *mod = ly_ctx_get_module_implemented(lybctx->ctx, mod_name);
             if (*mod && (!(*mod)->revision || (strcmp((*mod)->revision, mod_rev) < 0))) {
@@ -288,7 +314,7 @@
     }*/
 
     if (!*mod || !(*mod)->implemented) {
-        if (lybctx->parse_options & LYD_PARSE_STRICT) {
+        if (parse_options & LYD_PARSE_STRICT) {
             if (!*mod) {
                 LOGERR(lybctx->ctx, LY_EINVAL, "Invalid context for LYB data parsing, missing module \"%s%s%s\".",
                     mod_name, rev ? "@" : "", rev ? mod_rev : "");
@@ -325,37 +351,37 @@
     const struct lys_module *mod;
 
     /* read number of attributes stored */
-    lyb_read(&count, 1, lybctx);
+    lyb_read(&count, 1, lybctx->lybctx);
 
     /* read attributes */
     for (i = 0; i < count; ++i) {
-        ret = lyb_read_start_subtree(lybctx);
+        ret = lyb_read_start_subtree(lybctx->lybctx);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* find model */
-        ret = lyb_parse_model(lybctx, &mod);
+        ret = lyb_parse_model(lybctx->lybctx, lybctx->parse_options, &mod);
         LY_CHECK_GOTO(ret, cleanup);
 
         if (!mod) {
             /* skip it */
             do {
-                lyb_read(NULL, LYB_LAST_SUBTREE(lybctx).written, lybctx);
-            } while (LYB_LAST_SUBTREE(lybctx).written);
+                lyb_read(NULL, LYB_LAST_SUBTREE(lybctx->lybctx).written, lybctx->lybctx);
+            } while (LYB_LAST_SUBTREE(lybctx->lybctx).written);
             goto stop_subtree;
         }
 
         /* meta name */
-        ret = lyb_read_string(&meta_name, 1, lybctx);
+        ret = lyb_read_string(&meta_name, 1, lybctx->lybctx);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* meta value */
-        ret = lyb_read_string(&meta_value, 0, lybctx);
+        ret = lyb_read_string(&meta_value, 0, lybctx->lybctx);
         LY_CHECK_GOTO(ret, cleanup);
         dynamic = 1;
 
         /* create metadata */
-        ret = lyd_create_meta(NULL, meta, mod, meta_name, strlen(meta_name), meta_value, strlen(meta_value), &dynamic,
-                              lydjson_resolve_prefix, NULL, LYD_JSON, sparent);
+        ret = lyd_parser_create_meta((struct lyd_ctx*)lybctx, NULL, meta, mod, meta_name, strlen(meta_name), meta_value, ly_strlen(meta_value),
+                                     &dynamic, 0, lydjson_resolve_prefix, NULL, LYD_JSON, sparent);
 
         /* free strings */
         free(meta_name);
@@ -365,14 +391,10 @@
             dynamic = 0;
         }
 
-        if (ret == LY_EINCOMPLETE) {
-            ly_set_add(&lybctx->unres_meta_type, *meta, LY_SET_OPT_USEASLIST);
-        } else if (ret) {
-            goto cleanup;
-        }
+        LY_CHECK_GOTO(ret, cleanup);
 
 stop_subtree:
-        ret = lyb_read_stop_subtree(lybctx);
+        ret = lyb_read_stop_subtree(lybctx->lybctx);
         LY_CHECK_GOTO(ret, cleanup);
     }
 
@@ -393,7 +415,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_parse_opaq_prefixes(struct lyd_lyb_ctx *lybctx, struct ly_prefix **prefs)
+lyb_parse_opaq_prefixes(struct lylyb_ctx *lybctx, struct ly_prefix **prefs)
 {
     LY_ERR ret = LY_SUCCESS;
     uint8_t count, i;
@@ -412,12 +434,12 @@
         /* prefix */
         ret = lyb_read_string(&str, 1, lybctx);
         LY_CHECK_GOTO(ret, cleanup);
-        (*prefs)[i].pref = lydict_insert_zc(lybctx->ctx, str);
+        (*prefs)[i].id = lydict_insert_zc(lybctx->ctx, str);
 
-        /* namespace */
+        /* module reference is the same as JSON name */
         ret = lyb_read_string(&str, 1, lybctx);
         LY_CHECK_GOTO(ret, cleanup);
-        (*prefs)[i].ns = lydict_insert_zc(lybctx->ctx, str);
+        (*prefs)[i].module_name = lydict_insert_zc(lybctx->ctx, str);
     }
 
 cleanup:
@@ -436,12 +458,12 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_parse_attributes(struct lyd_lyb_ctx *lybctx, struct ly_attr **attr)
+lyb_parse_attributes(struct lylyb_ctx *lybctx, struct lyd_attr **attr)
 {
     LY_ERR ret = LY_SUCCESS;
     uint8_t count, i;
-    struct ly_attr *attr2;
-    char *prefix = NULL, *ns = NULL, *name = NULL, *value = NULL;
+    struct lyd_attr *attr2;
+    char *prefix = NULL, *module_name = NULL, *name = NULL, *value = NULL;
     int dynamic = 0;
     LYD_FORMAT format = 0;
     struct ly_prefix *val_prefs = NULL;
@@ -463,11 +485,11 @@
         }
 
         /* namespace, may be empty */
-        ret = lyb_read_string(&ns, 1, lybctx);
+        ret = lyb_read_string(&module_name, 1, lybctx);
         LY_CHECK_GOTO(ret, cleanup);
-        if (!ns[0]) {
-            free(ns);
-            ns = NULL;
+        if (!module_name[0]) {
+            free(module_name);
+            module_name = NULL;
         }
 
         /* name */
@@ -487,14 +509,14 @@
         dynamic = 1;
 
         /* attr2 is always changed to the created attribute */
-        ret = ly_create_attr(NULL, &attr2, lybctx->ctx, name, strlen(name), value, strlen(value), &dynamic, format,
-                             val_prefs, prefix, prefix ? strlen(prefix) : 0, ns);
+        ret = lyd_create_attr(NULL, &attr2, lybctx->ctx, name, strlen(name), value, ly_strlen(value), &dynamic, 0, format,
+                              val_prefs, prefix, ly_strlen(prefix), module_name, ly_strlen(module_name));
         LY_CHECK_GOTO(ret, cleanup);
 
         free(prefix);
         prefix = NULL;
-        free(ns);
-        ns = NULL;
+        free(module_name);
+        module_name = NULL;
         free(name);
         name = NULL;
         val_prefs = NULL;
@@ -511,7 +533,7 @@
 
 cleanup:
     free(prefix);
-    free(ns);
+    free(module_name);
     free(name);
     if (dynamic) {
         free(value);
@@ -551,54 +573,6 @@
 }
 
 /**
- * @brief Check that a schema node is suitable based on options.
- *
- * @param[in] lybctx LYB context.
- * @param[in] snode Schema node to check.
- * @return LY_ERR value.
- */
-static LY_ERR
-lyb_parse_check_schema(struct lyd_lyb_ctx *lybctx, const struct lysc_node *snode)
-{
-    LY_ERR ret = LY_SUCCESS;
-
-    if ((lybctx->parse_options & LYD_PARSE_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
-        LOGVAL(lybctx->ctx, LY_VLOG_LYSC, snode, LY_VCODE_INNODE, "state", snode->name);
-        return LY_EVALID;
-    }
-
-    if (snode->nodetype & (LYS_RPC | LYS_ACTION)) {
-        if (lybctx->int_opts & LYD_INTOPT_RPC) {
-            if (lybctx->op_ntf) {
-                LOGVAL(lybctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
-                       lys_nodetype2str(snode->nodetype), snode->name,
-                       lys_nodetype2str(lybctx->op_ntf->schema->nodetype), lybctx->op_ntf->schema->name);
-                return LY_EVALID;
-            }
-        } else {
-            LOGVAL(lybctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\".",
-                   lys_nodetype2str(snode->nodetype), snode->name);
-            return LY_EVALID;
-        }
-    } else if (snode->nodetype == LYS_NOTIF) {
-        if (lybctx->int_opts & LYD_INTOPT_NOTIF) {
-            if (lybctx->op_ntf) {
-                LOGVAL(lybctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
-                       lys_nodetype2str(snode->nodetype), snode->name,
-                       lys_nodetype2str(lybctx->op_ntf->schema->nodetype), lybctx->op_ntf->schema->name);
-                return LY_EVALID;
-            }
-        } else {
-            LOGVAL(lybctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\".",
-                   lys_nodetype2str(snode->nodetype), snode->name);
-            return LY_EVALID;
-        }
-    }
-
-    return ret;
-}
-
-/**
  * @brief Parse schema node hash.
  *
  * @param[in] lybctx LYB context.
@@ -622,7 +596,7 @@
     getnext_opts = LYS_GETNEXT_NOSTATECHECK | (lybctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0);
 
     /* read the first hash */
-    lyb_read(&hash[0], sizeof *hash, lybctx);
+    lyb_read(&hash[0], sizeof *hash, lybctx->lybctx);
 
     if (!hash[0]) {
         /* opaque node */
@@ -632,7 +606,7 @@
     /* based on the first hash read all the other ones, if any */
     for (i = 0; !(hash[0] & (LYB_HASH_COLLISION_ID >> i)); ++i) {
         if (i > LYB_HASH_BITS) {
-            LOGINT_RET(lybctx->ctx);
+            LOGINT_RET(lybctx->lybctx->ctx);
         }
     }
 
@@ -641,7 +615,7 @@
 
     /* read the rest of hashes */
     for (j = i; j; --j) {
-        lyb_read(&hash[j - 1], sizeof *hash, lybctx);
+        lyb_read(&hash[j - 1], sizeof *hash, lybctx->lybctx);
 
         /* correct collision ID */
         assert(hash[j - 1] & (LYB_HASH_COLLISION_ID >> (j - 1)));
@@ -653,7 +627,7 @@
     sibling = NULL;
     while ((sibling = lys_getnext(sibling, sparent, mod ? mod->compiled : NULL, getnext_opts))) {
         /* skip schema nodes from models not present during printing */
-        if (lyb_has_schema_model(sibling, lybctx->models)
+        if (lyb_has_schema_model(sibling, lybctx->lybctx->models)
                 && lyb_is_schema_hash_match((struct lysc_node *)sibling, hash, i + 1)) {
             /* match found */
             break;
@@ -662,14 +636,14 @@
 
     if (!sibling && (lybctx->parse_options & LYD_PARSE_STRICT)) {
         if (mod) {
-            LOGVAL(lybctx->ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Failed to find matching hash for a top-level node"
+            LOGVAL(lybctx->lybctx->ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Failed to find matching hash for a top-level node"
                    " from \"%s\".", mod->name);
         } else {
-            LOGVAL(lybctx->ctx, LY_VLOG_LYSC, sparent, LYVE_REFERENCE, "Failed to find matching hash for a child node"
+            LOGVAL(lybctx->lybctx->ctx, LY_VLOG_LYSC, sparent, LYVE_REFERENCE, "Failed to find matching hash for a child node"
                    " of \"%s\".", sparent->name);
         }
         return LY_EVALID;
-    } else if (sibling && (ret = lyb_parse_check_schema(lybctx, sibling))) {
+    } else if (sibling && (ret = lyd_parser_check_schema((struct lyd_ctx *)lybctx, sibling))) {
         return ret;
     }
 
@@ -683,7 +657,7 @@
  * @param[in] lybctx LYB context.
  */
 static void
-lyb_skip_subtree(struct lyd_lyb_ctx *lybctx)
+lyb_skip_subtree(struct lylyb_ctx *lybctx)
 {
     do {
         /* first skip any meta information inside */
@@ -710,20 +684,21 @@
     const struct lys_module *mod;
     const struct lysc_node *snode = NULL;
     struct lyd_meta *meta = NULL, *m;
-    struct ly_attr *attr = NULL, *a;
+    struct lyd_attr *attr = NULL, *a;
     struct ly_prefix *val_prefs = NULL;
     LYD_ANYDATA_VALUETYPE value_type;
-    char *value = NULL, *name = NULL, *prefix = NULL, *ns = NULL;
+    char *value = NULL, *name = NULL, *prefix = NULL, *module_key = NULL;
     int dynamic = 0;
     LYD_FORMAT format = 0;
     int prev_lo;
+    const struct ly_ctx *ctx = lybctx->lybctx->ctx;
 
     /* register a new subtree */
-    LY_CHECK_GOTO(ret = lyb_read_start_subtree(lybctx), cleanup);
+    LY_CHECK_GOTO(ret = lyb_read_start_subtree(lybctx->lybctx), cleanup);
 
     if (!parent) {
         /* top-level, read module name */
-        ret = lyb_parse_model(lybctx, &mod);
+        ret = lyb_parse_model(lybctx->lybctx, lybctx->parse_options, &mod);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* read hash, find the schema node starting from mod */
@@ -737,7 +712,7 @@
 
      if (!snode && !(lybctx->parse_options & LYD_PARSE_OPAQ)) {
         /* unknown data, skip them */
-        lyb_skip_subtree(lybctx);
+        lyb_skip_subtree(lybctx->lybctx);
         goto stop_subtree;
      }
 
@@ -746,73 +721,67 @@
         ret = lyb_parse_metadata(lybctx, snode, &meta);
         LY_CHECK_GOTO(ret, cleanup);
     } else {
-        ret = lyb_parse_attributes(lybctx, &attr);
+        ret = lyb_parse_attributes(lybctx->lybctx, &attr);
         LY_CHECK_GOTO(ret, cleanup);
     }
 
     if (!snode) {
         /* parse prefix */
-        ret = lyb_read_string(&prefix, 1, lybctx);
+        ret = lyb_read_string(&prefix, 1, lybctx->lybctx);
         LY_CHECK_GOTO(ret, cleanup);
 
-        /* parse namespace */
-        ret = lyb_read_string(&ns, 1, lybctx);
+        /* parse module key */
+        ret = lyb_read_string(&module_key, 1, lybctx->lybctx);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* parse name */
-        ret = lyb_read_string(&name, 1, lybctx);
+        ret = lyb_read_string(&name, 1, lybctx->lybctx);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* parse value prefixes */
-        ret = lyb_parse_opaq_prefixes(lybctx, &val_prefs);
+        ret = lyb_parse_opaq_prefixes(lybctx->lybctx, &val_prefs);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* parse format */
-        lyb_read((uint8_t *)&format, 1, lybctx);
+        lyb_read((uint8_t *)&format, 1, lybctx->lybctx);
 
         /* parse value */
-        ret = lyb_read_string(&value, 0, lybctx);
+        ret = lyb_read_string(&value, 0, lybctx->lybctx);
         LY_CHECK_GOTO(ret, cleanup);
         dynamic = 1;
 
         /* create node */
-        ret = lyd_create_opaq(lybctx->ctx, name, strlen(name), value, strlen(value), &dynamic, format, val_prefs, prefix,
-                              strlen(prefix), ns, &node);
+        ret = lyd_create_opaq(ctx, name, strlen(name), value, strlen(value), &dynamic, 0, format, val_prefs, prefix,
+                              ly_strlen(prefix), module_key, ly_strlen(module_key), &node);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* process children */
-        while (LYB_LAST_SUBTREE(lybctx).written) {
+        while (LYB_LAST_SUBTREE(lybctx->lybctx).written) {
             ret = lyb_parse_subtree_r(lybctx, (struct lyd_node_inner *)node, NULL);
             LY_CHECK_GOTO(ret, cleanup);
         }
     } else if (snode->nodetype & LYD_NODE_TERM) {
         /* parse value */
-        ret = lyb_read_string(&value, 0, lybctx);
+        ret = lyb_read_string(&value, 0, lybctx->lybctx);
         LY_CHECK_GOTO(ret, cleanup);
         dynamic = 1;
 
         /* create node */
-        ret = lyd_create_term(snode, value, strlen(value), &dynamic, lydjson_resolve_prefix, NULL, LYD_JSON, &node);
+        ret = lyd_parser_create_term((struct lyd_ctx*)lybctx, snode, value, ly_strlen(value), &dynamic, 0,
+                                     lydjson_resolve_prefix, NULL, LYD_JSON, &node);
         if (dynamic) {
             free(value);
             dynamic = 0;
         }
         value = NULL;
-        if (ret == LY_EINCOMPLETE) {
-            if (!(lybctx->parse_options & LYD_PARSE_ONLY)) {
-                ly_set_add(&lybctx->unres_node_type, node, LY_SET_OPT_USEASLIST);
-            }
-            ret = LY_SUCCESS;
-        } else if (ret) {
-            goto cleanup;
-        }
+        LY_CHECK_GOTO(ret, cleanup);
     } else if (snode->nodetype & LYD_NODE_INNER) {
         /* create node */
         ret = lyd_create_inner(snode, &node);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* process children */
-        while (LYB_LAST_SUBTREE(lybctx).written) {
+        while (LYB_LAST_SUBTREE(lybctx->lybctx).written) {
             ret = lyb_parse_subtree_r(lybctx, (struct lyd_node_inner *)node, NULL);
             LY_CHECK_GOTO(ret, cleanup);
         }
@@ -831,19 +800,19 @@
 
         if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
             /* rememeber the RPC/action/notification */
-            lybctx->op_ntf = node;
+            lybctx->op_node = node;
         }
     } else if (snode->nodetype & LYD_NODE_ANY) {
         /* parse value type */
-        lyb_read((uint8_t *)&value_type, sizeof value_type, lybctx);
+        lyb_read((uint8_t *)&value_type, sizeof value_type, lybctx->lybctx);
         if (value_type == LYD_ANYDATA_DATATREE) {
             /* invalid situation */
-            LOGINT(lybctx->ctx);
+            LOGINT(ctx);
             goto cleanup;
         }
 
         /* read anydata content */
-        ret = lyb_read_string(&value, 0, lybctx);
+        ret = lyb_read_string(&value, 0, lybctx->lybctx);
         LY_CHECK_GOTO(ret, cleanup);
         dynamic = 1;
 
@@ -852,7 +821,7 @@
             prev_lo = ly_log_options(0);
 
             /* try to parse LYB into a data tree */
-            if (lyd_parse_data_mem((struct ly_ctx *)lybctx->ctx, value, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT, 0, &tree) == LY_SUCCESS) {
+            if (lyd_parse_data_mem(ctx, value, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT, 0, &tree) == LY_SUCCESS) {
                 /* successfully parsed */
                 free(value);
                 value = (char *)tree;
@@ -902,20 +871,20 @@
 
 stop_subtree:
     /* end the subtree */
-    ret = lyb_read_stop_subtree(lybctx);
+    ret = lyb_read_stop_subtree(lybctx->lybctx);
     LY_CHECK_GOTO(ret, cleanup);
 
 cleanup:
     free(prefix);
-    free(ns);
+    free(module_key);
     free(name);
     if (dynamic) {
         free(value);
     }
-    ly_free_val_prefs(lybctx->ctx, val_prefs);
+    ly_free_val_prefs(ctx, val_prefs);
 
     lyd_free_meta_siblings(meta);
-    ly_free_attr_siblings(lybctx->ctx, attr);
+    ly_free_attr_siblings(ctx, attr);
     lyd_free_tree(node);
     return ret;
 }
@@ -927,7 +896,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_parse_data_models(struct lyd_lyb_ctx *lybctx)
+lyb_parse_data_models(struct lylyb_ctx *lybctx, uint32_t parse_options)
 {
     LY_ERR ret;
     uint32_t count;
@@ -941,7 +910,7 @@
 
         /* read modules */
         for (u = 0; u < count; ++u) {
-            ret = lyb_parse_model(lybctx, &lybctx->models[u]);
+            ret = lyb_parse_model(lybctx, parse_options, &lybctx->models[u]);
             LY_CHECK_RET(ret);
             LY_ARRAY_INCREMENT(lybctx->models);
         }
@@ -957,7 +926,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_parse_magic_number(struct lyd_lyb_ctx *lybctx)
+lyb_parse_magic_number(struct lylyb_ctx *lybctx)
 {
     char magic_byte = 0;
 
@@ -989,7 +958,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_parse_header(struct lyd_lyb_ctx *lybctx)
+lyb_parse_header(struct lylyb_ctx *lybctx)
 {
     uint8_t byte = 0;
 
@@ -1005,198 +974,139 @@
     return LY_SUCCESS;
 }
 
-LY_ERR
-lyd_parse_lyb_data(const struct ly_ctx *ctx, struct ly_in *in, int parse_options, int validate_options,
-                   struct lyd_node **tree)
+/*
+ * @param[in] ctx libyang context for logging
+ * @param[in] parent Parent node where to connect the parsed data, required for reply where the reply data are connected with the request operation
+ * @param[in] in Input structure.
+ * @param[in] parse_options Options for parser, see @ref dataparseroptions.
+ * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] data_type Internal data parser flag to distnguish type of the data to parse (RPC/Reply/Notification/regular data].
+ * @param[out] tree_p Parsed data tree. Note that NULL can be a valid result.
+ * @param[out] op_p Optional pointer to the actual operation. Useful for action and inner notifications.
+ * @param[out] lydctx_p Data parser context to finish validation.
+ */
+static LY_ERR
+lyd_parse_lyb_(const struct ly_ctx *ctx, struct lyd_node_inner **parent, struct ly_in *in, int parse_options, int validate_options,
+               int data_type, struct lyd_node **tree_p, struct lyd_node **op_p, struct lyd_ctx **lydctx_p)
 {
     LY_ERR ret = LY_SUCCESS;
-    struct lyd_lyb_ctx lybctx = {0};
+    struct lyd_lyb_ctx *lybctx;
+    struct lyd_node *tree = NULL;
 
     assert(!(parse_options & ~LYD_PARSE_OPTS_MASK));
     assert(!(validate_options & ~LYD_VALIDATE_OPTS_MASK));
 
-    *tree = NULL;
+    lybctx = calloc(1, sizeof *lybctx);
+    LY_CHECK_ERR_RET(!lybctx, LOGMEM(ctx), LY_EMEM);
+    lybctx->lybctx = calloc(1, sizeof *lybctx->lybctx);
+    LY_CHECK_ERR_RET(!lybctx->lybctx, LOGMEM(ctx), LY_EMEM);
 
-    lybctx.in = in;
-    lybctx.ctx = ctx;
-    lybctx.parse_options = parse_options;
-    lybctx.validate_options = validate_options;
+    lybctx->lybctx->in = in;
+    lybctx->lybctx->ctx = ctx;
+    lybctx->parse_options = parse_options;
+    lybctx->validate_options = validate_options;
+    lybctx->int_opts = data_type;
+    lybctx->free = lyd_lyb_ctx_free;
 
     /* read magic number */
-    ret = lyb_parse_magic_number(&lybctx);
+    ret = lyb_parse_magic_number(lybctx->lybctx);
     LY_CHECK_GOTO(ret, cleanup);
 
     /* read header */
-    ret = lyb_parse_header(&lybctx);
+    ret = lyb_parse_header(lybctx->lybctx);
     LY_CHECK_GOTO(ret, cleanup);
 
     /* read used models */
-    ret = lyb_parse_data_models(&lybctx);
+    ret = lyb_parse_data_models(lybctx->lybctx, lybctx->parse_options);
     LY_CHECK_GOTO(ret, cleanup);
 
     /* read subtree(s) */
-    while (lybctx.in->current[0]) {
-        ret = lyb_parse_subtree_r(&lybctx, NULL, tree);
+    while (lybctx->lybctx->in->current[0]) {
+        ret = lyb_parse_subtree_r(lybctx, parent ? *parent : NULL, &tree);
         LY_CHECK_GOTO(ret, cleanup);
     }
 
     /* read the last zero, parsing finished */
-    ly_in_skip(lybctx.in, 1);
+    ly_in_skip(lybctx->lybctx->in, 1);
 
-    /* TODO validation */
+    if (data_type == LYD_INTOPT_RPC) {
+        /* make sure we have parsed some operation */
+        if (!lybctx->op_node) {
+            LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_DATA, "Missing the \"rpc\"/\"action\" node.");
+            ret = LY_EVALID;
+            goto cleanup;
+        }
+
+        if (op_p) {
+            *op_p = lybctx->op_node;
+        }
+        assert(tree);
+    } else if (data_type == LYD_INTOPT_REPLY) {
+        struct lyd_node_inner *iter;
+
+        assert(parent);
+
+        if (op_p) {
+            *op_p = (struct lyd_node*)(*parent);
+        }
+        for (iter = *parent; iter->parent; iter = iter->parent);
+        tree = (struct lyd_node *)iter;
+        *parent = NULL;
+
+    } else if (data_type == LYD_INTOPT_NOTIF) {
+        /* make sure we have parsed some notification */
+        if (!lybctx->op_node) {
+            LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_DATA, "Missing the \"notification\" node.");
+            ret = LY_EVALID;
+            goto cleanup;
+        }
+
+        if (op_p) {
+            *op_p = lybctx->op_node;
+        }
+        assert(tree);
+    }
 
 cleanup:
-    LY_ARRAY_FREE(lybctx.subtrees);
-    LY_ARRAY_FREE(lybctx.models);
-    ly_set_erase(&lybctx.unres_node_type, NULL);
-    ly_set_erase(&lybctx.unres_meta_type, NULL);
-    ly_set_erase(&lybctx.when_check, NULL);
-
-    if (ret) {
-        lyd_free_all(*tree);
-        *tree = NULL;
+    if (ret || !lydctx_p) {
+        lyd_lyb_ctx_free((struct lyd_ctx *)lybctx);
+        if (ret) {
+            lyd_free_all(tree);
+        }
+    } else {
+        *lydctx_p = (struct lyd_ctx *)lybctx;
+        if (tree_p) {
+            *tree_p = tree;
+        }
     }
     return ret;
 }
 
 LY_ERR
-lyd_parse_lyb_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree, struct lyd_node **op)
+lyd_parse_lyb_data(const struct ly_ctx *ctx, struct ly_in *in, int parse_options, int validate_options,
+                   struct lyd_node **tree_p, struct lyd_ctx **lydctx_p)
 {
-    LY_ERR ret = LY_SUCCESS;
-    struct lyd_lyb_ctx lybctx = {0};
-
-    lybctx.in = in;
-    lybctx.ctx = ctx;
-    lybctx.parse_options = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
-    lybctx.int_opts = LYD_INTOPT_RPC;
-
-    *tree = NULL;
-    if (op) {
-        *op = NULL;
-    }
-
-    /* read magic number */
-    ret = lyb_parse_magic_number(&lybctx);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* read header */
-    ret = lyb_parse_header(&lybctx);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* read used models */
-    ret = lyb_parse_data_models(&lybctx);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* read subtree(s) */
-    while (lybctx.in->current[0]) {
-        ret = lyb_parse_subtree_r(&lybctx, NULL, tree);
-        LY_CHECK_GOTO(ret, cleanup);
-    }
-
-    /* read the last zero, parsing finished */
-    ly_in_skip(lybctx.in, 1);
-
-    /* make sure we have parsed some operation */
-    if (!lybctx.op_ntf) {
-        LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_DATA, "Missing the \"rpc\"/\"action\" node.");
-        ret = LY_EVALID;
-        goto cleanup;
-    }
-
-    if (op) {
-        *op = lybctx.op_ntf;
-    }
-    assert(*tree);
-
-cleanup:
-    LY_ARRAY_FREE(lybctx.subtrees);
-    LY_ARRAY_FREE(lybctx.models);
-    assert(!lybctx.unres_node_type.count && !lybctx.unres_meta_type.count && !lybctx.when_check.count);
-
-    if (ret) {
-        lyd_free_all(*tree);
-        *tree = NULL;
-    }
-    return ret;
+    return lyd_parse_lyb_(ctx, NULL, in, parse_options, validate_options, 0, tree_p, NULL, lydctx_p);
 }
 
 LY_ERR
-lyd_parse_lyb_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree, struct lyd_node **ntf)
+lyd_parse_lyb_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p)
 {
-    LY_ERR ret = LY_SUCCESS;
-    struct lyd_lyb_ctx lybctx = {0};
-
-    lybctx.in = in;
-    lybctx.ctx = ctx;
-    lybctx.parse_options = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
-    lybctx.int_opts = LYD_INTOPT_NOTIF;
-
-    *tree = NULL;
-    if (ntf) {
-        *ntf = NULL;
-    }
-
-    /* read magic number */
-    ret = lyb_parse_magic_number(&lybctx);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* read header */
-    ret = lyb_parse_header(&lybctx);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* read used models */
-    ret = lyb_parse_data_models(&lybctx);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* read subtree(s) */
-    while (lybctx.in->current[0]) {
-        ret = lyb_parse_subtree_r(&lybctx, NULL, tree);
-        LY_CHECK_GOTO(ret, cleanup);
-    }
-
-    /* read the last zero, parsing finished */
-    ly_in_skip(lybctx.in, 1);
-
-    /* make sure we have parsed some notification */
-    if (!lybctx.op_ntf) {
-        LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_DATA, "Missing the \"notification\" node.");
-        ret = LY_EVALID;
-        goto cleanup;
-    }
-
-    if (ntf) {
-        *ntf = lybctx.op_ntf;
-    }
-    assert(*tree);
-
-cleanup:
-    LY_ARRAY_FREE(lybctx.subtrees);
-    LY_ARRAY_FREE(lybctx.models);
-    assert(!lybctx.unres_node_type.count && !lybctx.unres_meta_type.count && !lybctx.when_check.count);
-
-    if (ret) {
-        lyd_free_all(*tree);
-        *tree = NULL;
-    }
-    return ret;
+    return lyd_parse_lyb_(ctx, NULL, in, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LYD_INTOPT_RPC, tree_p, op_p, NULL);
 }
 
 LY_ERR
-lyd_parse_lyb_reply(const struct lyd_node *request, struct ly_in *in, struct lyd_node **tree, struct lyd_node **op)
+lyd_parse_lyb_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **ntf_p)
 {
-    LY_ERR ret = LY_SUCCESS;
-    struct lyd_lyb_ctx lybctx = {0};
-    struct lyd_node *iter, *req_op, *rep_op = NULL;
+    return lyd_parse_lyb_(ctx, NULL, in, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LYD_INTOPT_NOTIF, tree_p, ntf_p, NULL);
+}
 
-    lybctx.in = in;
-    lybctx.ctx = LYD_NODE_CTX(request);
-    lybctx.parse_options = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
-    lybctx.int_opts = LYD_INTOPT_REPLY;
-
-    *tree = NULL;
-    if (op) {
-        *op = NULL;
-    }
+LY_ERR
+lyd_parse_lyb_reply(const struct lyd_node *request, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p)
+{
+    LY_ERR ret;
+    struct lyd_node *req_op, *rep_op = NULL;
+    const struct ly_ctx *ctx = LYD_NODE_CTX(request);
 
     /* find request OP */
     LYD_TREE_DFS_BEGIN((struct lyd_node *)request, req_op) {
@@ -1207,52 +1117,16 @@
     }
     if (!(req_op->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
         LOGERR(LYD_NODE_CTX(request), LY_EINVAL, "No RPC/action in the request found.");
-        ret = LY_EINVAL;
-        goto cleanup;
+        return LY_EINVAL;
     }
 
     /* duplicate request OP with parents */
-    ret = lyd_dup_single(req_op, NULL, LYD_DUP_WITH_PARENTS, &rep_op);
-    LY_CHECK_GOTO(ret, cleanup);
+    LY_CHECK_RET(lyd_dup_single(req_op, NULL, LYD_DUP_WITH_PARENTS, &rep_op));
 
-    /* read magic number */
-    ret = lyb_parse_magic_number(&lybctx);
-    LY_CHECK_GOTO(ret, cleanup);
+    ret = lyd_parse_lyb_(ctx, (struct lyd_node_inner **)&rep_op, in, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LYD_INTOPT_REPLY, tree_p, op_p, NULL);
 
-    /* read header */
-    ret = lyb_parse_header(&lybctx);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* read used models */
-    ret = lyb_parse_data_models(&lybctx);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* read subtree(s) */
-    while (lybctx.in->current[0]) {
-        ret = lyb_parse_subtree_r(&lybctx, (struct lyd_node_inner *)rep_op, NULL);
-        LY_CHECK_GOTO(ret, cleanup);
-    }
-
-    /* read the last zero, parsing finished */
-    ly_in_skip(lybctx.in, 1);
-
-    if (op) {
-        *op = rep_op;
-    }
-    for (iter = rep_op; iter->parent; iter = (struct lyd_node *)iter->parent);
-    *tree = iter;
-    rep_op = NULL;
-
-cleanup:
     lyd_free_all(rep_op);
-    LY_ARRAY_FREE(lybctx.subtrees);
-    LY_ARRAY_FREE(lybctx.models);
-    assert(!lybctx.unres_node_type.count && !lybctx.unres_meta_type.count && !lybctx.when_check.count);
 
-    if (ret) {
-        lyd_free_all(*tree);
-        *tree = NULL;
-    }
     return ret;
 }
 
@@ -1260,7 +1134,7 @@
 lyd_lyb_data_length(const char *data)
 {
     LY_ERR ret = LY_SUCCESS;
-    struct lyd_lyb_ctx lybctx = {0};
+    struct lylyb_ctx *lybctx;
     int count, i;
     size_t len;
     uint8_t buf[LYB_SIZE_MAX];
@@ -1269,53 +1143,56 @@
         return -1;
     }
 
-    ret = ly_in_new_memory(data, &lybctx.in);
+    lybctx = calloc(1, sizeof *lybctx);
+    LY_CHECK_ERR_RET(!lybctx, LOGMEM(NULL), LY_EMEM);
+    ret = ly_in_new_memory(data, &lybctx->in);
     LY_CHECK_GOTO(ret, cleanup);
 
     /* read magic number */
-    ret = lyb_parse_magic_number(&lybctx);
+    ret = lyb_parse_magic_number(lybctx);
     LY_CHECK_GOTO(ret, cleanup);
 
     /* read header */
-    ret = lyb_parse_header(&lybctx);
+    ret = lyb_parse_header(lybctx);
     LY_CHECK_GOTO(ret, cleanup);
 
     /* read model count */
-    lyb_read_number(&count, sizeof count, 2, &lybctx);
+    lyb_read_number(&count, sizeof count, 2, lybctx);
 
     /* read all models */
     for (i = 0; i < count; ++i) {
         /* module name length */
         len = 0;
-        lyb_read_number(&len, sizeof len, 2, &lybctx);
+        lyb_read_number(&len, sizeof len, 2, lybctx);
 
         /* model name */
-        lyb_read(buf, len, &lybctx);
+        lyb_read(buf, len, lybctx);
 
         /* revision */
-        lyb_read(buf, 2, &lybctx);
+        lyb_read(buf, 2, lybctx);
     }
 
-    while (lybctx.in->current[0]) {
+    while (lybctx->in->current[0]) {
         /* register a new subtree */
-        ret = lyb_read_start_subtree(&lybctx);
+        ret = lyb_read_start_subtree(lybctx);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* skip it */
-        lyb_skip_subtree(&lybctx);
+        lyb_skip_subtree(lybctx);
 
         /* subtree finished */
-        ret = lyb_read_stop_subtree(&lybctx);
+        ret = lyb_read_stop_subtree(lybctx);
         LY_CHECK_GOTO(ret, cleanup);
     }
 
     /* read the last zero, parsing finished */
-    ly_in_skip(lybctx.in, 1);
+    ly_in_skip(lybctx->in, 1);
 
 cleanup:
-    count = lybctx.in->current - lybctx.in->start;
+    count = lybctx->in->current - lybctx->in->start;
 
-    ly_in_free(lybctx.in, 0);
-    LY_ARRAY_FREE(lybctx.subtrees);
+    ly_in_free(lybctx->in, 0);
+    lylyb_ctx_free(lybctx);
+
     return ret ? -1 : count;
 }
diff --git a/src/parser_xml.c b/src/parser_xml.c
index a3c8acf..ffca41f 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -31,21 +31,37 @@
 
 /**
  * @brief Internal context for XML YANG data parser.
+ *
+ * Note that the structure maps to the lyd_ctx which is common for all the data parsers
  */
 struct lyd_xml_ctx {
-    struct lyxml_ctx *xmlctx;        /**< XML context */
+    uint32_t parse_options;        /**< various @ref dataparseroptions. */
+    uint32_t validate_options;     /**< various @ref datavalidationoptions. */
+    uint32_t int_opts;             /**< internal data parser options */
+    uint32_t path_len;             /**< used bytes in the path buffer */
+    char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
+    struct ly_set unres_node_type; /**< set of nodes validated with LY_EINCOMPLETE result */
+    struct ly_set unres_meta_type; /**< set of metadata validated with LY_EINCOMPLETE result */
+    struct ly_set when_check;      /**< set of nodes with "when" conditions */
+    struct lyd_node *op_node;      /**< if an RPC/action/notification is being parsed, store the pointer to it */
 
-    uint32_t options;                /**< various @ref dataparseroptions. */
-    uint32_t int_opts;               /**< internal data parser options */
-    uint32_t path_len;               /**< used bytes in the path buffer */
-#define LYD_PARSER_BUFSIZE 4078
-    char path[LYD_PARSER_BUFSIZE];   /**< buffer for the generated path */
-    struct ly_set unres_node_type;   /**< set of nodes validated with LY_EINCOMPLETE result */
-    struct ly_set unres_meta_type;   /**< set of metadata validated with LY_EINCOMPLETE result */
-    struct ly_set when_check;        /**< set of nodes with "when" conditions */
-    struct lyd_node *op_ntf;         /**< if an RPC/action/notification is being parsed, store the pointer to it */
+    /* callbacks */
+    lyd_ctx_free_clb free;           /* destructor */
+    ly_resolve_prefix_clb resolve_prefix;
+
+    struct lyxml_ctx *xmlctx;      /**< XML context */
 };
 
+void
+lyd_xml_ctx_free(struct lyd_ctx *lydctx)
+{
+    struct lyd_xml_ctx *ctx = (struct lyd_xml_ctx *)lydctx;
+
+    lyd_ctx_free(lydctx);
+    lyxml_ctx_free(ctx->xmlctx);
+    free(ctx);
+}
+
 /**
  * @brief XML-parser's implementation of ly_type_resolve_prefix() callback to provide mapping between prefixes used
  * in the values to the schema via XML namespaces.
@@ -65,14 +81,14 @@
 }
 
 static LY_ERR
-lydxml_metadata(struct lyxml_ctx *xmlctx, const struct lysc_node *sparent, int strict, struct ly_set *unres_meta_type,
-                struct lyd_meta **meta)
+lydxml_metadata(struct lyd_xml_ctx *lydctx, const struct lysc_node *sparent, struct lyd_meta **meta)
 {
     LY_ERR ret = LY_EVALID;
     const struct lyxml_ns *ns;
     struct lys_module *mod;
     const char *name;
     size_t name_len;
+    struct lyxml_ctx *xmlctx = lydctx->xmlctx;
 
     *meta = NULL;
 
@@ -80,7 +96,7 @@
         if (!xmlctx->prefix_len) {
             /* in XML, all attributes must be prefixed
              * TODO exception for NETCONF filters which are supposed to map to the ietf-netconf without prefix */
-            if (strict) {
+            if (lydctx->parse_options & LYD_PARSE_STRICT) {
                 LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_REFERENCE, "Missing mandatory prefix for XML metadata \"%.*s\".",
                        xmlctx->name_len, xmlctx->name);
                 goto cleanup;
@@ -104,7 +120,7 @@
         mod = ly_ctx_get_module_implemented_ns(xmlctx->ctx, ns->uri);
         if (!mod) {
             /* module is not implemented or not present in the schema */
-            if (strict) {
+            if (lydctx->parse_options & LYD_PARSE_STRICT) {
                 LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_REFERENCE,
                        "Unknown (or not implemented) YANG module with namespace \"%s\" for metadata \"%.*s%s%.*s\".",
                        ns->uri, xmlctx->prefix_len, xmlctx->prefix, xmlctx->prefix_len ? ":" : "", xmlctx->name_len,
@@ -121,15 +137,9 @@
         assert(xmlctx->status == LYXML_ATTR_CONTENT);
 
         /* create metadata */
-        ret = lyd_create_meta(NULL, meta, mod, name, name_len, xmlctx->value, xmlctx->value_len, &xmlctx->dynamic,
-                              lydxml_resolve_prefix, xmlctx, LYD_XML, sparent);
-        if (ret == LY_EINCOMPLETE) {
-            if (unres_meta_type) {
-                ly_set_add(unres_meta_type, *meta, LY_SET_OPT_USEASLIST);
-            }
-        } else if (ret) {
-            goto cleanup;
-        }
+        ret = lyd_parser_create_meta((struct lyd_ctx*)lydctx, NULL, meta, mod, name, name_len, xmlctx->value, xmlctx->value_len,
+                                     &xmlctx->dynamic, 0, lydxml_resolve_prefix, xmlctx, LYD_XML, sparent);
+        LY_CHECK_GOTO(ret, cleanup);
 
         /* next attribute */
         LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
@@ -146,12 +156,12 @@
 }
 
 static LY_ERR
-lydxml_attrs(struct lyxml_ctx *xmlctx, struct ly_attr **attr)
+lydxml_attrs(struct lyxml_ctx *xmlctx, struct lyd_attr **attr)
 {
     LY_ERR ret = LY_SUCCESS;
     const struct lyxml_ns *ns;
     struct ly_prefix *val_prefs;
-    struct ly_attr *attr2;
+    struct lyd_attr *attr2;
     const char *name, *prefix;
     size_t name_len, prefix_len;
 
@@ -189,8 +199,9 @@
         LY_CHECK_GOTO(ret = lyxml_get_prefixes(xmlctx, xmlctx->value, xmlctx->value_len, &val_prefs), cleanup);
 
         /* attr2 is always changed to the created attribute */
-        ret = ly_create_attr(NULL, &attr2, xmlctx->ctx, name, name_len, xmlctx->value, xmlctx->value_len,
-                             &xmlctx->dynamic, LYD_XML, val_prefs, prefix, prefix_len, ns ? ns->uri : NULL);
+        ret = lyd_create_attr(NULL, &attr2, xmlctx->ctx, name, name_len, xmlctx->value, xmlctx->value_len,
+                              &xmlctx->dynamic, 0, LYD_XML, val_prefs, prefix, prefix_len,
+                              ns ? ns->uri : NULL, ns ? strlen(ns->uri) : 0);
         LY_CHECK_GOTO(ret, cleanup);
 
         if (!*attr) {
@@ -307,7 +318,7 @@
 }
 
 static LY_ERR
-lydxml_data_check_schema(struct lyd_xml_ctx *lydctx, const struct lysc_node **snode)
+lydxml_data_check_opaq(struct lyd_xml_ctx *lydctx, const struct lysc_node **snode)
 {
     LY_ERR ret = LY_SUCCESS;
     enum LYXML_PARSER_STATUS prev_status;
@@ -315,40 +326,7 @@
     size_t pprefix_len, pname_len;
     struct lyxml_ctx *xmlctx = lydctx->xmlctx;
 
-    if ((lydctx->options & LYD_PARSE_NO_STATE) && ((*snode)->flags & LYS_CONFIG_R)) {
-        LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LY_VCODE_INNODE, "state", (*snode)->name);
-        return LY_EVALID;
-    }
-
-    if ((*snode)->nodetype & (LYS_RPC | LYS_ACTION)) {
-        if (lydctx->int_opts & LYD_INTOPT_RPC) {
-            if (lydctx->op_ntf) {
-                LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
-                       lys_nodetype2str((*snode)->nodetype), (*snode)->name,
-                       lys_nodetype2str(lydctx->op_ntf->schema->nodetype), lydctx->op_ntf->schema->name);
-                return LY_EVALID;
-            }
-        } else {
-            LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_DATA, "Unexpected %s element \"%s\".",
-                   lys_nodetype2str((*snode)->nodetype), (*snode)->name);
-            return LY_EVALID;
-        }
-    } else if ((*snode)->nodetype == LYS_NOTIF) {
-        if (lydctx->int_opts & LYD_INTOPT_NOTIF) {
-            if (lydctx->op_ntf) {
-                LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
-                       lys_nodetype2str((*snode)->nodetype), (*snode)->name,
-                       lys_nodetype2str(lydctx->op_ntf->schema->nodetype), lydctx->op_ntf->schema->name);
-                return LY_EVALID;
-            }
-        } else {
-            LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_DATA, "Unexpected %s element \"%s\".",
-                   lys_nodetype2str((*snode)->nodetype), (*snode)->name);
-            return LY_EVALID;
-        }
-    }
-
-    if ((lydctx->options & LYD_PARSE_OPAQ) && ((*snode)->nodetype & (LYD_NODE_TERM | LYS_LIST))) {
+    if ((lydctx->parse_options & LYD_PARSE_OPAQ) && ((*snode)->nodetype & (LYD_NODE_TERM | LYS_LIST))) {
         /* backup parser */
         prev_status = xmlctx->status;
         pprefix = xmlctx->prefix;
@@ -416,7 +394,7 @@
     const struct ly_ctx *ctx;
     const struct lyxml_ns *ns;
     struct lyd_meta *meta = NULL, *m;
-    struct ly_attr *attr = NULL, *a;
+    struct lyd_attr *attr = NULL, *a;
     const struct lysc_node *snode;
     struct lys_module *mod;
     uint32_t prev_opts;
@@ -446,13 +424,13 @@
         }
         mod = ly_ctx_get_module_implemented_ns(ctx, ns->uri);
         if (!mod) {
-            if (lydctx->options & LYD_PARSE_STRICT) {
+            if (lydctx->parse_options & LYD_PARSE_STRICT) {
                 LOGVAL(ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_REFERENCE, "No module with namespace \"%s\" in the context.",
                        ns->uri);
                 ret = LY_EVALID;
                 goto cleanup;
             }
-            if (!(lydctx->options & LYD_PARSE_OPAQ)) {
+            if (!(lydctx->parse_options & LYD_PARSE_OPAQ)) {
                 /* skip element with children */
                 LY_CHECK_GOTO(ret = lydxml_data_skip(xmlctx), cleanup);
                 continue;
@@ -467,29 +445,30 @@
         if (mod && (!parent || parent->schema)) {
             snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
             if (!snode) {
-                if (lydctx->options & LYD_PARSE_STRICT) {
+                if (lydctx->parse_options & LYD_PARSE_STRICT) {
                     LOGVAL(ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_REFERENCE, "Element \"%.*s\" not found in the \"%s\" module.",
                         name_len, name, mod->name);
                     ret = LY_EVALID;
                     goto cleanup;
-                } else if (!(lydctx->options & LYD_PARSE_OPAQ)) {
+                } else if (!(lydctx->parse_options & LYD_PARSE_OPAQ)) {
                     /* skip element with children */
                     LY_CHECK_GOTO(ret = lydxml_data_skip(xmlctx), cleanup);
                     continue;
                 }
             } else {
                 /* check that schema node is valid and can be used */
-                LY_CHECK_GOTO(ret = lydxml_data_check_schema(lydctx, &snode), cleanup);
+                LY_CHECK_GOTO(ret = lyd_parser_check_schema((struct lyd_ctx *)lydctx, snode), cleanup);
+                LY_CHECK_GOTO(ret = lydxml_data_check_opaq(lydctx, &snode), cleanup);
             }
         }
 
         /* create metadata/attributes */
         if (xmlctx->status == LYXML_ATTRIBUTE) {
             if (snode) {
-                ret = lydxml_metadata(xmlctx, snode, lydctx->options & LYD_PARSE_STRICT, &lydctx->unres_meta_type, &meta);
+                ret = lydxml_metadata(lydctx, snode, &meta);
                 LY_CHECK_GOTO(ret, cleanup);
             } else {
-                assert(lydctx->options & LYD_PARSE_OPAQ);
+                assert(lydctx->parse_options & LYD_PARSE_OPAQ);
                 ret = lydxml_attrs(xmlctx, &attr);
                 LY_CHECK_GOTO(ret, cleanup);
             }
@@ -497,7 +476,7 @@
 
         assert(xmlctx->status == LYXML_ELEM_CONTENT);
         if (!snode) {
-            assert(lydctx->options & LYD_PARSE_OPAQ);
+            assert(lydctx->parse_options & LYD_PARSE_OPAQ);
 
             if (xmlctx->ws_only) {
                 /* ignore WS-only value */
@@ -510,8 +489,8 @@
             }
 
             /* create node */
-            ret = lyd_create_opaq(ctx, name, name_len, xmlctx->value, xmlctx->value_len, &xmlctx->dynamic, LYD_XML,
-                                  val_prefs, prefix, prefix_len, ns->uri, &cur);
+            ret = lyd_create_opaq(ctx, name, name_len, xmlctx->value, xmlctx->value_len, &xmlctx->dynamic, 0, LYD_XML,
+                                  val_prefs, prefix, prefix_len, ns->uri, strlen(ns->uri), &cur);
             LY_CHECK_GOTO(ret, cleanup);
 
             /* parser next */
@@ -524,21 +503,14 @@
             }
         } else if (snode->nodetype & LYD_NODE_TERM) {
             /* create node */
-            ret = lyd_create_term(snode, xmlctx->value, xmlctx->value_len, &xmlctx->dynamic, lydxml_resolve_prefix,
-                                  xmlctx, LYD_XML, &cur);
-            if (ret == LY_EINCOMPLETE) {
-                if (!(lydctx->options & LYD_PARSE_ONLY)) {
-                    ly_set_add(&lydctx->unres_node_type, cur, LY_SET_OPT_USEASLIST);
-                }
-            } else if (ret) {
-                goto cleanup;
-            }
+            LY_CHECK_GOTO(ret = lyd_parser_create_term((struct lyd_ctx*)lydctx, snode, xmlctx->value, xmlctx->value_len, &xmlctx->dynamic, 0,
+                                                       lydxml_resolve_prefix, xmlctx, LYD_XML, &cur), cleanup);
 
             if (parent && (cur->schema->flags & LYS_KEY)) {
                 /* check the key order, the anchor must never be a key */
                 anchor = lyd_insert_get_next_anchor(parent->child, cur);
                 if (anchor && (anchor->schema->flags & LYS_KEY)) {
-                    if (lydctx->options & LYD_PARSE_STRICT) {
+                    if (lydctx->parse_options & LYD_PARSE_STRICT) {
                         LOGVAL(ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_DATA, "Invalid position of the key \"%s\" in a list.",
                                 cur->schema->name);
                         ret = LY_EVALID;
@@ -586,21 +558,20 @@
                 LY_CHECK_GOTO(ret = lyd_parse_check_keys(cur), cleanup);
             }
 
-            if (!(lydctx->options & LYD_PARSE_ONLY)) {
+            if (!(lydctx->parse_options & LYD_PARSE_ONLY)) {
                 /* new node validation, autodelete CANNOT occur, all nodes are new */
                 ret = lyd_validate_new(lyd_node_children_p(cur), snode, NULL, NULL);
                 LY_CHECK_GOTO(ret, cleanup);
 
                 /* add any missing default children */
-                ret = lyd_new_implicit_r(cur, lyd_node_children_p(cur), NULL, NULL, &lydctx->unres_node_type,
-                                         &lydctx->when_check, (lydctx->options & LYD_VALIDATE_NO_STATE)
-                                            ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+                ret = lyd_new_implicit_r(cur, lyd_node_children_p(cur), NULL, NULL, &lydctx->unres_node_type, &lydctx->when_check,
+                                         (lydctx->validate_options & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
                 LY_CHECK_GOTO(ret, cleanup);
             }
 
             if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
                 /* rememeber the RPC/action/notification */
-                lydctx->op_ntf = cur;
+                lydctx->op_node = cur;
             }
         } else if (snode->nodetype & LYD_NODE_ANY) {
             if (!xmlctx->ws_only) {
@@ -615,12 +586,12 @@
             LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
 
             /* parse any data tree with correct options */
-            prev_opts = lydctx->options;
-            lydctx->options &= ~LYD_PARSE_STRICT;
-            lydctx->options |= LYD_PARSE_OPAQ;
+            prev_opts = lydctx->parse_options;
+            lydctx->parse_options &= ~LYD_PARSE_STRICT;
+            lydctx->parse_options |= LYD_PARSE_OPAQ;
             anchor = NULL;
             ret = lydxml_data_r(lydctx, NULL, &anchor);
-            lydctx->options = prev_opts;
+            lydctx->parse_options = prev_opts;
             LY_CHECK_GOTO(ret, cleanup);
 
             /* create node */
@@ -631,7 +602,7 @@
 
         /* add/correct flags */
         if (snode) {
-            lyd_parse_set_data_flags(cur, &lydctx->when_check, &meta, lydctx->options);
+            lyd_parse_set_data_flags(cur, &lydctx->when_check, &meta, lydctx->parse_options);
         }
 
         /* add metadata/attributes */
@@ -677,73 +648,37 @@
 }
 
 LY_ERR
-lyd_parse_xml_data(const struct ly_ctx *ctx, struct ly_in *in, int parse_options, int validate_options, struct lyd_node **tree)
+lyd_parse_xml_data(const struct ly_ctx *ctx, struct ly_in *in, int parse_options, int validate_options, struct lyd_node **tree_p, struct lyd_ctx **lydctx_p)
 {
     LY_ERR ret = LY_SUCCESS;
-    struct lyd_xml_ctx lydctx = {0};
-    uint32_t i = 0;
-    const struct lys_module *mod;
-    struct lyd_node *first, *next, **first2;
+    struct lyd_xml_ctx *lydctx;
 
     assert(!(parse_options & ~LYD_PARSE_OPTS_MASK));
     assert(!(validate_options & ~LYD_VALIDATE_OPTS_MASK));
 
-    /* init context and tree */
-    LY_CHECK_GOTO(ret = lyxml_ctx_new(ctx, in, &lydctx.xmlctx), cleanup);
-    lydctx.options = parse_options;
-    *tree = NULL;
+    /* init context */
+    lydctx = calloc(1, sizeof *lydctx);
+    LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM);
+    LY_CHECK_GOTO(ret = lyxml_ctx_new(ctx, in, &lydctx->xmlctx), cleanup);
+    lydctx->parse_options = parse_options;
+    lydctx->validate_options = validate_options;
+    lydctx->free = lyd_xml_ctx_free;
+    lydctx->resolve_prefix = lydxml_resolve_prefix;
 
     /* parse XML data */
-    LY_CHECK_GOTO(ret = lydxml_data_r(&lydctx, NULL, tree), cleanup);
-
-    if (!(parse_options & LYD_PARSE_ONLY)) {
-        next = *tree;
-        while (1) {
-            if (validate_options & LYD_VALIDATE_PRESENT) {
-                mod = lyd_data_next_module(&next, &first);
-            } else {
-                mod = lyd_mod_next_module(next, NULL, ctx, &i, &first);
-            }
-            if (!mod) {
-                break;
-            }
-            if (first == *tree) {
-                /* make sure first2 changes are carried to tree */
-                first2 = tree;
-            } else {
-                first2 = &first;
-            }
-
-            /* validate new top-level nodes, autodelete CANNOT occur, all nodes are new */
-            LY_CHECK_GOTO(ret = lyd_validate_new(first2, NULL, mod, NULL), cleanup);
-
-            /* add all top-level defaults for this module */
-            ret = lyd_new_implicit_r(NULL, first2, NULL, mod, &lydctx.unres_node_type, &lydctx.when_check,
-                                     validate_options & LYD_VALIDATE_NO_STATE ? LYD_IMPLICIT_NO_STATE : 0, NULL);
-            LY_CHECK_GOTO(ret, cleanup);
-
-            /* finish incompletely validated terminal values/attributes and when conditions */
-            ret = lyd_validate_unres(tree, &lydctx.when_check, &lydctx.unres_node_type, &lydctx.unres_meta_type, LYD_XML,
-                                     lydxml_resolve_prefix, lydctx.xmlctx, NULL);
-            LY_CHECK_GOTO(ret, cleanup);
-
-            /* perform final validation that assumes the data tree is final */
-            LY_CHECK_GOTO(ret = lyd_validate_final_r(*first2, NULL, mod, validate_options, 0), cleanup);
-        }
-    }
+    LY_CHECK_GOTO(ret = lydxml_data_r(lydctx, NULL, tree_p), cleanup);
 
 cleanup:
     /* there should be no unresolved types stored */
-    assert(!(parse_options & LYD_PARSE_ONLY) || (!lydctx.unres_node_type.count && !lydctx.unres_meta_type.count
-           && !lydctx.when_check.count));
+    assert(!(parse_options & LYD_PARSE_ONLY) || (!lydctx->unres_node_type.count && !lydctx->unres_meta_type.count
+           && !lydctx->when_check.count));
 
-    ly_set_erase(&lydctx.unres_node_type, NULL);
-    ly_set_erase(&lydctx.unres_meta_type, NULL);
-    ly_set_erase(&lydctx.when_check, NULL);
-    lyxml_ctx_free(lydctx.xmlctx);
     if (ret) {
-        lyd_free_all(*tree);
-        *tree = NULL;
+        lyd_xml_ctx_free((struct lyd_ctx *)lydctx);
+        lyd_free_all(*tree_p);
+        *tree_p = NULL;
+    } else {
+        *lydctx_p = (struct lyd_ctx *)lydctx;
     }
     return ret;
 }
@@ -753,7 +688,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     const struct lyxml_ns *ns = NULL;
-    struct ly_attr *attr = NULL;
+    struct lyd_attr *attr = NULL;
     const char *prefix;
     size_t prefix_len;
 
@@ -795,7 +730,8 @@
     LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
 
     /* create node */
-    ret = lyd_create_opaq(xmlctx->ctx, name, strlen(name), "", 0, NULL, LYD_XML, NULL, prefix, prefix_len, uri, envp);
+    ret = lyd_create_opaq(xmlctx->ctx, name, strlen(name), "", 0, NULL, 0, LYD_XML, NULL, prefix, prefix_len,
+                          uri, strlen(uri), envp);
     LY_CHECK_GOTO(ret, cleanup);
 
     /* assign atributes */
@@ -808,20 +744,17 @@
 }
 
 LY_ERR
-lyd_parse_xml_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree, struct lyd_node **op)
+lyd_parse_xml_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyd_xml_ctx lydctx = {0};
     struct lyd_node *rpc_e = NULL, *act_e = NULL;
+    struct lyd_node *tree = NULL;
 
     /* init */
     LY_CHECK_GOTO(ret = lyxml_ctx_new(ctx, in, &lydctx.xmlctx), cleanup);
-    lydctx.options = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
+    lydctx.parse_options = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
     lydctx.int_opts = LYD_INTOPT_RPC;
-    *tree = NULL;
-    if (op) {
-        *op = NULL;
-    }
 
     /* parse "rpc", if any */
     LY_CHECK_GOTO(ret = lydxml_envelope(lydctx.xmlctx, "rpc", "urn:ietf:params:xml:ns:netconf:base:1.0", &rpc_e), cleanup);
@@ -832,10 +765,10 @@
     }
 
     /* parse the rest of data normally */
-    LY_CHECK_GOTO(ret = lydxml_data_r(&lydctx, NULL, tree), cleanup);
+    LY_CHECK_GOTO(ret = lydxml_data_r(&lydctx, NULL, &tree), cleanup);
 
     /* make sure we have parsed some operation */
-    if (!lydctx.op_ntf) {
+    if (!lydctx.op_node) {
         LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_DATA, "Missing the \"rpc\"/\"action\" node.");
         ret = LY_EVALID;
         goto cleanup;
@@ -849,9 +782,9 @@
                    lydctx.xmlctx->name_len, lydctx.xmlctx->name);
             ret = LY_EVALID;
             goto cleanup;
-        } else if (lydctx.op_ntf->schema->nodetype != LYS_ACTION) {
-            LOGVAL(ctx, LY_VLOG_LYD, lydctx.op_ntf, LYVE_DATA, "Unexpected %s element, an \"action\" expected.",
-                   lys_nodetype2str(lydctx.op_ntf->schema->nodetype));
+        } else if (lydctx.op_node->schema->nodetype != LYS_ACTION) {
+            LOGVAL(ctx, LY_VLOG_LYD, lydctx.op_node, LYVE_DATA, "Unexpected %s element, an \"action\" expected.",
+                   lys_nodetype2str(lydctx.op_node->schema->nodetype));
             ret = LY_EVALID;
             goto cleanup;
         }
@@ -864,28 +797,35 @@
                    lydctx.xmlctx->name_len, lydctx.xmlctx->name);
             ret = LY_EVALID;
             goto cleanup;
-        } else if (!act_e && (lydctx.op_ntf->schema->nodetype != LYS_RPC)) {
-            LOGVAL(ctx, LY_VLOG_LYD, lydctx.op_ntf, LYVE_DATA, "Unexpected %s element, an \"rpc\" expected.",
-                   lys_nodetype2str(lydctx.op_ntf->schema->nodetype));
+        } else if (!act_e && (lydctx.op_node->schema->nodetype != LYS_RPC)) {
+            LOGVAL(ctx, LY_VLOG_LYD, lydctx.op_node, LYVE_DATA, "Unexpected %s element, an \"rpc\" expected.",
+                   lys_nodetype2str(lydctx.op_node->schema->nodetype));
             ret = LY_EVALID;
             goto cleanup;
         }
         LY_CHECK_GOTO(ret = lyxml_ctx_next(lydctx.xmlctx), cleanup);
     }
 
-    if (op) {
-        *op = lydctx.op_ntf;
+    if (op_p) {
+        *op_p = lydctx.op_node;
     }
-    assert(*tree);
+    assert(tree);
+    if (tree_p) {
+        *tree_p = tree;
+    }
     if (act_e) {
         /* connect to the action */
-        lyd_insert_node(act_e, NULL, *tree);
-        *tree = act_e;
+        lyd_insert_node(act_e, NULL, *tree_p);
+        if (tree_p) {
+            *tree_p = act_e;
+        }
     }
     if (rpc_e) {
         /* connect to the rpc */
-        lyd_insert_node(rpc_e, NULL, *tree);
-        *tree = rpc_e;
+        lyd_insert_node(rpc_e, NULL, *tree_p);
+        if (tree_p) {
+            *tree_p = rpc_e;
+        }
     }
 
 cleanup:
@@ -893,10 +833,9 @@
     assert(!lydctx.unres_node_type.count && !lydctx.unres_meta_type.count && !lydctx.when_check.count);
     lyxml_ctx_free(lydctx.xmlctx);
     if (ret) {
-        lyd_free_all(*tree);
+        lyd_free_all(tree);
         lyd_free_tree(act_e);
         lyd_free_tree(rpc_e);
-        *tree = NULL;
     }
     return ret;
 }
@@ -906,7 +845,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     const struct lyxml_ns *ns = NULL;
-    struct ly_attr *attr = NULL;
+    struct lyd_attr *attr = NULL;
     struct lyd_node *et;
     const char *prefix;
     size_t prefix_len;
@@ -961,8 +900,8 @@
     }*/
 
     /* create node */
-    ret = lyd_create_opaq(xmlctx->ctx, "eventTime", 9, xmlctx->value, xmlctx->value_len, NULL, LYD_XML, NULL,
-                          prefix, prefix_len, ns->uri, &et);
+    ret = lyd_create_opaq(xmlctx->ctx, "eventTime", 9, xmlctx->value, xmlctx->value_len, NULL, 0, LYD_XML, NULL,
+                          prefix, prefix_len, ns->uri, strlen(ns->uri), &et);
     LY_CHECK_GOTO(ret, cleanup);
 
     /* assign atributes */
@@ -992,29 +931,26 @@
 }
 
 LY_ERR
-lyd_parse_xml_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree, struct lyd_node **ntf)
+lyd_parse_xml_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **ntf_p)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyd_xml_ctx lydctx = {0};
     struct lyd_node *ntf_e = NULL;
+    struct lyd_node *tree = NULL;
 
     /* init */
     LY_CHECK_GOTO(ret = lyxml_ctx_new(ctx, in, &lydctx.xmlctx), cleanup);
-    lydctx.options = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
+    lydctx.parse_options = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
     lydctx.int_opts = LYD_INTOPT_NOTIF;
-    *tree = NULL;
-    if (ntf) {
-        *ntf = NULL;
-    }
 
     /* parse "notification" and "eventTime", if present */
     LY_CHECK_GOTO(ret = lydxml_notif_envelope(lydctx.xmlctx, &ntf_e), cleanup);
 
     /* parse the rest of data normally */
-    LY_CHECK_GOTO(ret = lydxml_data_r(&lydctx, NULL, tree), cleanup);
+    LY_CHECK_GOTO(ret = lydxml_data_r(&lydctx, NULL, &tree), cleanup);
 
     /* make sure we have parsed some notification */
-    if (!lydctx.op_ntf) {
+    if (!lydctx.op_node) {
         LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_DATA, "Missing the \"notification\" node.");
         ret = LY_EVALID;
         goto cleanup;
@@ -1032,14 +968,19 @@
         LY_CHECK_GOTO(ret = lyxml_ctx_next(lydctx.xmlctx), cleanup);
     }
 
-    if (ntf) {
-        *ntf = lydctx.op_ntf;
+    if (ntf_p) {
+        *ntf_p = lydctx.op_node;
     }
-    assert(*tree);
+    assert(tree);
+    if (tree_p) {
+        *tree_p = tree;
+    }
     if (ntf_e) {
         /* connect to the notification */
-        lyd_insert_node(ntf_e, NULL, *tree);
-        *tree = ntf_e;
+        lyd_insert_node(ntf_e, NULL, *tree_p);
+        if (tree_p) {
+            *tree_p = ntf_e;
+        }
     }
 
 cleanup:
@@ -1047,15 +988,14 @@
     assert(!lydctx.unres_node_type.count && !lydctx.unres_meta_type.count && !lydctx.when_check.count);
     lyxml_ctx_free(lydctx.xmlctx);
     if (ret) {
-        lyd_free_all(*tree);
+        lyd_free_all(tree);
         lyd_free_tree(ntf_e);
-        *tree = NULL;
     }
     return ret;
 }
 
 LY_ERR
-lyd_parse_xml_reply(const struct lyd_node *request, struct ly_in *in, struct lyd_node **tree, struct lyd_node **op)
+lyd_parse_xml_reply(const struct lyd_node *request, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyd_xml_ctx lydctx = {0};
@@ -1063,12 +1003,8 @@
 
     /* init */
     LY_CHECK_GOTO(ret = lyxml_ctx_new(LYD_NODE_CTX(request), in, &lydctx.xmlctx), cleanup);
-    lydctx.options = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
+    lydctx.parse_options = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
     lydctx.int_opts = LYD_INTOPT_REPLY;
-    *tree = NULL;
-    if (op) {
-        *op = NULL;
-    }
 
     /* find request OP */
     LYD_TREE_DFS_BEGIN((struct lyd_node *)request, req_op) {
@@ -1105,15 +1041,19 @@
         LY_CHECK_GOTO(ret = lyxml_ctx_next(lydctx.xmlctx), cleanup);
     }
 
-    if (op) {
-        *op = rep_op;
+    if (op_p) {
+        *op_p = rep_op;
     }
     for (iter = rep_op; iter->parent; iter = (struct lyd_node *)iter->parent);
-    *tree = iter;
+    if (tree_p) {
+        *tree_p = iter;
+    }
     if (rpcr_e) {
         /* connect to the operation */
-        lyd_insert_node(rpcr_e, NULL, *tree);
-        *tree = rpcr_e;
+        lyd_insert_node(rpcr_e, NULL, *tree_p);
+        if (tree_p) {
+            *tree_p = rpcr_e;
+        }
     }
 
 cleanup:
diff --git a/src/path.c b/src/path.c
index 3a299ae..5f30e94 100644
--- a/src/path.c
+++ b/src/path.c
@@ -364,7 +364,7 @@
 static LY_ERR
 ly_path_compile_prefix(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod,
                        const struct lysc_node *prev_ctx_node, const struct lyxp_expr *expr, uint16_t tok_idx,
-                       uint8_t lref, ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format,
+                       uint8_t lref, ly_resolve_prefix_clb resolve_prefix, void *prefix_data, LYD_FORMAT format,
                        const struct lys_module **mod, const char **name, size_t *name_len)
 {
     const char *ptr;
@@ -423,7 +423,7 @@
 LY_ERR
 ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod,
                           const struct lysc_node *ctx_node, const struct lyxp_expr *expr, uint16_t *tok_idx,
-                          ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format,
+                          ly_resolve_prefix_clb resolve_prefix, void *prefix_data, LYD_FORMAT format,
                           struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type)
 {
     struct ly_path_predicate *p;
@@ -574,7 +574,7 @@
  */
 static LY_ERR
 ly_path_compile_predicate_leafref(const struct lysc_node *ctx_node, const struct lysc_node *cur_node,
-                                  const struct lyxp_expr *expr, uint16_t *tok_idx, ly_clb_resolve_prefix resolve_prefix,
+                                  const struct lyxp_expr *expr, uint16_t *tok_idx, ly_resolve_prefix_clb resolve_prefix,
                                   void *prefix_data, LYD_FORMAT format)
 {
     const struct lysc_node *key, *node, *node2;
@@ -694,7 +694,7 @@
 LY_ERR
 ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
                 const struct lyxp_expr *expr, uint8_t lref, uint8_t oper, uint8_t target,
-                ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format, struct ly_path **path)
+                ly_resolve_prefix_clb resolve_prefix, void *prefix_data, LYD_FORMAT format, struct ly_path **path)
 {
     LY_ERR ret = LY_SUCCESS;
     uint16_t tok_idx = 0;
diff --git a/src/path.h b/src/path.h
index 80085a6..9faa0e1 100644
--- a/src/path.h
+++ b/src/path.h
@@ -160,7 +160,7 @@
  */
 LY_ERR ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
                        const struct lyxp_expr *expr, uint8_t lref, uint8_t oper, uint8_t target,
-                       ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format, struct ly_path **path);
+                       ly_resolve_prefix_clb resolve_prefix, void *prefix_data, LYD_FORMAT format, struct ly_path **path);
 
 /**
  * @brief Compile predicate into ly_path_predicate structure. Only simple predicates (not leafref) are supported.
@@ -180,7 +180,7 @@
  */
 LY_ERR ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod,
                                  const struct lysc_node *ctx_node, const struct lyxp_expr *expr, uint16_t *tok_idx,
-                                 ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format,
+                                 ly_resolve_prefix_clb resolve_prefix, void *prefix_data, LYD_FORMAT format,
                                  struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type);
 
 /**
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 2b64a20..e6e4e36 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -57,7 +57,7 @@
  */
 static const char *
 ly_type_print_canonical(const struct lyd_value *value, LYD_FORMAT UNUSED(format),
-                        ly_clb_get_prefix UNUSED(get_prefix), void *UNUSED(printer), int *dynamic)
+                        ly_get_prefix_clb UNUSED(get_prefix), void *UNUSED(printer), int *dynamic)
 {
     *dynamic = 0;
     return (char*)value->canonical_cache;
@@ -161,7 +161,7 @@
 }
 
 API struct lyd_value_prefix *
-ly_type_get_prefixes(const struct ly_ctx *ctx, const char *value, size_t value_len, ly_clb_resolve_prefix resolve_prefix,
+ly_type_get_prefixes(const struct ly_ctx *ctx, const char *value, size_t value_len, ly_resolve_prefix_clb resolve_prefix,
                      void *parser)
 {
     LY_ERR ret;
@@ -524,7 +524,7 @@
  */
 static LY_ERR
 ly_type_store_int(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                  ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                  ly_resolve_prefix_clb UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                   const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                   struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
@@ -599,7 +599,7 @@
  */
 static LY_ERR
 ly_type_store_uint(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                   ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                   ly_resolve_prefix_clb UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                    const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                    struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
@@ -673,7 +673,7 @@
  */
 static LY_ERR
 ly_type_store_decimal64(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                        ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                        ly_resolve_prefix_clb UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                         const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                         struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
@@ -757,7 +757,7 @@
  */
 static LY_ERR
 ly_type_store_binary(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                     ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                     ly_resolve_prefix_clb UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                      const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                      struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
@@ -867,7 +867,7 @@
  */
 static LY_ERR
 ly_type_store_string(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                     ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                     ly_resolve_prefix_clb UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                      const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                      struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
@@ -911,7 +911,7 @@
  */
 static LY_ERR
 ly_type_store_bits(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                   ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                   ly_resolve_prefix_clb UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                    const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                    struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
@@ -1100,7 +1100,7 @@
  */
 static LY_ERR
 ly_type_store_enum(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                   ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                   ly_resolve_prefix_clb UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                    const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                    struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
@@ -1180,7 +1180,7 @@
  */
 static LY_ERR
 ly_type_store_boolean(const struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
-                      ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                      ly_resolve_prefix_clb UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                       const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                       struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
@@ -1240,7 +1240,7 @@
  */
 static LY_ERR
 ly_type_store_empty(const struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
-                    ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                    ly_resolve_prefix_clb UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                     const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                     struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
@@ -1305,7 +1305,7 @@
  */
 static LY_ERR
 ly_type_store_identityref(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                          ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT UNUSED(format),
+                          ly_resolve_prefix_clb resolve_prefix, void *parser, LYD_FORMAT UNUSED(format),
                           const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                           struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
@@ -1426,7 +1426,7 @@
  * Implementation of the ly_type_print_clb.
  */
 static const char *
-ly_type_print_identityref(const struct lyd_value *value, LYD_FORMAT UNUSED(format), ly_clb_get_prefix get_prefix, void *printer, int *dynamic)
+ly_type_print_identityref(const struct lyd_value *value, LYD_FORMAT UNUSED(format), ly_get_prefix_clb get_prefix, void *printer, int *dynamic)
 {
     char *result = NULL;
 
@@ -1453,7 +1453,7 @@
  * @brief Helper function for ly_type_store_instanceid_parse_predicate_value() to provide prefix mapping for the
  * validation callbacks for the values used in instance-identifier predicates.
  *
- * Implementation of the ly_clb_resolve_prefix.
+ * Implementation of the ly_resolve_prefix_clb.
  */
 static const struct lys_module *
 ly_type_stored_prefixes_clb(const struct ly_ctx *UNUSED(ctx), const char *prefix, size_t prefix_len, void *private)
@@ -1476,7 +1476,7 @@
  */
 static LY_ERR
 ly_type_store_instanceid(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                         ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
+                         ly_resolve_prefix_clb resolve_prefix, void *parser, LYD_FORMAT format,
                          const void *context_node, const struct lyd_node *tree, struct lyd_value *storage,
                          const char **canonized, struct ly_err_item **err)
 {
@@ -1490,6 +1490,7 @@
     struct lyxp_expr *exp = NULL;
     const struct lysc_node *ctx_scnode;
     int erc = 0;
+    int prefix_opt = 0;
 
     /* init */
     *err = NULL;
@@ -1504,6 +1505,17 @@
         goto cleanup;
     }
 
+    switch (format) {
+    case LYD_SCHEMA:
+    case LYD_XML:
+        prefix_opt = LY_PATH_PREFIX_MANDATORY;
+        break;
+    case LYD_JSON:
+    case LYD_LYB:
+        prefix_opt = LY_PATH_PREFIX_STRICT_INHERIT;
+        break;
+    }
+
     if (!(options & LY_TYPE_OPTS_SCHEMA) && (options & LY_TYPE_OPTS_SECOND_CALL) && (options & LY_TYPE_OPTS_STORE)) {
         /* the second run in data tree, the first one ended with LY_EINCOMPLETE, but we have prepared the target structure */
         if (ly_path_eval(storage->target, tree, NULL)) {
@@ -1528,7 +1540,7 @@
 
     /* parse the value */
     ret = ly_path_parse(ctx, ctx_scnode, value, value_len, LY_PATH_BEGIN_ABSOLUTE, LY_PATH_LREF_FALSE,
-                        LY_PATH_PREFIX_MANDATORY, LY_PATH_PRED_SIMPLE, &exp);
+                        prefix_opt, LY_PATH_PRED_SIMPLE, &exp);
     if (ret) {
         erc = asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - syntax error.", (int)value_len, value);
         goto error;
@@ -1670,7 +1682,7 @@
  * Implementation of the ly_type_print_clb.
  */
 static const char *
-ly_type_print_instanceid(const struct lyd_value *value, LYD_FORMAT format, ly_clb_get_prefix get_prefix, void *printer,
+ly_type_print_instanceid(const struct lyd_value *value, LYD_FORMAT format, ly_get_prefix_clb get_prefix, void *printer,
                          int *dynamic)
 {
     LY_ARRAY_COUNT_TYPE u, v;
@@ -1882,7 +1894,7 @@
  */
 static LY_ERR
 ly_type_store_leafref(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                      ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
+                      ly_resolve_prefix_clb resolve_prefix, void *parser, LYD_FORMAT format,
                       const void *context_node, const struct lyd_node *tree,
                       struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
@@ -1990,7 +2002,7 @@
  * Implementation of the ly_type_print_clb.
  */
 static const char *
-ly_type_print_leafref(const struct lyd_value *value, LYD_FORMAT format, ly_clb_get_prefix get_prefix, void *printer, int *dynamic)
+ly_type_print_leafref(const struct lyd_value *value, LYD_FORMAT format, ly_get_prefix_clb get_prefix, void *printer, int *dynamic)
 {
     return value->realtype->plugin->print(value, format, get_prefix, printer, dynamic);
 }
@@ -2023,13 +2035,62 @@
 }
 
 /**
+ * @brief Answer if the type is suitable for the parser's hit (if any) in the specified format
+ */
+LY_ERR
+type_check_parser_hint(LYD_FORMAT format, int hint, LY_DATA_TYPE type)
+{
+    if (format == LYD_JSON && hint) {
+        switch (type) {
+        case LY_TYPE_UINT8:
+        case LY_TYPE_UINT16:
+        case LY_TYPE_UINT32:
+        case LY_TYPE_INT8:
+        case LY_TYPE_INT16:
+        case LY_TYPE_INT32:
+            if (hint != LY_TYPE_OPTS_ISNUMBER) {
+                return LY_ENOT;
+            }
+            break;
+        case LY_TYPE_STRING:
+        case LY_TYPE_UINT64:
+        case LY_TYPE_INT64:
+        case LY_TYPE_DEC64:
+        case LY_TYPE_ENUM:
+        case LY_TYPE_BITS:
+        case LY_TYPE_BINARY:
+        case LY_TYPE_IDENT:
+        case LY_TYPE_INST:
+            if (hint != LY_TYPE_OPTS_ISSTRING) {
+                return LY_ENOT;
+            }
+            break;
+        case LY_TYPE_BOOL:
+            if (hint != LY_TYPE_OPTS_ISBOOLEAN) {
+                return LY_ENOT;
+            }
+            break;
+        case LY_TYPE_EMPTY:
+            if (hint != LY_TYPE_OPTS_ISEMPTY) {
+                return LY_ENOT;
+            }
+            break;
+        default:
+            LOGINT_RET(NULL);
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
  * @brief Validate, canonize and store value of the YANG built-in union type.
  *
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
 ly_type_store_union(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                    ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
+                    ly_resolve_prefix_clb resolve_prefix, void *parser, LYD_FORMAT format,
                     const void *context_node, const struct lyd_node *tree,
                     struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
@@ -2070,11 +2131,19 @@
         /* store prefixes for later use */
         subvalue->prefixes = ly_type_get_prefixes(ctx, value, value_len, resolve_prefix, parser);
 
+        /* remember the hint options */
+        subvalue->parser_hint = options & LY_TYPE_PARSER_HINTS_MASK;
+
 search_subtype:
         /* use the first usable sybtype to store the value */
         LY_ARRAY_FOR(type_u->types, u) {
             subvalue->value->realtype = type_u->types[u];
 
+            if (type_check_parser_hint(format, subvalue->parser_hint, subvalue->value->realtype->basetype)) {
+                /* not a suitable type */
+                continue;
+            }
+
             /* turn logging off */
             prev_lo = ly_log_options(0);
             ret = type_u->types[u]->plugin->store(ctx, type_u->types[u], value, value_len, options & ~LY_TYPE_OPTS_DYNAMIC,
@@ -2154,7 +2223,7 @@
  * Implementation of the ly_type_print_clb.
  */
 static const char *
-ly_type_print_union(const struct lyd_value *value, LYD_FORMAT format, ly_clb_get_prefix get_prefix, void *printer, int *dynamic)
+ly_type_print_union(const struct lyd_value *value, LYD_FORMAT format, ly_get_prefix_clb get_prefix, void *printer, int *dynamic)
 {
     return value->subvalue->value->realtype->plugin->print(value->subvalue->value, format, get_prefix, printer, dynamic);
 }
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 62d2d38..eaf449f 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -155,10 +155,19 @@
                                             can (e.g. store the canonical/auxiliary value if it is requested) and in the case of need to use
                                             data trees (checking require-instance), it returns LY_EINCOMPLETE.
                                             Caller is supposed to call such validation callback again later with complete data trees. */
-#define LY_TYPE_OPTS_SECOND_CALL 0x20  /**< Flag for the second call of the callback when the first call returns LY_EINCOMPLETE,
+#define LY_TYPE_OPTS_SECOND_CALL  0x20 /**< Flag for the second call of the callback when the first call returns LY_EINCOMPLETE,
                                             other options should be the same as for the first call. **!!** Note that this second call
                                             can occur even if the first call succeeded, in which case the plugin should immediately
                                             return LY_SUCCESS. */
+#define LY_TYPE_OPTS_ISSTRING     LYD_NODE_OPAQ_ISSTRING /**< Hint flag from the parser in case the source format provides some additional information
+                                            about the type of the data. The flag is expected to be used in combination with the format information. */
+#define LY_TYPE_OPTS_ISNUMBER     LYD_NODE_OPAQ_ISNUMBER /**< Hint flag from the parser in case the source format provides some additional information
+                                            about the type of the data. The flag is expected to be used in combination with the format information. */
+#define LY_TYPE_OPTS_ISBOOLEAN    LYD_NODE_OPAQ_ISBOOLEAN /**< Hint flag from the parser in case the source format provides some additional information
+                                            about the type of the data. The flag is expected to be used in combination with the format information. */
+#define LY_TYPE_OPTS_ISEMPTY      LYD_NODE_OPAQ_ISEMPTY /**< Hint flag from the parser in case the source format provides some additional information
+                                            about the type of the data. The flag is expected to be used in combination with the format information. */
+#define LY_TYPE_PARSER_HINTS_MASK (LY_TYPE_OPTS_ISSTRING | LY_TYPE_OPTS_ISNUMBER | LY_TYPE_OPTS_ISBOOLEAN | LY_TYPE_OPTS_ISEMPTY)
 
 /** @} plugintypeopts */
 
@@ -200,7 +209,7 @@
  * @return LY_ERR value if an error occurred and the value could not be canonized following the type's rules.
  */
 typedef LY_ERR (*ly_type_store_clb)(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len,
-                                    int options, ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
+                                    int options, ly_resolve_prefix_clb resolve_prefix, void *parser, LYD_FORMAT format,
                                     const void *context_node, const struct lyd_node *tree,
                                     struct lyd_value *storage, const char **canonized, struct ly_err_item **err);
 
@@ -234,7 +243,7 @@
  *         can be responsible for freeing allocated memory.
  * @return NULL in case of error.
  */
-typedef const char *(*ly_type_print_clb)(const struct lyd_value *value, LYD_FORMAT format, ly_clb_get_prefix get_prefix,
+typedef const char *(*ly_type_print_clb)(const struct lyd_value *value, LYD_FORMAT format, ly_get_prefix_clb get_prefix,
                                          void *printer, int *dynamic);
 
 /**
@@ -391,7 +400,7 @@
  * @return Created [sized array](@ref sizedarrays) of prefix mappings, NULL in case of error.
  */
 struct lyd_value_prefix *ly_type_get_prefixes(const struct ly_ctx *ctx, const char *value, size_t value_len,
-                                              ly_clb_resolve_prefix get_prefix, void *parser);
+                                              ly_resolve_prefix_clb get_prefix, void *parser);
 
 /** @} types */
 
diff --git a/src/printer_json.c b/src/printer_json.c
index 057bf6e..705d2eb 100644
--- a/src/printer_json.c
+++ b/src/printer_json.c
@@ -20,7 +20,7 @@
 /**
  * @brief JSON mapping of YANG modules to prefixes in values.
  *
- * Implementation of ly_clb_get_prefix.
+ * Implementation of ly_get_prefix_clb.
  */
 const char *
 json_print_get_prefix(const struct lys_module *mod, void *UNUSED(private))
diff --git a/src/printer_lyb.c b/src/printer_lyb.c
index 1b89e39..9331876 100644
--- a/src/printer_lyb.c
+++ b/src/printer_lyb.c
@@ -223,7 +223,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_write(struct ly_out *out, const uint8_t *buf, size_t count, struct lyd_lyb_ctx *lybctx)
+lyb_write(struct ly_out *out, const uint8_t *buf, size_t count, struct lylyb_ctx *lybctx)
 {
     LY_ARRAY_COUNT_TYPE u;
     struct lyd_lyb_subtree *full, *iter;
@@ -307,7 +307,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_write_stop_subtree(struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+lyb_write_stop_subtree(struct ly_out *out, struct lylyb_ctx *lybctx)
 {
     ssize_t r;
     uint8_t meta_buf[LYB_META_BYTES];
@@ -334,7 +334,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_write_start_subtree(struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+lyb_write_start_subtree(struct ly_out *out, struct lylyb_ctx *lybctx)
 {
     ssize_t r;
     LY_ARRAY_COUNT_TYPE u;
@@ -381,7 +381,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_write_number(uint64_t num, size_t bytes, struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+lyb_write_number(uint64_t num, size_t bytes, struct ly_out *out, struct lylyb_ctx *lybctx)
 {
     /* correct byte order */
     num = htole64(num);
@@ -400,7 +400,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_write_string(const char *str, size_t str_len, int with_length, struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+lyb_write_string(const char *str, size_t str_len, int with_length, struct ly_out *out, struct lylyb_ctx *lybctx)
 {
     if (!str) {
         str = "";
@@ -432,7 +432,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_print_model(struct ly_out *out, const struct lys_module *mod, struct lyd_lyb_ctx *lybctx)
+lyb_print_model(struct ly_out *out, const struct lys_module *mod, struct lylyb_ctx *lybctx)
 {
     int r;
     uint16_t revision;
@@ -477,7 +477,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_print_data_models(struct ly_out *out, const struct lyd_node *root, struct lyd_lyb_ctx *lybctx)
+lyb_print_data_models(struct ly_out *out, const struct lyd_node *root, struct lylyb_ctx *lybctx)
 {
     struct ly_set *set;
     LY_ARRAY_COUNT_TYPE u;
@@ -577,7 +577,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_print_opaq_prefixes(struct ly_out *out, const struct ly_prefix *prefs, struct lyd_lyb_ctx *lybctx)
+lyb_print_opaq_prefixes(struct ly_out *out, const struct ly_prefix *prefs, struct lylyb_ctx *lybctx)
 {
     uint8_t count;
     LY_ARRAY_COUNT_TYPE u;
@@ -595,10 +595,10 @@
     /* write all the prefixes */
     LY_ARRAY_FOR(prefs, u) {
         /* prefix */
-        LY_CHECK_RET(lyb_write_string(prefs[u].pref, 0, 1, out, lybctx));
+        LY_CHECK_RET(lyb_write_string(prefs[u].id, 0, 1, out, lybctx));
 
         /* namespace */
-        LY_CHECK_RET(lyb_write_string(prefs[u].ns, 0, 1, out, lybctx));
+        LY_CHECK_RET(lyb_write_string(prefs[u].module_name, 0, 1, out, lybctx));
     }
 
     return LY_SUCCESS;
@@ -613,13 +613,13 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_print_opaq(struct lyd_node_opaq *opaq, struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+lyb_print_opaq(struct lyd_node_opaq *opaq, struct ly_out *out, struct lylyb_ctx *lybctx)
 {
     /* prefix */
-    LY_CHECK_RET(lyb_write_string(opaq->prefix.pref, 0, 1, out, lybctx));
+    LY_CHECK_RET(lyb_write_string(opaq->prefix.id, 0, 1, out, lybctx));
 
-    /* namespace */
-    LY_CHECK_RET(lyb_write_string(opaq->prefix.ns, 0, 1, out, lybctx));
+    /* module reference */
+    LY_CHECK_RET(lyb_write_string(opaq->prefix.module_name, 0, 1, out, lybctx));
 
     /* name */
     LY_CHECK_RET(lyb_write_string(opaq->name, 0, 1, out, lybctx));
@@ -645,7 +645,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_print_anydata(struct lyd_node_any *anydata, struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+lyb_print_anydata(struct lyd_node_any *anydata, struct ly_out *out, struct lylyb_ctx *lybctx)
 {
     LY_ERR ret = LY_SUCCESS;
     LYD_ANYDATA_VALUETYPE value_type;
@@ -698,7 +698,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_print_term(struct lyd_node_term *term, struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+lyb_print_term(struct lyd_node_term *term, struct ly_out *out, struct lylyb_ctx *lybctx)
 {
     LY_ERR ret;
     int dynamic;
@@ -749,47 +749,47 @@
     }
     for (iter = node->meta; iter; iter = iter->next) {
         if (count == UINT8_MAX) {
-            LOGERR(lybctx->ctx, LY_EINT, "Maximum supported number of data node metadata is %u.", UINT8_MAX);
+            LOGERR(lybctx->lybctx->ctx, LY_EINT, "Maximum supported number of data node metadata is %u.", UINT8_MAX);
             return LY_EINT;
         }
         ++count;
     }
 
     /* write number of metadata on 1 byte */
-    LY_CHECK_RET(lyb_write(out, &count, 1, lybctx));
+    LY_CHECK_RET(lyb_write(out, &count, 1, lybctx->lybctx));
 
     if (wd_mod) {
         /* write the "default" metadata */
-        LY_CHECK_RET(lyb_write_start_subtree(out, lybctx));
-        LY_CHECK_RET(lyb_print_model(out, wd_mod, lybctx));
-        LY_CHECK_RET(lyb_write_string("default", 0, 1, out, lybctx));
-        LY_CHECK_RET(lyb_write_string("true", 0, 0, out, lybctx));
-        LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx));
+        LY_CHECK_RET(lyb_write_start_subtree(out, lybctx->lybctx));
+        LY_CHECK_RET(lyb_print_model(out, wd_mod, lybctx->lybctx));
+        LY_CHECK_RET(lyb_write_string("default", 0, 1, out, lybctx->lybctx));
+        LY_CHECK_RET(lyb_write_string("true", 0, 0, out, lybctx->lybctx));
+        LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx->lybctx));
     }
 
     /* write all the node metadata */
     LY_LIST_FOR(node->meta, iter) {
         /* each metadata is a subtree */
-        LY_CHECK_RET(lyb_write_start_subtree(out, lybctx));
+        LY_CHECK_RET(lyb_write_start_subtree(out, lybctx->lybctx));
 
         /* model */
-        LY_CHECK_RET(lyb_print_model(out, iter->annotation->module, lybctx));
+        LY_CHECK_RET(lyb_print_model(out, iter->annotation->module, lybctx->lybctx));
 
         /* annotation name with length */
-        LY_CHECK_RET(lyb_write_string(iter->name, 0, 1, out, lybctx));
+        LY_CHECK_RET(lyb_write_string(iter->name, 0, 1, out, lybctx->lybctx));
 
         /* get the value */
         str = lyd_meta2str(iter, &dynamic);
 
         /* metadata value */
-        ret = lyb_write_string(str, 0, 0, out, lybctx);
+        ret = lyb_write_string(str, 0, 0, out, lybctx->lybctx);
         if (dynamic) {
             free((char *)str);
         }
         LY_CHECK_RET(ret);
 
         /* finish metadata subtree */
-        LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx));
+        LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx->lybctx));
     }
 
     return LY_SUCCESS;
@@ -804,7 +804,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_print_attributes(struct ly_out *out, const struct lyd_node_opaq *node, struct lyd_lyb_ctx *lybctx)
+lyb_print_attributes(struct ly_out *out, const struct lyd_node_opaq *node, struct lylyb_ctx *lybctx)
 {
     uint8_t count = 0;
     struct ly_attr *iter;
@@ -826,10 +826,10 @@
         LY_CHECK_RET(lyb_write_start_subtree(out, lybctx));
 
         /* prefix */
-        LY_CHECK_RET(lyb_write_string(iter->prefix.pref, 0, 1, out, lybctx));
+        LY_CHECK_RET(lyb_write_string(iter->prefix.id, 0, 1, out, lybctx));
 
         /* namespace */
-        LY_CHECK_RET(lyb_write_string(iter->prefix.ns, 0, 1, out, lybctx));
+        LY_CHECK_RET(lyb_write_string(iter->prefix.module_name, 0, 1, out, lybctx));
 
         /* name */
         LY_CHECK_RET(lyb_write_string(iter->name, 0, 1, out, lybctx));
@@ -860,7 +860,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_print_schema_hash(struct ly_out *out, struct lysc_node *schema, struct hash_table **sibling_ht, struct lyd_lyb_ctx *lybctx)
+lyb_print_schema_hash(struct ly_out *out, struct lysc_node *schema, struct hash_table **sibling_ht, struct lylyb_ctx *lybctx)
 {
     LY_ARRAY_COUNT_TYPE u;
     uint32_t i;
@@ -941,36 +941,36 @@
     struct hash_table *child_ht = NULL;
 
     /* register a new subtree */
-    LY_CHECK_RET(lyb_write_start_subtree(out, lybctx));
+    LY_CHECK_RET(lyb_write_start_subtree(out, lybctx->lybctx));
 
     /* write model info first */
     if (!node->schema && !((struct lyd_node_opaq *)node)->parent) {
-        LY_CHECK_RET(lyb_print_model(out, NULL, lybctx));
+        LY_CHECK_RET(lyb_print_model(out, NULL, lybctx->lybctx));
     } else if (node->schema && !lysc_data_parent(node->schema)) {
-        LY_CHECK_RET(lyb_print_model(out, node->schema->module, lybctx));
+        LY_CHECK_RET(lyb_print_model(out, node->schema->module, lybctx->lybctx));
     }
 
     /* write schema hash */
-    LY_CHECK_RET(lyb_print_schema_hash(out, (struct lysc_node *)node->schema, sibling_ht, lybctx));
+    LY_CHECK_RET(lyb_print_schema_hash(out, (struct lysc_node *)node->schema, sibling_ht, lybctx->lybctx));
 
     /* write any metadata/attributes */
     if (node->schema) {
         LY_CHECK_RET(lyb_print_metadata(out, node, lybctx));
     } else {
-        LY_CHECK_RET(lyb_print_attributes(out, (struct lyd_node_opaq *)node, lybctx));
+        LY_CHECK_RET(lyb_print_attributes(out, (struct lyd_node_opaq *)node, lybctx->lybctx));
     }
 
     /* write node content */
     if (!node->schema) {
-        LY_CHECK_RET(lyb_print_opaq((struct lyd_node_opaq *)node, out, lybctx));
+        LY_CHECK_RET(lyb_print_opaq((struct lyd_node_opaq *)node, out, lybctx->lybctx));
     } else if (node->schema->nodetype & LYD_NODE_INNER) {
         /* nothing to write */
     } else if (node->schema->nodetype & LYD_NODE_TERM) {
-        LY_CHECK_RET(lyb_print_term((struct lyd_node_term *)node, out, lybctx));
+        LY_CHECK_RET(lyb_print_term((struct lyd_node_term *)node, out, lybctx->lybctx));
     } else if (node->schema->nodetype & LYD_NODE_ANY) {
-        LY_CHECK_RET(lyb_print_anydata((struct lyd_node_any *)node, out, lybctx));
+        LY_CHECK_RET(lyb_print_anydata((struct lyd_node_any *)node, out, lybctx->lybctx));
     } else {
-        LOGINT_RET(lybctx->ctx);
+        LOGINT_RET(lybctx->lybctx->ctx);
     }
 
     /* recursively write all the descendants */
@@ -979,7 +979,7 @@
     }
 
     /* finish this subtree */
-    LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx));
+    LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx->lybctx));
 
     return LY_SUCCESS;
 }
@@ -989,17 +989,22 @@
 {
     LY_ERR ret = LY_SUCCESS;
     uint8_t zero = 0;
-    LY_ARRAY_COUNT_TYPE u;
     struct hash_table *top_sibling_ht = NULL;
     const struct lys_module *prev_mod = NULL;
-    struct lyd_lyb_ctx lybctx = {0};
+    struct lyd_lyb_ctx *lybctx;
+    const struct ly_ctx *ctx = LYD_NODE_CTX(root);
 
-    lybctx.print_options = options;
+    lybctx = calloc(1, sizeof *lybctx);
+    LY_CHECK_ERR_RET(!lybctx, LOGMEM(ctx), LY_EMEM);
+    lybctx->lybctx = calloc(1, sizeof *lybctx->lybctx);
+    LY_CHECK_ERR_RET(!lybctx, LOGMEM(ctx), LY_EMEM);
+
+    lybctx->print_options = options;
     if (root) {
-        lybctx.ctx = LYD_NODE_CTX(root);
+        lybctx->lybctx->ctx = ctx;
 
         if (root->schema && lysc_data_parent(root->schema)) {
-            LOGERR(lybctx.ctx, LY_EINVAL, "LYB printer supports only printing top-level nodes.");
+            LOGERR(lybctx->lybctx->ctx, LY_EINVAL, "LYB printer supports only printing top-level nodes.");
             return LY_EINVAL;
         }
     }
@@ -1011,7 +1016,7 @@
     LY_CHECK_GOTO(ret = lyb_print_header(out), cleanup);
 
     /* all used models */
-    LY_CHECK_GOTO(ret = lyb_print_data_models(out, root, &lybctx), cleanup);
+    LY_CHECK_GOTO(ret = lyb_print_data_models(out, root, lybctx->lybctx), cleanup);
 
     LY_LIST_FOR(root, root) {
         /* do not reuse sibling hash tables from different modules */
@@ -1020,7 +1025,7 @@
             prev_mod = root->schema ? root->schema->module : NULL;
         }
 
-        LY_CHECK_GOTO(ret = lyb_print_subtree(out, root, &top_sibling_ht, &lybctx), cleanup);
+        LY_CHECK_GOTO(ret = lyb_print_subtree(out, root, &top_sibling_ht, lybctx), cleanup);
 
         if (!(options & LYD_PRINT_WITHSIBLINGS)) {
             break;
@@ -1028,14 +1033,9 @@
     }
 
     /* ending zero byte */
-    LY_CHECK_GOTO(ret = lyb_write(out, &zero, sizeof zero, &lybctx), cleanup);
+    LY_CHECK_GOTO(ret = lyb_write(out, &zero, sizeof zero, lybctx->lybctx), cleanup);
 
 cleanup:
-    LY_ARRAY_FREE(lybctx.subtrees);
-    LY_ARRAY_FOR(lybctx.sib_hts, u) {
-        lyht_free(lybctx.sib_hts[u].ht);
-    }
-    LY_ARRAY_FREE(lybctx.sib_hts);
-
+    lyd_lyb_ctx_free((struct lyd_ctx *)lybctx);
     return ret;
 }
diff --git a/src/printer_xml.c b/src/printer_xml.c
index 71e53c0..3a94406 100644
--- a/src/printer_xml.c
+++ b/src/printer_xml.c
@@ -50,6 +50,7 @@
 #define LEVEL_DEC if (LEVEL) {LEVEL--;}      /**< decrease indentation level */
 
 #define LYXML_PREFIX_REQUIRED 0x01  /**< The prefix is not just a suggestion but a requirement. */
+#define LYXML_PREFIX_DEFAULT  0x02  /**< The namespace is required to be a default (without prefix) */
 
 /**
  * @brief Print a namespace if not already printed.
@@ -107,10 +108,35 @@
     return ctx->prefix.objs[i];
 }
 
+static const char*
+xml_print_ns_opaq(struct xmlpr_ctx *ctx, LYD_FORMAT format, const struct ly_prefix *prefix, int prefix_opts)
+{
+
+    switch (format) {
+    case LYD_XML:
+        return xml_print_ns(ctx, prefix->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : prefix->id, prefix_opts);
+        break;
+    case LYD_JSON:
+        if (prefix->module_name) {
+            const struct lys_module *mod = ly_ctx_get_module_latest(ctx->ctx, prefix->module_name);
+            if (mod) {
+                return xml_print_ns(ctx, mod->ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : prefix->id, prefix_opts);
+            }
+        }
+        break;
+    case LYD_SCHEMA:
+    case LYD_LYB:
+        /* cannot be created */
+        LOGINT(ctx->ctx);
+    }
+
+    return NULL;
+}
+
 /**
  * @brief XML mapping of YANG modules to prefixes in values.
  *
- * Implementation of ly_clb_get_prefix
+ * Implementation of ly_get_prefix_clb
  */
 static const char *
 xml_print_get_prefix(const struct lys_module *mod, void *private)
@@ -237,24 +263,15 @@
 
     LY_LIST_FOR(node->attr, attr) {
         pref = NULL;
-        if (attr->prefix.pref) {
+        if (attr->prefix.id) {
             /* print attribute namespace */
-            switch (attr->format) {
-            case LYD_XML:
-                pref = xml_print_ns(ctx, attr->prefix.ns, attr->prefix.pref, 0);
-                break;
-            case LYD_SCHEMA:
-            case LYD_LYB:
-                /* cannot be created */
-                LOGINT(node->ctx);
-                return LY_EINT;
-            }
+            pref = xml_print_ns_opaq(ctx, attr->format, &attr->prefix, 0);
         }
 
         /* print namespaces connected with the value's prefixes */
         if (attr->val_prefs) {
             LY_ARRAY_FOR(attr->val_prefs, u) {
-                xml_print_ns(ctx, attr->val_prefs[u].ns, attr->val_prefs[u].pref, LYXML_PREFIX_REQUIRED);
+                xml_print_ns_opaq(ctx, attr->format, &attr->val_prefs[u], LYXML_PREFIX_REQUIRED);
             }
         }
 
@@ -272,16 +289,7 @@
     ly_print(ctx->out, "%*s<%s", INDENT, node->name);
 
     /* print default namespace */
-    switch (node->format) {
-    case LYD_XML:
-        xml_print_ns(ctx, node->prefix.ns, NULL, 0);
-        break;
-    case LYD_SCHEMA:
-    case LYD_LYB:
-        /* cannot be created */
-        LOGINT(node->ctx);
-        return LY_EINT;
-    }
+    xml_print_ns_opaq(ctx, node->format, &node->prefix, LYXML_PREFIX_DEFAULT);
 
     /* print attributes */
     LY_CHECK_RET(xml_print_attr(ctx, node));
@@ -457,7 +465,7 @@
         /* print namespaces connected with the value's prefixes */
         if (node->val_prefs) {
             LY_ARRAY_FOR(node->val_prefs, u) {
-                xml_print_ns(ctx, node->val_prefs[u].ns, node->val_prefs[u].pref, LYXML_PREFIX_REQUIRED);
+                xml_print_ns_opaq(ctx, node->format, &node->val_prefs[u], LYXML_PREFIX_REQUIRED);
             }
         }
 
diff --git a/src/tree_data.c b/src/tree_data.c
index 5ca3f4a..a5572d7 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -55,14 +55,14 @@
                                       struct lyd_node **match);
 
 LY_ERR
-lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int *dynamic, int second,
-                ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node *tree)
+lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int *dynamic, int second, int value_hint,
+                ly_resolve_prefix_clb get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node *tree)
 {
     LY_ERR ret = LY_SUCCESS;
     struct ly_err_item *err = NULL;
     struct ly_ctx *ctx;
     struct lysc_type *type;
-    int options = LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
+    int options = value_hint | LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
             (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
     assert(node);
 
@@ -96,7 +96,7 @@
 /* similar to lyd_value_parse except can be used just to store the value, hence also does not support a second call */
 LY_ERR
 lyd_value_store(struct lyd_value *val, const struct lysc_node *schema, const char *value, size_t value_len, int *dynamic,
-                ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format)
+                ly_resolve_prefix_clb get_prefix, void *parser, LYD_FORMAT format)
 {
     LY_ERR ret = LY_SUCCESS;
     struct ly_err_item *err = NULL;
@@ -128,13 +128,13 @@
 
 LY_ERR
 lyd_value_parse_meta(const struct ly_ctx *ctx, struct lyd_meta *meta, const char *value, size_t value_len, int *dynamic,
-                     int second, ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,
+                     int second, int value_hint, ly_resolve_prefix_clb get_prefix, void *parser, LYD_FORMAT format,
                      const struct lysc_node *ctx_snode, const struct lyd_node *tree)
 {
     LY_ERR ret = LY_SUCCESS;
     struct ly_err_item *err = NULL;
     struct lyext_metadata *ant;
-    int options = LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
+    int options = value_hint | LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
             (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
 
     assert(ctx && meta && ((tree && meta->parent) || ctx_snode));
@@ -163,7 +163,7 @@
 
 LY_ERR
 _lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
-                    ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format)
+                    ly_resolve_prefix_clb resolve_prefix, void *prefix_data, LYD_FORMAT format)
 {
     LY_ERR rc = LY_SUCCESS;
     struct ly_err_item *err = NULL;
@@ -309,10 +309,8 @@
 
         if (len >= 5 && !strncmp(&path[len - 4], ".xml", 4)) {
             format = LYD_XML;
-#if 0
         } else if (len >= 6 && !strncmp(&path[len - 5], ".json", 5)) {
             format = LYD_JSON;
-#endif
         } else if (len >= 5 && !strncmp(&path[len - 4], ".lyb", 4)) {
             format = LYD_LYB;
         } /* else still unknown */
@@ -325,6 +323,9 @@
 lyd_parse_data(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, int parse_options, int validate_options,
                struct lyd_node **tree)
 {
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_ctx *lydctx = NULL;
+
     LY_CHECK_ARG_RET(ctx, ctx, in, tree, LY_EINVAL);
     LY_CHECK_ARG_RET(ctx, !(parse_options & ~LYD_PARSE_OPTS_MASK), LY_EINVAL);
     LY_CHECK_ARG_RET(ctx, !(validate_options & ~LYD_VALIDATE_OPTS_MASK), LY_EINVAL);
@@ -332,26 +333,73 @@
     format = lyd_parse_get_format(in, format);
     LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
 
+    /* init */
+    *tree = NULL;
+
     /* remember input position */
     in->func_start = in->current;
 
     switch (format) {
     case LYD_XML:
-        return lyd_parse_xml_data(ctx, in, parse_options, validate_options, tree);
-#if 0
+        LY_CHECK_RET(lyd_parse_xml_data(ctx, in, parse_options, validate_options, tree, &lydctx));
+        break;
     case LYD_JSON:
-        return lyd_parse_json_data(ctx, in, parse_options, validate_options, tree);
-#endif
+        LY_CHECK_RET(lyd_parse_json_data(ctx, in, parse_options, validate_options, tree, &lydctx));
+        break;
     case LYD_LYB:
-        return lyd_parse_lyb_data(ctx, in, parse_options, validate_options, tree);
+        LY_CHECK_RET(lyd_parse_lyb_data(ctx, in, parse_options, validate_options, tree, &lydctx));
+        break;
     case LYD_SCHEMA:
         LOGINT_RET(ctx);
     }
 
-    /* TODO move here the top-level validation from parser_xml.c's lyd_parse_xml_data() and make
-     * it common for all the lyd_parse_*_data() functions */
+    if (!(parse_options & LYD_PARSE_ONLY)) {
+        uint32_t i = 0;
+        const struct lys_module *mod;
+        struct lyd_node *first, *next, **first2;
 
-    LOGINT_RET(ctx);
+        next = *tree;
+        while (1) {
+            if (validate_options & LYD_VALIDATE_PRESENT) {
+                mod = lyd_data_next_module(&next, &first);
+            } else {
+                mod = lyd_mod_next_module(next, NULL, ctx, &i, &first);
+            }
+            if (!mod) {
+                break;
+            }
+            if (first == *tree) {
+                /* make sure first2 changes are carried to tree */
+                first2 = tree;
+            } else {
+                first2 = &first;
+            }
+
+            /* validate new top-level nodes, autodelete CANNOT occur, all nodes are new */
+            LY_CHECK_GOTO(ret = lyd_validate_new(first2, NULL, mod, NULL), cleanup);
+
+            /* add all top-level defaults for this module */
+            ret = lyd_new_implicit_r(NULL, first2, NULL, mod, &lydctx->unres_node_type, &lydctx->when_check,
+                                     (validate_options & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+            LY_CHECK_GOTO(ret, cleanup);
+
+            /* finish incompletely validated terminal values/attributes and when conditions */
+            ret = lyd_validate_unres(tree, &lydctx->when_check, &lydctx->unres_node_type, &lydctx->unres_meta_type, LYD_XML,
+                                     lydctx->resolve_prefix, lydctx->data_ctx, NULL);
+            LY_CHECK_GOTO(ret, cleanup);
+
+            /* perform final validation that assumes the data tree is final */
+            LY_CHECK_GOTO(ret = lyd_validate_final_r(*first2, NULL, mod, validate_options, 0), cleanup);
+        }
+    }
+
+cleanup:
+    lydctx->free((struct lyd_ctx *)lydctx);
+    if (ret) {
+        lyd_free_all(*tree);
+        *tree = NULL;
+    }
+    return ret;
 }
 
 API LY_ERR
@@ -404,16 +452,20 @@
     format = lyd_parse_get_format(in, format);
     LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
 
+    /* init */
+    *tree = NULL;
+    if (op) {
+        *op = NULL;
+    }
+
     /* remember input position */
     in->func_start = in->current;
 
     switch (format) {
     case LYD_XML:
         return lyd_parse_xml_rpc(ctx, in, tree, op);
-#if 0
     case LYD_JSON:
         return lyd_parse_json_rpc(ctx, in, tree, op);
-#endif
     case LYD_LYB:
         return lyd_parse_lyb_rpc(ctx, in, tree, op);
     case LYD_SCHEMA:
@@ -428,21 +480,27 @@
                 struct lyd_node **op)
 {
     LY_CHECK_ARG_RET(NULL, request, LY_EINVAL);
-    LY_CHECK_ARG_RET(LYD_NODE_CTX(request), in, tree, LY_EINVAL);
+    LY_CHECK_ARG_RET(LYD_NODE_CTX(request), in, tree || op, LY_EINVAL);
 
     format = lyd_parse_get_format(in, format);
     LY_CHECK_ARG_RET(LYD_NODE_CTX(request), format, LY_EINVAL);
 
+    /* init */
+    if (tree) {
+        *tree = NULL;
+    }
+    if (op) {
+        *op = NULL;
+    }
+
     /* remember input position */
     in->func_start = in->current;
 
     switch (format) {
     case LYD_XML:
         return lyd_parse_xml_reply(request, in, tree, op);
-#if 0
     case LYD_JSON:
         return lyd_parse_json_reply(request, in, tree, op);
-#endif
     case LYD_LYB:
         return lyd_parse_lyb_reply(request, in, tree, op);
     case LYD_SCHEMA:
@@ -455,21 +513,27 @@
 API LY_ERR
 lyd_parse_notif(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, struct lyd_node **tree, struct lyd_node **ntf)
 {
-    LY_CHECK_ARG_RET(ctx, ctx, in, tree, LY_EINVAL);
+    LY_CHECK_ARG_RET(ctx, ctx, in, tree || ntf, LY_EINVAL);
 
     format = lyd_parse_get_format(in, format);
     LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
 
+    /* init */
+    if (tree) {
+        *tree = NULL;
+    }
+    if (ntf) {
+        *ntf = NULL;
+    }
+
     /* remember input position */
     in->func_start = in->current;
 
     switch (format) {
     case LYD_XML:
         return lyd_parse_xml_notif(ctx, in, tree, ntf);
-#if 0
     case LYD_JSON:
         return lyd_parse_json_notif(ctx, in, tree, ntf);
-#endif
     case LYD_LYB:
         return lyd_parse_lyb_notif(ctx, in, tree, ntf);
     case LYD_SCHEMA:
@@ -480,8 +544,8 @@
 }
 
 LY_ERR
-lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, int *dynamic,
-                ly_clb_resolve_prefix get_prefix, void *prefix_data, LYD_FORMAT format, struct lyd_node **node)
+lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, int *dynamic, int value_hint,
+                ly_resolve_prefix_clb get_prefix, void *prefix_data, LYD_FORMAT format, struct lyd_node **node)
 {
     LY_ERR ret;
     struct lyd_node_term *term;
@@ -495,7 +559,7 @@
     term->prev = (struct lyd_node *)term;
     term->flags = LYD_NEW;
 
-    ret = lyd_value_parse(term, value, value_len, dynamic, 0, get_prefix, prefix_data, format, NULL);
+    ret = lyd_value_parse(term, value, value_len, dynamic, 0, value_hint, get_prefix, prefix_data, format, NULL);
     if (ret && (ret != LY_EINCOMPLETE)) {
         free(term);
         return ret;
@@ -630,7 +694,8 @@
     any->prev = (struct lyd_node *)any;
     any->flags = LYD_NEW;
 
-    any->value.xml = value;
+    /* TODO: convert XML/JSON strings into a opaq data tree */
+    any->value.str = value;
     any->value_type = value_type;
     lyd_hash((struct lyd_node *)any);
 
@@ -640,12 +705,12 @@
 
 LY_ERR
 lyd_create_opaq(const struct ly_ctx *ctx, const char *name, size_t name_len, const char *value, size_t value_len,
-                int *dynamic, LYD_FORMAT format, struct ly_prefix *val_prefs, const char *prefix, size_t pref_len,
-                const char *ns, struct lyd_node **node)
+                int *dynamic, int value_hint, LYD_FORMAT format, struct ly_prefix *val_prefs, const char *prefix, size_t pref_len,
+                const char *module_key, size_t module_key_len, struct lyd_node **node)
 {
     struct lyd_node_opaq *opaq;
 
-    assert(ctx && name && name_len && ns);
+    assert(ctx && name && name_len);
 
     if (!value_len) {
         value = "";
@@ -659,9 +724,12 @@
     opaq->name = lydict_insert(ctx, name, name_len);
     opaq->format = format;
     if (pref_len) {
-        opaq->prefix.pref = lydict_insert(ctx, prefix, pref_len);
+        opaq->prefix.id = lydict_insert(ctx, prefix, pref_len);
     }
-    opaq->prefix.ns = lydict_insert(ctx, ns, 0);
+    if (module_key_len) {
+        opaq->prefix.module_ns = lydict_insert(ctx, module_key, module_key_len);
+    }
+
     opaq->val_prefs = val_prefs;
     if (dynamic && *dynamic) {
         opaq->value = lydict_insert_zc(ctx, (char *)value);
@@ -669,6 +737,7 @@
     } else {
         opaq->value = lydict_insert(ctx, value, value_len);
     }
+    opaq->hint = value_hint;
     opaq->ctx = ctx;
 
     *node = (struct lyd_node *)opaq;
@@ -731,7 +800,7 @@
     for (key_s = lysc_node_children(schema, 0); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
         key_val = va_arg(ap, const char *);
 
-        rc = lyd_create_term(key_s, key_val, key_val ? strlen(key_val) : 0, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &key);
+        rc = lyd_create_term(key_s, key_val, key_val ? strlen(key_val) : 0, NULL, 0, lydjson_resolve_prefix, NULL, LYD_JSON, &key);
         LY_CHECK_GOTO(rc && (rc != LY_EINCOMPLETE), cleanup);
         rc = LY_SUCCESS;
         lyd_insert_node(ret, NULL, key);
@@ -808,7 +877,7 @@
     schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYD_NODE_TERM, 0);
     LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Term node \"%s\" not found.", name), LY_ENOTFOUND);
 
-    rc = lyd_create_term(schema, val_str, val_str ? strlen(val_str) : 0, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &ret);
+    rc = lyd_create_term(schema, val_str, val_str ? strlen(val_str) : 0, NULL, 0, lydjson_resolve_prefix, NULL, LYD_JSON, &ret);
     LY_CHECK_RET(rc && (rc != LY_EINCOMPLETE), rc);
     if (parent) {
         lyd_insert_node(parent, NULL, ret);
@@ -952,7 +1021,7 @@
         val_str = "";
     }
 
-    LY_CHECK_RET(lyd_create_meta(parent, &ret, module, name, name_len, val_str, strlen(val_str), NULL,
+    LY_CHECK_RET(lyd_create_meta(parent, &ret, module, name, name_len, val_str, strlen(val_str), NULL, 0,
                                  lydjson_resolve_prefix, NULL, LYD_JSON, parent->schema));
 
     if (meta) {
@@ -976,8 +1045,8 @@
         value = "";
     }
 
-    LY_CHECK_RET(lyd_create_opaq(ctx, name, strlen(name), value, strlen(value), NULL, LYD_JSON, NULL, NULL, 0,
-                                 module_name, &ret));
+    LY_CHECK_RET(lyd_create_opaq(ctx, name, strlen(name), value, strlen(value), NULL, 0,
+                                 LYD_JSON, NULL, NULL, 0, module_name, strlen(module_name), &ret));
     if (parent) {
         lyd_insert_node(parent, NULL, ret);
     }
@@ -990,9 +1059,9 @@
 
 API LY_ERR
 lyd_new_attr(struct lyd_node *parent, const char *module_name, const char *name, const char *val_str,
-             struct ly_attr **attr)
+             struct lyd_attr **attr)
 {
-    struct ly_attr *ret = NULL;
+    struct lyd_attr *ret = NULL;
     const struct ly_ctx *ctx;
     const char *prefix, *tmp;
     size_t pref_len, name_len;
@@ -1013,8 +1082,8 @@
         val_str = "";
     }
 
-    LY_CHECK_RET(ly_create_attr(parent, &ret, ctx, name, name_len, val_str, strlen(val_str), NULL, LYD_JSON, NULL,
-                                prefix, pref_len, module_name));
+    LY_CHECK_RET(lyd_create_attr(parent, &ret, ctx, name, name_len, val_str, strlen(val_str), NULL, 0, LYD_JSON, NULL,
+                                 prefix, pref_len, module_name, module_name ? strlen(module_name) : 0));
 
     if (attr) {
         *attr = ret;
@@ -1117,7 +1186,7 @@
 
     /* parse the new value into a new meta structure */
     LY_CHECK_GOTO(ret = lyd_create_meta(NULL, &m2, meta->annotation->module, meta->name, strlen(meta->name), val_str,
-                                        strlen(val_str), NULL, lydjson_resolve_prefix, NULL, LYD_JSON, NULL), cleanup);
+                                        strlen(val_str), NULL, 0, lydjson_resolve_prefix, NULL, LYD_JSON, NULL), cleanup);
 
     /* compare original and new value */
     if (lyd_compare_meta(meta, m2)) {
@@ -1238,8 +1307,9 @@
             if (!(schema->flags & LYS_KEYLESS)) {
                 if ((options & LYD_NEWOPT_OPAQ) && (p[path_idx].pred_type == LY_PATH_PREDTYPE_NONE)) {
                     /* creating opaque list without keys */
-                    LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL,
-                                                        LYD_JSON, NULL, NULL, 0, schema->module->name, &node), cleanup);
+                    LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL, 0,
+                                                        LYD_JSON, NULL, NULL, 0, schema->module->name, strlen(schema->module->name), &node),
+                                  cleanup);
                 } else {
                     assert(p[path_idx].pred_type == LY_PATH_PREDTYPE_LIST);
                     LY_CHECK_GOTO(ret = lyd_create_list(schema, p[path_idx].predicates, &node), cleanup);
@@ -1256,8 +1326,9 @@
         case LYS_LEAFLIST:
             if ((options & LYD_NEWOPT_OPAQ) && (p[path_idx].pred_type == LY_PATH_PREDTYPE_NONE)) {
                 /* creating opaque leaf-list without value */
-                LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL,
-                                                    LYD_JSON, NULL, NULL, 0, schema->module->name, &node), cleanup);
+                LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL, 0,
+                                                    LYD_JSON, NULL, NULL, 0, schema->module->name, strlen(schema->module->name), &node),
+                              cleanup);
             } else {
                 assert(p[path_idx].pred_type == LY_PATH_PREDTYPE_LEAFLIST);
                 LY_CHECK_GOTO(ret = lyd_create_term2(schema, &p[path_idx].predicates[0].value, &node), cleanup);
@@ -1274,12 +1345,13 @@
                 r = lys_value_validate(NULL, schema, value, strlen(value));
             }
             if (!r) {
-                LY_CHECK_GOTO(ret = lyd_create_term(schema, value, strlen(value), NULL, lydjson_resolve_prefix, NULL,
+                LY_CHECK_GOTO(ret = lyd_create_term(schema, value, strlen(value), NULL, 0, lydjson_resolve_prefix, NULL,
                                                     LYD_JSON, &node), cleanup);
             } else {
                 /* creating opaque leaf without value */
-                LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL,
-                                                    LYD_JSON, NULL, NULL, 0, schema->module->name, &node), cleanup);
+                LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL, 0,
+                                                    LYD_JSON, NULL, NULL, 0, schema->module->name, strlen(schema->module->name), &node),
+                              cleanup);
             }
             break;
         case LYS_ANYDATA:
@@ -2030,8 +2102,37 @@
 }
 
 LY_ERR
+lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta)
+{
+    struct lyd_meta *last, *iter;
+
+    assert(parent);
+    assert(meta);
+
+    for (iter = meta; iter; iter = iter->next) {
+        iter->parent = parent;
+    }
+
+    /* insert as the last attribute */
+    if (parent->meta) {
+        for (last = parent->meta; last->next; last = last->next);
+        last->next = meta;
+    } else {
+        parent->meta = meta;
+    }
+
+    /* remove default flags from NP containers */
+    while (parent && (parent->schema->nodetype == LYS_CONTAINER) && (parent->flags & LYD_DEFAULT)) {
+        parent->flags &= ~LYD_DEFAULT;
+        parent = (struct lyd_node *)parent->parent;
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
 lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod, const char *name,
-                size_t name_len, const char *value, size_t value_len, int *dynamic, ly_clb_resolve_prefix resolve_prefix,
+                size_t name_len, const char *value, size_t value_len, int *dynamic, int value_hint, ly_resolve_prefix_clb resolve_prefix,
                 void *prefix_data, LYD_FORMAT format, const struct lysc_node *ctx_snode)
 {
     LY_ERR ret;
@@ -2060,7 +2161,7 @@
     LY_CHECK_ERR_RET(!mt, LOGMEM(mod->ctx), LY_EMEM);
     mt->parent = parent;
     mt->annotation = ant;
-    ret = lyd_value_parse_meta(mod->ctx, mt, value, value_len, dynamic, 0, resolve_prefix, prefix_data, format, ctx_snode, NULL);
+    ret = lyd_value_parse_meta(mod->ctx, mt, value, value_len, dynamic, 0, value_hint, resolve_prefix, prefix_data, format, ctx_snode, NULL);
     if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
         free(mt);
         return ret;
@@ -2069,23 +2170,12 @@
 
     /* insert as the last attribute */
     if (parent) {
-        if (parent->meta) {
-            for (last = parent->meta; last->next; last = last->next);
-            last->next = mt;
-        } else {
-            parent->meta = mt;
-        }
+        LY_CHECK_RET(lyd_insert_meta(parent, mt))
     } else if (*meta) {
         for (last = *meta; last->next; last = last->next);
         last->next = mt;
     }
 
-    /* remove default flags from NP containers */
-    while (parent && (parent->schema->nodetype == LYS_CONTAINER) && (parent->flags & LYD_DEFAULT)) {
-        parent->flags &= ~LYD_DEFAULT;
-        parent = (struct lyd_node *)parent->parent;
-    }
-
     if (meta) {
         *meta = mt;
     }
@@ -2093,16 +2183,15 @@
 }
 
 LY_ERR
-ly_create_attr(struct lyd_node *parent, struct ly_attr **attr, const struct ly_ctx *ctx, const char *name,
-               size_t name_len, const char *value, size_t value_len, int *dynamic, LYD_FORMAT format,
-               struct ly_prefix *val_prefs, const char *prefix, size_t prefix_len, const char *ns)
+lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const struct ly_ctx *ctx, const char *name,
+                size_t name_len, const char *value, size_t value_len, int *dynamic, int value_hint, LYD_FORMAT format,
+                struct ly_prefix *val_prefs, const char *prefix, size_t prefix_len, const char *module_key, size_t module_key_len)
 {
-    struct ly_attr *at, *last;
+    struct lyd_attr *at, *last;
     struct lyd_node_opaq *opaq;
 
     assert(ctx && (parent || attr) && (!parent || !parent->schema));
     assert(name && name_len);
-    assert((prefix_len && ns) || (!prefix_len && !ns));
 
     if (!value_len) {
         value = "";
@@ -2119,11 +2208,14 @@
         at->value = lydict_insert(ctx, value, value_len);
     }
 
+    at->hint = value_hint;
     at->format = format;
     at->val_prefs = val_prefs;
-    if (ns) {
-        at->prefix.pref = lydict_insert(ctx, prefix, prefix_len);
-        at->prefix.ns = lydict_insert(ctx, ns, 0);
+    if (prefix_len) {
+        at->prefix.id = lydict_insert(ctx, prefix, prefix_len);
+    }
+    if (module_key_len) {
+        at->prefix.module_ns = lydict_insert(ctx, module_key, module_key_len);
     }
 
     /* insert as the last attribute */
@@ -2187,7 +2279,7 @@
     if (!node1->schema) {
         opaq1 = (struct lyd_node_opaq *)node1;
         opaq2 = (struct lyd_node_opaq *)node2;
-        if ((opaq1->name != opaq2->name) || (opaq1->prefix.ns != opaq2->prefix.ns) || (opaq1->format != opaq2->format)) {
+        if ((opaq1->name != opaq2->name) || (opaq1->format != opaq2->format) || (opaq1->prefix.module_ns != opaq2->prefix.module_ns)) {
             return LY_ENOT;
         }
         switch (opaq1->format) {
@@ -2196,6 +2288,12 @@
                 return LY_ENOT;
             }
             break;
+        case LYD_JSON:
+            /* prefixes in JSON are unique, so it is not necessary to canonize the values */
+            if (strcmp(opaq1->value, opaq2->value)) {
+                return LY_ENOT;
+            }
+            break;
         case LYD_SCHEMA:
         case LYD_LYB:
             /* not allowed */
@@ -2438,17 +2536,15 @@
         }
         opaq->name = lydict_insert(LYD_NODE_CTX(node), orig->name, 0);
         opaq->format = orig->format;
-        if (orig->prefix.pref) {
-            opaq->prefix.pref = lydict_insert(LYD_NODE_CTX(node), orig->prefix.pref, 0);
+        if (orig->prefix.id) {
+            opaq->prefix.id = lydict_insert(LYD_NODE_CTX(node), orig->prefix.id, 0);
         }
-        if (orig->prefix.ns) {
-            opaq->prefix.ns = lydict_insert(LYD_NODE_CTX(node), orig->prefix.ns, 0);
-        }
+        opaq->prefix.module_ns = lydict_insert(LYD_NODE_CTX(node), orig->prefix.module_ns, 0);
         if (orig->val_prefs) {
             LY_ARRAY_CREATE_GOTO(LYD_NODE_CTX(node), opaq->val_prefs, LY_ARRAY_COUNT(orig->val_prefs), ret, error);
             LY_ARRAY_FOR(orig->val_prefs, u) {
-                opaq->val_prefs[u].pref = lydict_insert(LYD_NODE_CTX(node), orig->val_prefs[u].pref, 0);
-                opaq->val_prefs[u].ns = lydict_insert(LYD_NODE_CTX(node), orig->val_prefs[u].ns, 0);
+                opaq->val_prefs[u].id = lydict_insert(LYD_NODE_CTX(node), orig->val_prefs[u].id, 0);
+                opaq->val_prefs[u].module_ns = lydict_insert(LYD_NODE_CTX(node), orig->val_prefs[u].module_ns, 0);
                 LY_ARRAY_INCREMENT(opaq->val_prefs);
             }
         }
@@ -2954,22 +3050,23 @@
 iter_print:
             /* print prefix and name */
             mod = NULL;
-            if (!iter->parent || (iter->schema->module != iter->parent->schema->module)) {
+            if (iter->schema && (!iter->parent || iter->schema->module != iter->parent->schema->module)) {
                 mod = iter->schema->module;
             }
 
             /* realloc string */
-            len = 1 + (mod ? strlen(mod->name) + 1 : 0) + strlen(iter->schema->name);
+            len = 1 + (mod ? strlen(mod->name) + 1 : 0) + (iter->schema ? strlen(iter->schema->name) : strlen(((struct lyd_node_opaq*)iter)->name));
             rc = lyd_path_str_enlarge(&buffer, &buflen, bufused + len, is_static);
             if (rc != LY_SUCCESS) {
                 break;
             }
 
             /* print next node */
-            bufused += sprintf(buffer + bufused, "/%s%s%s", mod ? mod->name : "", mod ? ":" : "", iter->schema->name);
+            bufused += sprintf(buffer + bufused, "/%s%s%s", mod ? mod->name : "", mod ? ":" : "",
+                               iter->schema ? iter->schema->name : ((struct lyd_node_opaq*)iter)->name);
 
             /* do not always print the last (first) predicate */
-            if (bufused || (pathtype == LYD_PATH_LOG)) {
+            if (iter->schema && (bufused || (pathtype == LYD_PATH_LOG))) {
                 switch (iter->schema->nodetype) {
                 case LYS_LIST:
                     if (iter->schema->flags & LYS_KEYLESS) {
@@ -3216,7 +3313,7 @@
         /* create a data node and find the instance */
         if (schema->nodetype == LYS_LEAFLIST) {
             /* target used attributes: schema, hash, value */
-            rc = lyd_create_term(schema, key_or_value, val_len, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &target);
+            rc = lyd_create_term(schema, key_or_value, val_len, NULL, 0, lydjson_resolve_prefix, NULL, LYD_JSON, &target);
             LY_CHECK_RET(rc && (rc != LY_EINCOMPLETE), rc);
         } else {
             /* target used attributes: schema, hash, child (all keys) */
diff --git a/src/tree_data.h b/src/tree_data.h
index 58f5aeb..4aa13f5 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -194,6 +194,7 @@
             } *prefixes;                 /**< list of mappings between prefix in canonized value to a YANG schema ([sized array](@ref sizedarrays)) */
             struct lyd_value *value;     /**< representation of the value according to the selected union's subtype (stored as lyd_value::realpath
                                               here, in subvalue structure */
+            int parser_hint;             /**< Hint options from the parser */
         } *subvalue;                     /**< data to represent data with multiple types (union). Original value is stored in the main
                                               lyd_value:canonical_cache while the lyd_value_subvalue::value contains representation according to the
                                               one of the union's type. The lyd_value_subvalue:prefixes provides (possible) mappings from prefixes
@@ -238,23 +239,31 @@
 
 /**
  * @brief Generic prefix and namespace mapping, meaning depends on the format.
+ *
+ * The union is used as a reference to the data's module and according to the format, it can be used as a key for
+ * ly_ctx_get_module_implemented_ns() or ly_ctx_get_module_implemented(). While the module reference is always present,
+ * the id member can be omitted in case it is not present in the source data as a reference to the default namespace.
  */
 struct ly_prefix {
-    const char *pref;
-    const char *ns;
+    const char *id;               /**< identifier used in the qualified name of the item to reference data node namespace */
+    union {
+        const char *module_ns;    /**< namespace of the module where the data are supposed to belongs to, used for LYD_XML format. */
+        const char *module_name;  /**< name of the module where the data are supposed to belongs to, used for LYD_JSON format. */
+    };
 };
 
 /**
  * @brief Generic attribute structure.
  */
-struct ly_attr {
+struct lyd_attr {
     struct lyd_node_opaq *parent;   /**< data node where the attribute is placed */
-    struct ly_attr *next;
+    struct lyd_attr *next;
     struct ly_prefix *val_prefs;    /**< list of prefixes in the value ([sized array](@ref sizedarrays)) */
     const char *name;
     const char *value;
 
     LYD_FORMAT format;
+    int hint;                       /**< additional information about from the data source, see the [hints list](@ref lydopaqhints) */
     struct ly_prefix prefix;        /**< name prefix, it is stored because they are a real pain to generate properly */
 
 };
@@ -293,6 +302,37 @@
 /** @} */
 
 /**
+ * @brief Callback provided by the data/schema parsers to type plugins to resolve (format-specific) mapping between prefixes used
+ * in the value strings to the YANG schemas.
+ *
+ * Reverse function to ly_get_prefix_clb.
+ *
+ * XML uses XML namespaces, JSON uses schema names as prefixes, YIN/YANG uses prefixes of the imports.
+ *
+ * @param[in] ctx libyang context to find the schema.
+ * @param[in] prefix Prefix found in the value string
+ * @param[in] prefix_len Length of the @p prefix.
+ * @param[in] private Internal data needed by the callback.
+ * @return Pointer to the YANG schema identified by the provided prefix or NULL if no mapping found.
+ */
+typedef const struct lys_module *(*ly_resolve_prefix_clb)(const struct ly_ctx *ctx, const char *prefix, size_t prefix_len,
+                                                          void *private);
+
+/**
+ * @brief Callback provided by the data/schema printers to type plugins to resolve (format-specific) mapping between YANG module of a data object
+ * to prefixes used in the value strings.
+ *
+ * Reverse function to ly_resolve_prefix_clb.
+ *
+ * XML uses XML namespaces, JSON uses schema names as prefixes, YIN/YANG uses prefixes of the imports.
+ *
+ * @param[in] mod YANG module of the object.
+ * @param[in] private Internal data needed by the callback.
+ * @return String representing prefix for the object of the given YANG module @p mod.
+ */
+typedef const char *(*ly_get_prefix_clb)(const struct lys_module *mod, void *private);
+
+/**
  * @brief Generic structure for a data node.
  */
 struct lyd_node {
@@ -401,6 +441,23 @@
 };
 
 /**
+ * @defgroup lydopaqhints Opaq data node hints.
+ *
+ * Additional information about the opaq nodes from their source. The flags are stored in lyd_node_opaq::hint
+ * and they can have a slightly different meaning for the specific lyd_node_opaq::format.
+ * @{
+ */
+#define LYD_NODE_OPAQ_ISLIST       0x001 /**< LYD_JSON: node is expected to be a list or leaf-list */
+#define LYD_NODE_OPAQ_ISENVELOPE   0x002 /**< LYD_JSON, LYD_XML: RPC/Action/Notification envelope out of the YANG schemas */
+
+#define LYD_NODE_OPAQ_ISSTRING     0x100 /**< LYD_JSON: value is expected to be string as defined in JSON encoding. */
+#define LYD_NODE_OPAQ_ISNUMBER     0x200 /**< LYD_JSON: value is expected to be number as defined in JSON encoding. */
+#define LYD_NODE_OPAQ_ISBOOLEAN    0x400 /**< LYD_JSON: value is expected to be boolean as defined in JSON encoding. */
+#define LYD_NODE_OPAQ_ISEMPTY      0x800 /**< LYD_JSON: value is expected to be empty as defined in JSON encoding. */
+
+/** @} lydopaqhints */
+
+/**
  * @brief Data node structure for unparsed (opaque) nodes.
  */
 struct lyd_node_opaq {
@@ -410,7 +467,7 @@
     struct lyd_node *parent;        /**< pointer to the parent node (NULL in case of root node) */
     struct lyd_node *next;          /**< pointer to the next sibling node (NULL if there is no one) */
     struct lyd_node *prev;          /**< pointer to the previous sibling node (last sibling if there is none) */
-    struct ly_attr *attr;
+    struct lyd_attr *attr;
 
 #ifdef LY_ENABLED_LYD_PRIV
     void *priv;                     /**< private user data, not used by libyang */
@@ -422,6 +479,7 @@
     struct ly_prefix prefix;        /**< name prefix */
     struct ly_prefix *val_prefs;    /**< list of prefixes in the value ([sized array](@ref sizedarrays)) */
     const char *value;              /**< original value */
+    int hint;                       /**< additional information about from the data source, see the [hints list](@ref lydopaqhints) */
     const struct ly_ctx *ctx;       /**< libyang context */
 };
 
@@ -580,7 +638,7 @@
  * @return LY_ERR value.
  */
 LY_ERR lyd_new_attr(struct lyd_node *parent, const char *module_name, const char *name, const char *val_str,
-                    struct ly_attr **attr);
+                    struct lyd_attr **attr);
 
 /**
  * @defgroup pathoptions Data path creation options
@@ -845,7 +903,7 @@
  * @param[in] ctx Context where the attributes were created.
  * @param[in] attr Attribute to free.
  */
-void ly_free_attr_single(const struct ly_ctx *ctx, struct ly_attr *attr);
+void ly_free_attr_single(const struct ly_ctx *ctx, struct lyd_attr *attr);
 
 /**
  * @brief Free the attribute with any following attributes.
@@ -853,7 +911,7 @@
  * @param[in] ctx Context where the attributes were created.
  * @param[in] attr First attribute to free.
  */
-void ly_free_attr_siblings(const struct ly_ctx *ctx, struct ly_attr *attr);
+void ly_free_attr_siblings(const struct ly_ctx *ctx, struct lyd_attr *attr);
 
 /**
  * @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value.
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
index 7cd1283..519ce06 100644
--- a/src/tree_data_free.c
+++ b/src/tree_data_free.c
@@ -78,9 +78,9 @@
 }
 
 static void
-ly_free_attr(const struct ly_ctx *ctx, struct ly_attr *attr, int siblings)
+ly_free_attr(const struct ly_ctx *ctx, struct lyd_attr *attr, int siblings)
 {
-    struct ly_attr *iter;
+    struct lyd_attr *iter;
     LY_ARRAY_COUNT_TYPE u;
 
     LY_CHECK_ARG_RET(NULL, ctx, );
@@ -116,26 +116,26 @@
         iter = iter->next;
 
         LY_ARRAY_FOR(attr->val_prefs, u) {
-            FREE_STRING(ctx, attr->val_prefs[u].pref);
-            FREE_STRING(ctx, attr->val_prefs[u].ns);
+            FREE_STRING(ctx, attr->val_prefs[u].id);
+            FREE_STRING(ctx, attr->val_prefs[u].module_ns);
         }
         LY_ARRAY_FREE(attr->val_prefs);
         FREE_STRING(ctx, attr->name);
         FREE_STRING(ctx, attr->value);
-        FREE_STRING(ctx, attr->prefix.pref);
-        FREE_STRING(ctx, attr->prefix.ns);
+        FREE_STRING(ctx, attr->prefix.id);
+        FREE_STRING(ctx, attr->prefix.module_ns);
         free(attr);
     }
 }
 
 API void
-ly_free_attr_single(const struct ly_ctx *ctx, struct ly_attr *attr)
+ly_free_attr_single(const struct ly_ctx *ctx, struct lyd_attr *attr)
 {
     ly_free_attr(ctx, attr, 0);
 }
 
 API void
-ly_free_attr_siblings(const struct ly_ctx *ctx, struct ly_attr *attr)
+ly_free_attr_siblings(const struct ly_ctx *ctx, struct lyd_attr *attr)
 {
     ly_free_attr(ctx, attr, 1);
 }
@@ -146,8 +146,8 @@
     LY_ARRAY_COUNT_TYPE u;
 
     LY_ARRAY_FOR(val_prefs, u) {
-        FREE_STRING(ctx, val_prefs[u].pref);
-        FREE_STRING(ctx, val_prefs[u].ns);
+        FREE_STRING(ctx, val_prefs[u].id);
+        FREE_STRING(ctx, val_prefs[u].module_ns);
     }
     LY_ARRAY_FREE(val_prefs);
 }
@@ -176,8 +176,8 @@
         }
 
         FREE_STRING(LYD_NODE_CTX(opaq), opaq->name);
-        FREE_STRING(LYD_NODE_CTX(opaq), opaq->prefix.pref);
-        FREE_STRING(LYD_NODE_CTX(opaq), opaq->prefix.ns);
+        FREE_STRING(LYD_NODE_CTX(opaq), opaq->prefix.id);
+        FREE_STRING(LYD_NODE_CTX(opaq), opaq->prefix.module_ns);
         ly_free_val_prefs(LYD_NODE_CTX(opaq), opaq->val_prefs);
         FREE_STRING(LYD_NODE_CTX(opaq), opaq->value);
     } else if (node->schema->nodetype & LYD_NODE_INNER) {
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index 0e8a17b..3dce35c 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -188,7 +188,7 @@
     key = lyd_node_children(node, 0);
     while ((skey = lys_getnext(skey, node->schema, NULL, 0)) && (skey->flags & LYS_KEY)) {
         if (!key || (key->schema != skey)) {
-            LOGVAL(node->schema->module->ctx, LY_VLOG_LYD, node, LY_VCODE_NOKEY, skey->name);
+            LOGVAL(LYD_NODE_CTX(node), LY_VLOG_LYD, node, LY_VCODE_NOKEY, skey->name);
             return LY_EVALID;
         }
 
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index eb934ca..7911fe0 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -94,6 +94,7 @@
  * @param[in] value String value to be parsed.
  * @param[in] value_len Length of @p value, must be set correctly.
  * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
+ * @param[in] value_hint [Hint options](@ref lydvalueparseopts) from the parser regarding the value type.
  * @param[in] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
  * @param[in] prefix_data User data for @p get_prefix.
  * @param[in] format Input format of @p value.
@@ -102,8 +103,8 @@
  * @return LY_EINCOMPLETE in case data tree is needed to finish the validation.
  * @return LY_ERR value if an error occurred.
  */
-LY_ERR lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, int *dynamic,
-                       ly_clb_resolve_prefix get_prefix, void *prefix_data, LYD_FORMAT format, struct lyd_node **node);
+LY_ERR lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, int *dynamic, int value_hint,
+                       ly_resolve_prefix_clb get_prefix, void *prefix_data, LYD_FORMAT format, struct lyd_node **node);
 
 /**
  * @brief Create a term (leaf/leaf-list) node from a parsed value by duplicating it.
@@ -169,18 +170,20 @@
  * @param[in] value String value to be parsed.
  * @param[in] value_len Length of @p value, must be set correctly.
  * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
+ * @param[in] value_hint [Value hint](@ref lydvalueparseopts) from the parser regarding the value type.
  * @param[in] format Input format of @p value and @p ns.
  * @param[in] val_prefs Possible value prefixes, array is spent.
  * @param[in] prefix Element prefix.
  * @param[in] pref_len Length of @p prefix, must be set correctly.
- * @param[in] ns Node namespace, meaning depends on @p format.
+ * @param[in] module_key Mandatory key to reference module, can be namespace or name.
+ * @param[in] module_key_len Length of @p module_key, must be set correctly.
  * @param[out] node Created node.
  * @return LY_SUCCESS on success.
  * @return LY_ERR value if an error occurred.
  */
 LY_ERR lyd_create_opaq(const struct ly_ctx *ctx, const char *name, size_t name_len, const char *value, size_t value_len,
-                       int *dynamic, LYD_FORMAT format, struct ly_prefix *val_prefs, const char *prefix, size_t pref_len,
-                       const char *ns, struct lyd_node **node);
+                       int *dynamic, int value_hint, LYD_FORMAT format, struct ly_prefix *val_prefs, const char *prefix, size_t pref_len,
+                       const char *module_key, size_t module_key_len, struct lyd_node **node);
 
 /**
  * @brief Check the existence and create any non-existing implicit siblings, recursively for the created nodes.
@@ -229,6 +232,7 @@
  * @param[in] value String value to be parsed.
  * @param[in] value_len Length of @p value, must be set correctly.
  * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
+ * @param[in] value_hint [Value hint](@ref lydvalueparseopts) from the parser regarding the value type.
  * @param[in] resolve_prefix Parser-specific getter to resolve prefixes used in the @p value string.
  * @param[in] prefix_data User data for @p get_prefix.
  * @param[in] format Input format of @p value.
@@ -238,8 +242,17 @@
  * @return LY_ERR value if an error occurred.
  */
 LY_ERR lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod, const char *name,
-                       size_t name_len, const char *value, size_t value_len, int *dynamic, ly_clb_resolve_prefix resolve_prefix,
-                       void *prefix_data, LYD_FORMAT format, const struct lysc_node *ctx_snode);
+                       size_t name_len, const char *value, size_t value_len, int *dynamic, int value_hint,
+                       ly_resolve_prefix_clb resolve_prefix, void *prefix_data, LYD_FORMAT format, const struct lysc_node *ctx_snode);
+
+/**
+ * @brief Insert a metadata (last) into a parent
+ *
+ * @param[in] parent Parent of the metadata.
+ * @param[in] meta Metadata (list) to be added into the @p parent.
+ * @return LY_SUCCESS on success.
+ */
+LY_ERR lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta);
 
 /**
  * @brief Create and insert a generic attribute (last) into a parent.
@@ -252,17 +265,32 @@
  * @param[in] value String value to be parsed.
  * @param[in] value_len Length of @p value, must be set correctly.
  * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
+ * @param[in] value_hint [Value hint](@ref lydvalueparseopts) from the parser regarding the value type.
  * @param[in] format Input format of @p value and @p ns.
  * @param[in] val_prefs Possible value prefixes, array is spent.
  * @param[in] prefix Attribute prefix.
  * @param[in] prefix_len Attribute prefix length.
- * @param[in] ns Attribute namespace, meaning depends on @p format.
+ * @param[in] module_key Mandatory key to reference module, can be namespace or name.
+ * @param[in] module_key_len Length of @p module_key, must be set correctly.
  * @return LY_SUCCESS on success.
  * @return LY_ERR value if an error occurred.
  */
-LY_ERR ly_create_attr(struct lyd_node *parent, struct ly_attr **attr, const struct ly_ctx *ctx, const char *name,
-                      size_t name_len, const char *value, size_t value_len, int *dynamic, LYD_FORMAT format,
-                      struct ly_prefix *val_prefs, const char *prefix, size_t prefix_len, const char *ns);
+LY_ERR lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const struct ly_ctx *ctx, const char *name,
+                       size_t name_len, const char *value, size_t value_len, int *dynamic, int value_hint, LYD_FORMAT format,
+                       struct ly_prefix *val_prefs, const char *prefix, size_t prefix_len, const char *module_key, size_t module_key_len);
+
+/**
+ * @defgroup lydvalueparseopts Hint options for type plugin callbacks from the data parsers.
+ *
+ * Options applicable to ly_value_parse()
+ * @{
+ */
+#define LYD_VALUE_PARSE_ISSTRING LY_TYPE_OPTS_ISSTRING /**< The input value is supposed to be a string. */
+#define LYD_VALUE_PARSE_ISNUMBER LY_TYPE_OPTS_ISNUMBER /**< The input value is supposed to be a number. */
+#define LYD_VALUE_PARSE_ISBOOLEAN LY_TYPE_OPTS_ISBOOLEAN /**< The input value is supposed to be a boolean. */
+#define LYD_VALUE_PARSE_ISEMPTY LY_TYPE_OPTS_ISEMPTY /**< The input value is supposed to be empty type. */
+
+/** @} lydvalueparseopts */
 
 /**
  * @brief Validate, canonize and store the given @p value into the node according to the node's type's rules.
@@ -272,6 +300,7 @@
  * @param[in] value_len Length of the give @p value, must be set correctly.
  * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
  * @param[in] second Flag for the second call after returning LY_EINCOMPLETE
+ * @param[in] value_hint [Value hint](@ref lydvalueparseopts) from the parser.
  * @param[in] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
  * @param[in] parser Parser's data for @p get_prefix
  * @param[in] format Input format of @p value.
@@ -282,12 +311,12 @@
  * @return LY_EINCOMPLETE in case the @p trees is not provided and it was needed to finish the validation.
  * @return LY_ERR value if an error occurred.
  */
-LY_ERR lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int *dynamic, int second,
-                       ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node *tree);
+LY_ERR lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int *dynamic, int second, int value_hint,
+                       ly_resolve_prefix_clb get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node *tree);
 
 /* similar to lyd_value_parse except can be used just to store the value, hence does also not support a second call */
 LY_ERR lyd_value_store(struct lyd_value *val, const struct lysc_node *schema, const char *value, size_t value_len,
-                       int *dynamic, ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format);
+                       int *dynamic, ly_resolve_prefix_clb get_prefix, void *parser, LYD_FORMAT format);
 
 /**
  * @brief Validate, canonize and store the given @p value into the metadata according to the annotation type's rules.
@@ -298,6 +327,7 @@
  * @param[in] value_len Length of the give @p value, must be set correctly.
  * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
  * @param[in] second Flag for the second call after returning LY_EINCOMPLETE
+ * @param[in] value_hint [Value hint](@ref lydvalueparseopts) from the parser.
  * @param[in] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
  * @param[in] parser Parser's data for @p get_prefix
  * @param[in] format Input format of the data.
@@ -310,8 +340,8 @@
  * @return LY_ERR value if an error occurred.
  */
 LY_ERR lyd_value_parse_meta(const struct ly_ctx *ctx, struct lyd_meta *meta, const char *value, size_t value_len,
-                            int *dynamic, int second, ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,
-                            const struct lysc_node *ctx_snode, const struct lyd_node *tree);
+                            int *dynamic, int second, int value_hint, ly_resolve_prefix_clb get_prefix, void *parser,
+                            LYD_FORMAT format, const struct lysc_node *ctx_snode, const struct lyd_node *tree);
 
 /* generic function lys_value_validate */
 LY_ERR _lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
@@ -324,11 +354,12 @@
  * @param[in] in Input structure.
  * @param[in] parse_options Options for parser, see @ref dataparseroptions.
  * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
- * @param[out] tree Parsed data tree. Note that NULL can be a valid result.
+ * @param[out] tree_p Parsed data tree. Note that NULL can be a valid result.
+ * @param[out] lydctx_p Data parser context to finish validation.
  * @return LY_ERR value.
  */
 LY_ERR lyd_parse_xml_data(const struct ly_ctx *ctx, struct ly_in *in, int parse_options, int validate_options,
-                          struct lyd_node **tree);
+                          struct lyd_node **tree_p, struct lyd_ctx **lydctx_p);
 
 /**
  * @brief Parse XML string as YANG RPC/action invocation.
@@ -339,11 +370,11 @@
  *
  * @param[in] ctx libyang context.
  * @param[in] in Input structure.
- * @param[out] tree Parsed full RPC/action tree.
- * @param[out] op Optional pointer to the actual operation. Useful mainly for action.
+ * @param[out] tree_p Parsed full RPC/action tree.
+ * @param[out] op_p Optional pointer to the actual operation. Useful mainly for action.
  * @return LY_ERR value.
  */
-LY_ERR lyd_parse_xml_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree, struct lyd_node **op);
+LY_ERR lyd_parse_xml_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p);
 
 /**
  * @brief Parse XML string as YANG notification.
@@ -353,11 +384,11 @@
  *
  * @param[in] ctx libyang context.
  * @param[in] in Input structure.
- * @param[out] tree Parsed full notification tree.
- * @param[out] op Optional pointer to the actual notification. Useful mainly for nested notifications.
+ * @param[out] tree_p Parsed full notification tree.
+ * @param[out] op_p Optional pointer to the actual notification. Useful mainly for nested notifications.
  * @return LY_ERR value.
  */
-LY_ERR lyd_parse_xml_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree, struct lyd_node **ntf);
+LY_ERR lyd_parse_xml_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **ntf_p);
 
 /**
  * @brief Parse XML string as YANG RPC/action reply.
@@ -367,11 +398,67 @@
  *
  * @param[in] request Data tree of the RPC/action request.
  * @param[in] in Input structure.
- * @param[out] tree Parsed full reply tree. It always includes duplicated operation and parents of the @p request.
- * @param[out] op Optional pointer to the reply operation. Useful mainly for action.
+ * @param[out] tree_p Parsed full reply tree. It always includes duplicated operation and parents of the @p request.
+ * @param[out] op_p Optional pointer to the reply operation. Useful mainly for action.
  * @return LY_ERR value.
  */
-LY_ERR lyd_parse_xml_reply(const struct lyd_node *request, struct ly_in *in, struct lyd_node **tree, struct lyd_node **op);
+LY_ERR lyd_parse_xml_reply(const struct lyd_node *request, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p);
+
+/**
+ * @brief Parse JSON string as YANG data tree.
+ *
+ * @param[in] ctx libyang context
+ * @param[in] in Input structure.
+ * @param[in] parse_options Options for parser, see @ref dataparseroptions.
+ * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
+ * @param[out] tree_p Parsed data tree. Note that NULL can be a valid result.
+ * @param[out] lydctx_p Data parser context to finish validation.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_json_data(const struct ly_ctx *ctx, struct ly_in *in, int parse_options, int validate_options,
+                           struct lyd_node **tree_p, struct lyd_ctx **lydctx_p);
+
+/**
+ * @brief Parse JSON string as YANG notification.
+ *
+ * Optional top-level "notification" envelope object, if present, is [checked](https://tools.ietf.org/html/rfc5277#page-25)
+ * and parsed. Specifically the child "eventTime" member and its value.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] in Input structure.
+ * @param[out] tree_p Parsed full notification tree.
+ * @param[out] ntf_p Optional pointer to the actual notification. Useful mainly for nested notifications.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_json_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **ntf_p);
+
+/**
+ * @brief Parse JSON string as YANG RPC/action invocation.
+ *
+ * Optional top-level "rpc" envelope object, if present is is [checked](https://tools.ietf.org/html/rfc6241#section-4.1) and the parser
+ * goes inside for the content, which is an RPC data or "action" envelope objects. The "action" envelope object is
+ * also [checked](https://tools.ietf.org/html/rfc7950#section-7.15.2) and then an action data is expected as a content of this envelope.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] in Input structure.
+ * @param[out] tree_p Parsed full RPC/action tree.
+ * @param[out] op_p Optional pointer to the actual operation. Useful mainly for action.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_json_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p);
+
+/**
+ * @brief Parse JSON string as YANG RPC/action reply.
+ *
+ * Optional "rpc-reply" envelope object, if present, is [checked](https://tools.ietf.org/html/rfc6241#section-4.2).
+ *
+ * @param[in] request Data tree of the RPC/action request.
+ * @param[in] in Input structure.
+ * @param[out] tree_p Parsed full reply tree. It always includes duplicated operation and parents of the @p request.
+ * @param[out] op_p Optional pointer to the reply operation. Useful mainly for action.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_json_reply(const struct lyd_node *request, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p);
 
 /**
  * @brief Parse binary data as YANG data tree.
@@ -380,44 +467,45 @@
  * @param[in] in Input structure.
  * @param[in] parse_options Options for parser, see @ref dataparseroptions.
  * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
- * @param[out] tree Parsed data tree. Note that NULL can be a valid result.
+ * @param[out] tree_p Parsed data tree. Note that NULL can be a valid result.
+ * @param[out] lydctx_p Data parser context to finish validation.
  * @return LY_ERR value.
  */
 LY_ERR lyd_parse_lyb_data(const struct ly_ctx *ctx, struct ly_in *in, int parse_options, int validate_options,
-                          struct lyd_node **tree);
+                          struct lyd_node **tree_p, struct lyd_ctx **lydctx_p);
 
 /**
  * @brief Parse binary data as YANG RPC/action invocation.
  *
  * @param[in] ctx libyang context.
  * @param[in] in Input structure.
- * @param[out] tree Parsed full RPC/action tree.
- * @param[out] op Optional pointer to the actual operation. Useful mainly for action.
+ * @param[out] tree_p Parsed full RPC/action tree.
+ * @param[out] op_p Optional pointer to the actual operation. Useful mainly for action.
  * @return LY_ERR value.
  */
-LY_ERR lyd_parse_lyb_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree, struct lyd_node **op);
+LY_ERR lyd_parse_lyb_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p);
 
 /**
  * @brief Parse binary data as YANG notification.
  *
  * @param[in] ctx libyang context.
  * @param[in] in Input structure.
- * @param[out] tree Parsed full notification tree.
- * @param[out] op Optional pointer to the actual notification. Useful mainly for nested notifications.
+ * @param[out] tree_p Parsed full notification tree.
+ * @param[out] ntf_p Optional pointer to the actual notification. Useful mainly for nested notifications.
  * @return LY_ERR value.
  */
-LY_ERR lyd_parse_lyb_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree, struct lyd_node **ntf);
+LY_ERR lyd_parse_lyb_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **ntf_p);
 
 /**
  * @brief Parse binary data as YANG RPC/action reply.
  *
  * @param[in] request Data tree of the RPC/action request.
  * @param[in] in Input structure.
- * @param[out] tree Parsed full reply tree. It always includes duplicated operation and parents of the @p request.
- * @param[out] op Optional pointer to the reply operation. Useful mainly for action.
+ * @param[out] tree_p Parsed full reply tree. It always includes duplicated operation and parents of the @p request.
+ * @param[out] op_p Optional pointer to the reply operation. Useful mainly for action.
  * @return LY_ERR value.
  */
-LY_ERR lyd_parse_lyb_reply(const struct lyd_node *request, struct ly_in *in, struct lyd_node **tree, struct lyd_node **op);
+LY_ERR lyd_parse_lyb_reply(const struct lyd_node *request, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p);
 
 /**
  * @defgroup datahash Data nodes hash manipulation
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 0dd45b2..5b8c1a4 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -1637,7 +1637,7 @@
 /**
  * @brief Schema mapping of YANG modules to prefixes in values.
  *
- * Implementation of ly_clb_get_prefix. Inverse function to lys_resolve_prefix.
+ * Implementation of ly_get_prefix_clb. Inverse function to lys_resolve_prefix.
  *
  * In this case the @p mod is searched in the list of imports and the import's prefix
  * (not the module's itself) prefix is returned.
@@ -1664,7 +1664,7 @@
 /**
  * @brief Schema mapping of prefix in values to YANG modules (imports).
  *
- * Implementation of ly_clb_resolve_prefix. Inverse function to lys_get_prefix().
+ * Implementation of ly_resolve_prefix_clb. Inverse function to lys_get_prefix().
  *
  * In this case the @p prefix is searched in the list of imports' prefixes (not the prefixes of the imported modules themselves).
  */
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index db11a38..f6c7958 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -222,7 +222,7 @@
 /**
  * @brief Schema mapping of YANG modules to prefixes in values.
  *
- * Implementation of ly_clb_get_prefix. Inverse function to lys_resolve_prefix.
+ * Implementation of ly_get_prefix_clb. Inverse function to lys_resolve_prefix.
  *
  * In this case the @p mod is searched in the list of imports and the import's prefix
  * (not the module's itself) prefix is returned.
@@ -232,7 +232,7 @@
 /**
  * @brief Schema mapping of prefix in values to YANG modules (imports).
  *
- * Implementation of ly_clb_resolve_prefix. Inverse function to lys_get_prefix().
+ * Implementation of ly_resolve_prefix_clb. Inverse function to lys_get_prefix().
  *
  * In this case the @p prefix is searched in the list of imports' prefixes (not the prefixes of the imported modules themselves).
  */
diff --git a/src/validation.c b/src/validation.c
index 9c39173..65c64e7 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -111,7 +111,7 @@
 
 LY_ERR
 lyd_validate_unres(struct lyd_node **tree, struct ly_set *node_when, struct ly_set *node_types, struct ly_set *meta_types,
-                   LYD_FORMAT format, ly_clb_resolve_prefix get_prefix_clb, void *parser_data, struct lyd_node **diff)
+                   LYD_FORMAT format, ly_resolve_prefix_clb get_prefix_clb, void *parser_data, struct lyd_node **diff)
 {
     LY_ERR ret = LY_SUCCESS;
     uint32_t i;
@@ -172,7 +172,7 @@
             struct lyd_node_term *node = (struct lyd_node_term *)node_types->objs[i];
 
             /* validate and store the value of the node */
-            ret = lyd_value_parse(node, node->value.original, strlen(node->value.original), 0, 1, get_prefix_clb,
+            ret = lyd_value_parse(node, node->value.original, strlen(node->value.original), 0, 1, 0, get_prefix_clb,
                                   parser_data, format, *tree);
             LY_CHECK_RET(ret);
 
@@ -191,7 +191,7 @@
 
             /* validate and store the value of the metadata */
             ret = lyd_value_parse_meta(meta->parent->schema->module->ctx, meta, meta->value.original,
-                                       strlen(meta->value.original), 0, 1, get_prefix_clb, parser_data, format, NULL, *tree);
+                                       strlen(meta->value.original), 0, 1, 0, get_prefix_clb, parser_data, format, NULL, *tree);
             LY_CHECK_RET(ret);
 
             /* remove this attr from the set */
diff --git a/src/validation.h b/src/validation.h
index 41a47a5..8b23e6f 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -48,7 +48,7 @@
  * @return LY_ERR value.
  */
 LY_ERR lyd_validate_unres(struct lyd_node **tree, struct ly_set *node_when, struct ly_set *node_types, struct ly_set *meta_types,
-                          LYD_FORMAT format, ly_clb_resolve_prefix get_prefix_clb, void *parser_data, struct lyd_node **diff);
+                          LYD_FORMAT format, ly_resolve_prefix_clb get_prefix_clb, void *parser_data, struct lyd_node **diff);
 
 /**
  * @brief Validate new siblings. Specifically, check duplicated instances, autodelete default values and cases.
diff --git a/src/xml.c b/src/xml.c
index d1ba184..a3645c1 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -1103,15 +1103,15 @@
 
                     /* check whether we do not already have this prefix stored */
                     LY_ARRAY_FOR(prefixes, u) {
-                        if (!ly_strncmp(prefixes[u].pref, start, len)) {
+                        if (!ly_strncmp(prefixes[u].id, start, len)) {
                             p = &prefixes[u];
                             break;
                         }
                     }
                     if (!p) {
                         LY_ARRAY_NEW_GOTO(xmlctx->ctx, prefixes, p, ret, error);
-                        p->pref = lydict_insert(xmlctx->ctx, start, len);
-                        p->ns = lydict_insert(xmlctx->ctx, ns->uri, 0);
+                        p->id = lydict_insert(xmlctx->ctx, start, len);
+                        p->module_ns = lydict_insert(xmlctx->ctx, ns->uri, 0);
                     } /* else the prefix already present */
                 }
             }
@@ -1124,7 +1124,8 @@
 
 error:
     LY_ARRAY_FOR(prefixes, u) {
-        lydict_remove(xmlctx->ctx, prefixes[u].pref);
+        lydict_remove(xmlctx->ctx, prefixes[u].id);
+        lydict_remove(xmlctx->ctx, prefixes[u].module_ns);
     }
     LY_ARRAY_FREE(prefixes);
     return ret;
@@ -1154,9 +1155,9 @@
             if (prefs1) {
                 /* find module of the first prefix, if any */
                 LY_ARRAY_FOR(prefs1, u1) {
-                    len = strlen(prefs1[u1].pref);
-                    if (!strncmp(ptr1, prefs1[u1].pref, len) && (ptr1[len] == ':')) {
-                        ns1 = prefs1[u1].ns;
+                    len = strlen(prefs1[u1].id);
+                    if (!strncmp(ptr1, prefs1[u1].id, len) && (ptr1[len] == ':')) {
+                        ns1 = prefs1[u1].module_ns;
                         break;
                     }
                 }
@@ -1164,9 +1165,9 @@
             if (prefs2) {
                 /* find module of the second prefix, if any */
                 LY_ARRAY_FOR(prefs2, u2) {
-                    len = strlen(prefs2[u2].pref);
-                    if (!strncmp(ptr2, prefs2[u2].pref, len) && (ptr2[len] == ':')) {
-                        ns2 = prefs2[u2].ns;
+                    len = strlen(prefs2[u2].id);
+                    if (!strncmp(ptr2, prefs2[u2].id, len) && (ptr2[len] == ':')) {
+                        ns2 = prefs2[u2].module_ns;
                         break;
                     }
                 }
@@ -1178,8 +1179,8 @@
             }
 
             /* skip prefixes in both values (':' is skipped as iter) */
-            ptr1 += strlen(prefs1[u1].pref);
-            ptr2 += strlen(prefs2[u2].pref);
+            ptr1 += strlen(prefs1[u1].id);
+            ptr2 += strlen(prefs2[u2].id);
         }
 
         ++ptr1;
diff --git a/src/xml.h b/src/xml.h
index b89a283..fef0bb5 100644
--- a/src/xml.h
+++ b/src/xml.h
@@ -76,6 +76,10 @@
 };
 
 struct lyxml_ctx {
+    const struct ly_ctx *ctx;
+    uint64_t line;          /* current line */
+    struct ly_in *in;       /* input structure */
+
     enum LYXML_PARSER_STATUS status; /* status providing information about the last parsed object, following attributes
                                         are filled based on it */
     union {
@@ -95,9 +99,6 @@
         int dynamic;        /* LYXML_ELEM_CONTENT, LYXML_ATTR_CONTENT - whether elem/attr value is dynamically allocated */
     };
 
-    const struct ly_ctx *ctx;
-    uint64_t line;          /* current line */
-    struct ly_in *in;       /* input structure */
     struct ly_set elements; /* list of not-yet-closed elements */
     struct ly_set ns;       /* handled with LY_SET_OPT_USEASLIST */
 };
diff --git a/tests/utests/CMakeLists.txt b/tests/utests/CMakeLists.txt
index 5180113..dcf1910 100644
--- a/tests/utests/CMakeLists.txt
+++ b/tests/utests/CMakeLists.txt
@@ -17,6 +17,7 @@
 ly_add_utest(NAME new SOURCES data/test_new.c)
 ly_add_utest(NAME parser_xml SOURCES data/test_parser_xml.c)
 ly_add_utest(NAME printer_xml SOURCES data/test_printer_xml.c)
+ly_add_utest(NAME parser_json SOURCES data/test_parser_json.c)
 ly_add_utest(NAME lyb SOURCES data/test_lyb.c)
 ly_add_utest(NAME validation SOURCES data/test_validation.c)
 ly_add_utest(NAME types SOURCES data/test_types.c)
diff --git a/tests/utests/data/test_new.c b/tests/utests/data/test_new.c
index 21eb126..50a7740 100644
--- a/tests/utests/data/test_new.c
+++ b/tests/utests/data/test_new.c
@@ -196,14 +196,14 @@
     opq = (struct lyd_node_opaq *)root;
     assert_string_equal(opq->name, "node1");
     assert_string_equal(opq->value, "");
-    assert_string_equal(opq->prefix.ns, "my-module");
+    assert_string_equal(opq->prefix.module_name, "my-module");
 
     assert_int_equal(lyd_new_opaq(root, NULL, "node2", "value", "my-module2", &node), LY_SUCCESS);
     assert_null(node->schema);
     opq = (struct lyd_node_opaq *)node;
     assert_string_equal(opq->name, "node2");
     assert_string_equal(opq->value, "value");
-    assert_string_equal(opq->prefix.ns, "my-module2");
+    assert_string_equal(opq->prefix.module_name, "my-module2");
     assert_ptr_equal(opq->parent, root);
 
     lyd_free_tree(root);
diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c
new file mode 100644
index 0000000..7539dc7
--- /dev/null
+++ b/tests/utests/data/test_parser_json.c
@@ -0,0 +1,809 @@
+/*
+ * @file test_parser_xml.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from parser_xml.c
+ *
+ * Copyright (c) 2019 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.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "context.h"
+#include "parser.h"
+#include "parser_data.h"
+#include "printer.h"
+#include "printer_data.h"
+#include "tests/config.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+
+#define BUFSIZE 1024
+char logbuf[BUFSIZE] = {0};
+int store = -1; /* negative for infinite logging, positive for limited logging */
+
+struct ly_ctx *ctx; /* context for tests */
+
+/* set to 0 to printing error messages to stderr instead of checking them in code */
+#define ENABLE_LOGGER_CHECKING 1
+
+#if ENABLE_LOGGER_CHECKING
+static void
+logger(LY_LOG_LEVEL level, const char *msg, const char *path)
+{
+    (void) level; /* unused */
+    if (store) {
+        if (path && path[0]) {
+            snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
+        } else {
+            strncpy(logbuf, msg, BUFSIZE - 1);
+        }
+        if (store > 0) {
+            --store;
+        }
+    }
+}
+#endif
+
+static int
+setup(void **state)
+{
+    (void) state; /* unused */
+
+    const char *schema_a = "module a {namespace urn:tests:a;prefix a;yang-version 1.1; import ietf-yang-metadata {prefix md;}"
+            "md:annotation hint { type int8;}"
+            "list l1 { key \"a b c\"; leaf a {type string;} leaf b {type string;} leaf c {type int16;} leaf d {type string;}}"
+            "leaf foo { type string;}"
+            "container c {"
+                "leaf x {type string;}"
+                "action act { input { leaf al {type string;} } output { leaf al {type uint8;} } }"
+                "notification n1 { leaf nl {type string;} }"
+            "}"
+            "container cp {presence \"container switch\"; leaf y {type string;} leaf z {type int8;}}"
+            "anydata any {config false;}"
+            "leaf-list ll1 { type uint8; }"
+            "leaf foo2 { type string; default \"default-val\"; }"
+            "leaf foo3 { type uint32; }"
+            "notification n2;}";
+    const struct lys_module *mod;
+
+#if ENABLE_LOGGER_CHECKING
+    ly_set_log_clb(logger, 1);
+#endif
+
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &ctx));
+    assert_non_null(ly_ctx_load_module(ctx, "ietf-netconf-with-defaults", "2011-06-01"));
+    assert_non_null((mod = ly_ctx_load_module(ctx, "ietf-netconf", "2011-06-01")));
+    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "writable-running"));
+    assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL));
+
+    return 0;
+}
+
+static int
+teardown(void **state)
+{
+#if ENABLE_LOGGER_CHECKING
+    if (*state) {
+        fprintf(stderr, "%s\n", logbuf);
+    }
+#else
+    (void) state; /* unused */
+#endif
+
+    ly_ctx_destroy(ctx, NULL);
+    ctx = NULL;
+
+    return 0;
+}
+
+void
+logbuf_clean(void)
+{
+    logbuf[0] = '\0';
+}
+
+#if ENABLE_LOGGER_CHECKING
+#   define logbuf_assert(str) assert_string_equal(logbuf, str)
+#else
+#   define logbuf_assert(str)
+#endif
+
+static void
+test_leaf(void **state)
+{
+    *state = test_leaf;
+
+    const char *data = "{\"a:foo\":\"foo value\"}";
+    struct lyd_node *tree;
+    struct lyd_node_term *leaf;
+
+    char *printed;
+    struct ly_out *out;
+    assert_int_equal(LY_SUCCESS, ly_out_new_memory(&printed, 0, &out));
+
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("foo", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("foo value", leaf->value.original);
+
+    assert_int_equal(LYS_LEAF, tree->next->next->schema->nodetype);
+    assert_string_equal("foo2", tree->next->next->schema->name);
+    leaf = (struct lyd_node_term*)tree->next->next;
+    assert_string_equal("default-val", leaf->value.original);
+    assert_true(leaf->flags & LYD_DEFAULT);
+
+    lyd_print_tree(out, tree, LYD_JSON, 0);
+    assert_string_equal(printed, data);
+    ly_out_reset(out);
+    lyd_free_all(tree);
+
+    /* make foo2 explicit */
+    data = "{\"a:foo2\":\"default-val\"}";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("foo2", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("default-val", leaf->value.original);
+    assert_false(leaf->flags & LYD_DEFAULT);
+
+    lyd_print_tree(out, tree, LYD_JSON, 0);
+    assert_string_equal(printed, data);
+    ly_out_reset(out);
+    lyd_free_all(tree);
+
+    /* parse foo2 but make it implicit */
+    data = "{\"a:foo2\" : \"default-val\", \"@a:foo2\" : { \"ietf-netconf-with-defaults:default\" : true }}";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("foo2", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("default-val", leaf->value.original);
+    assert_true(leaf->flags & LYD_DEFAULT);
+
+    lyd_free_all(tree);
+
+    /* multiple meatadata hint and unknown metadata xxx supposed to be skipped since it is from missing schema */
+    data = "{\"@a:foo\" : { \"a:hint\" : 1, \"a:hint\" : 2, \"x:xxx\" : { \"value\" : \"/x:no/x:yes\" }}, \"a:foo\" : \"xxx\"}";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("foo", tree->schema->name);
+    assert_non_null(tree->meta);
+    assert_string_equal("hint", tree->meta->name);
+    assert_int_equal(LY_TYPE_INT8, tree->meta->value.realtype->basetype);
+    assert_string_equal("1", tree->meta->value.original);
+    assert_int_equal(1, tree->meta->value.int8);
+    assert_ptr_equal(tree, tree->meta->parent);
+    assert_non_null(tree->meta->next);
+    assert_string_equal("hint", tree->meta->next->name);
+    assert_int_equal(LY_TYPE_INT8, tree->meta->next->value.realtype->basetype);
+    assert_string_equal("2", tree->meta->next->value.original);
+    assert_int_equal(2, tree->meta->next->value.int8);
+    assert_ptr_equal(tree, tree->meta->next->parent);
+    assert_null(tree->meta->next->next);
+
+    ly_out_free(out, NULL, 1);
+    lyd_free_all(tree);
+
+    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("Unknown (or not implemented) YANG module \"x\" for metadata \"x:xxx\". /a:foo");
+
+    /* missing referenced metadata node */
+    data = "{\"@a:foo\" : { \"a:hint\" : 1 }}";
+    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("Missing JSON data instance to be coupled with @a:foo metadata. /");
+
+    /* missing namespace for meatadata*/
+    data = "{\"a:foo\" : \"value\", \"@a:foo\" : { \"hint\" : 1 }}";
+    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("Metadata in JSON must be namespace-qualified, missing prefix for \"hint\". /a:foo");
+
+    *state = NULL;
+}
+
+static void
+test_leaflist(void **state)
+{
+    *state = test_leaflist;
+
+    const char *data = "{ \"a:ll1\" : [10,11] }";
+    struct lyd_node *tree;
+    struct lyd_node_term *ll;
+
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_LEAFLIST, tree->schema->nodetype);
+    assert_string_equal("ll1", tree->schema->name);
+    ll = (struct lyd_node_term*)tree;
+    assert_string_equal("10", ll->value.original);
+
+    assert_non_null(tree->next);
+    assert_int_equal(LYS_LEAFLIST, tree->next->schema->nodetype);
+    assert_string_equal("ll1", tree->next->schema->name);
+    ll = (struct lyd_node_term*)tree->next;
+    assert_string_equal("11", ll->value.original);
+
+    lyd_free_all(tree);
+
+    /* simple metadata */
+    data = "{ \"a:ll1\" : [10,11], \"@a:ll1\" : [null, {\"a:hint\" : 2}] }";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_LEAFLIST, tree->schema->nodetype);
+    assert_string_equal("ll1", tree->schema->name);
+    ll = (struct lyd_node_term*)tree;
+    assert_string_equal("10", ll->value.original);
+    assert_null(ll->meta);
+
+    assert_non_null(tree->next);
+    assert_int_equal(LYS_LEAFLIST, tree->next->schema->nodetype);
+    assert_string_equal("ll1", tree->next->schema->name);
+    ll = (struct lyd_node_term*)tree->next;
+    assert_string_equal("11", ll->value.original);
+    assert_non_null(ll->meta);
+    assert_string_equal("2", ll->meta->value.original);
+    assert_null(ll->meta->next);
+
+    lyd_free_all(tree);
+
+        /* multiple meatadata hint and unknown metadata xxx supposed to be skipped since it is from missing schema */
+    data = "{\"@a:ll1\" : [{\"a:hint\" : 1, \"x:xxx\" :  { \"value\" : \"/x:no/x:yes\" }, \"a:hint\" : 10},null,{\"a:hint\" : 3}], \"a:ll1\" : [1,2,3]}";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_LEAFLIST, tree->schema->nodetype);
+    assert_string_equal("ll1", tree->schema->name);
+    ll = (struct lyd_node_term*)tree;
+    assert_string_equal("1", ll->value.original);
+    assert_non_null(ll->meta);
+    assert_string_equal("hint", ll->meta->name);
+    assert_int_equal(LY_TYPE_INT8, ll->meta->value.realtype->basetype);
+    assert_string_equal("1", ll->meta->value.original);
+    assert_int_equal(1, ll->meta->value.int8);
+    assert_ptr_equal(ll, ll->meta->parent);
+    assert_non_null(ll->meta->next);
+    assert_string_equal("hint", ll->meta->next->name);
+    assert_int_equal(LY_TYPE_INT8, ll->meta->next->value.realtype->basetype);
+    assert_string_equal("10", ll->meta->next->value.original);
+    assert_int_equal(10, ll->meta->next->value.int8);
+    assert_ptr_equal(ll, ll->meta->next->parent);
+    assert_null(ll->meta->next->next);
+
+    assert_non_null(tree->next);
+    assert_int_equal(LYS_LEAFLIST, tree->next->schema->nodetype);
+    assert_string_equal("ll1", tree->next->schema->name);
+    ll = (struct lyd_node_term*)tree->next;
+    assert_string_equal("2", ll->value.original);
+    assert_null(ll->meta);
+
+    assert_non_null(tree->next->next);
+    assert_int_equal(LYS_LEAFLIST, tree->next->next->schema->nodetype);
+    assert_string_equal("ll1", tree->next->next->schema->name);
+    ll = (struct lyd_node_term*)tree->next->next;
+    assert_string_equal("3", ll->value.original);
+    assert_non_null(ll->meta);
+    assert_string_equal("hint", ll->meta->name);
+    assert_int_equal(LY_TYPE_INT8, ll->meta->value.realtype->basetype);
+    assert_string_equal("3", ll->meta->value.original);
+    assert_int_equal(3, ll->meta->value.int8);
+    assert_ptr_equal(ll, ll->meta->parent);
+    assert_null(ll->meta->next);
+
+    lyd_free_all(tree);
+
+    /* missing referenced metadata node */
+    data = "{\"@a:ll1\":[{\"a:hint\":1}]}";
+    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("Missing JSON data instance to be coupled with @a:ll1 metadata. /");
+
+    data = "{\"a:ll1\":[1],\"@a:ll1\":[{\"a:hint\":1},{\"a:hint\":2}]}";
+    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("Missing JSON data instance no. 2 of a:ll1 to be coupled with metadata. /");
+
+    data = "{\"@a:ll1\":[{\"a:hint\":1},{\"a:hint\":2},{\"a:hint\":3}],\"a:ll1\" : [1, 2]}";
+    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("Missing 3rd JSON data instance to be coupled with @a:ll1 metadata. /");
+
+    *state = NULL;
+}
+
+static void
+test_anydata(void **state)
+{
+    *state = test_anydata;
+
+    const char *data;
+    char *str;
+    struct lyd_node *tree;
+
+    struct ly_out *out;
+    assert_int_equal(LY_SUCCESS, ly_out_new_memory(&str, 0, &out));
+
+    data = "{\"a:any\":{\"x:element1\":{\"element2\":\"/a:some/a:path\",\"list\":[{},{\"key\":\"a\"}]}}}";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_ANYDATA, tree->schema->nodetype);
+    assert_string_equal("any", tree->schema->name);
+
+    lyd_print_tree(out, tree, LYD_JSON, 0);
+    assert_string_equal(str, data);
+    ly_out_reset(out);
+
+    lyd_free_all(tree);
+    ly_out_free(out, NULL, 1);
+
+    *state = NULL;
+}
+
+static void
+test_list(void **state)
+{
+    *state = test_list;
+
+    const char *data = "{ \"a:l1\" : [{ \"a\":\"one\",\"b\":\"one\",\"c\":1 }]} ";
+    struct lyd_node *tree, *iter;
+    struct lyd_node_inner *list;
+    struct lyd_node_term *leaf;
+
+    /* check hashes */
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_LIST, tree->schema->nodetype);
+    assert_string_equal("l1", tree->schema->name);
+    list = (struct lyd_node_inner*)tree;
+    LY_LIST_FOR(list->child, iter) {
+        assert_int_not_equal(0, iter->hash);
+    }
+    lyd_free_all(tree);
+
+    /* missing keys */
+    data = "{ \"a:l1\": [ {\"c\" : 1, \"b\" : \"b\"}]}";
+    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("List instance is missing its key \"a\". /a:l1[b='b'][c='1']");
+
+    data = "{ \"a:l1\": [ {\"a\" : \"a\"}]}";
+    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("List instance is missing its key \"b\". /a:l1[a='a']");
+
+    data = "{ \"a:l1\": [ {\"b\" : \"b\", \"a\" : \"a\"}]}";
+    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("List instance is missing its key \"c\". /a:l1[a='a'][b='b']");
+
+    /* key duplicate */
+    data = "{ \"a:l1\": [ {\"c\" : 1, \"b\" : \"b\", \"a\" : \"a\", \"c\" : 1}]}";
+    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("Duplicate instance of \"c\". /a:l1[a='a'][b='b'][c='1'][c='1']/c");
+
+    /* keys order, in contrast to XML, JSON accepts keys in any order even in strict mode */
+    logbuf_clean();
+    data = "{ \"a:l1\": [ {\"d\" : \"d\", \"a\" : \"a\", \"c\" : 1, \"b\" : \"b\"}]}";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_LIST, tree->schema->nodetype);
+    assert_string_equal("l1", tree->schema->name);
+    list = (struct lyd_node_inner*)tree;
+    assert_non_null(leaf = (struct lyd_node_term*)list->child);
+    assert_string_equal("a", leaf->schema->name);
+    assert_non_null(leaf = (struct lyd_node_term*)leaf->next);
+    assert_string_equal("b", leaf->schema->name);
+    assert_non_null(leaf = (struct lyd_node_term*)leaf->next);
+    assert_string_equal("c", leaf->schema->name);
+    assert_non_null(leaf = (struct lyd_node_term*)leaf->next);
+    assert_string_equal("d", leaf->schema->name);
+    logbuf_assert("");
+    lyd_free_all(tree);
+
+    /*  */
+    data = "{ \"a:l1\": [ {\"c\" : 1, \"b\" : \"b\", \"a\" : \"a\"}]}";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_LIST, tree->schema->nodetype);
+    assert_string_equal("l1", tree->schema->name);
+    list = (struct lyd_node_inner*)tree;
+    assert_non_null(leaf = (struct lyd_node_term*)list->child);
+    assert_string_equal("a", leaf->schema->name);
+    assert_non_null(leaf = (struct lyd_node_term*)leaf->next);
+    assert_string_equal("b", leaf->schema->name);
+    assert_non_null(leaf = (struct lyd_node_term*)leaf->next);
+    assert_string_equal("c", leaf->schema->name);
+    logbuf_assert("");
+    lyd_free_all(tree);
+
+    data = "{\"a:cp\" : { \"@\" : { \"a:hint\" : 1 }}}";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_CONTAINER, tree->schema->nodetype);
+    assert_string_equal("cp", tree->schema->name);
+    assert_non_null(tree->meta);
+    assert_string_equal("hint", tree->meta->name);
+    assert_string_equal("1", tree->meta->value.original);
+    assert_ptr_equal(tree, tree->meta->parent);
+    assert_null(tree->meta->next);
+    lyd_free_all(tree);
+
+    *state = NULL;
+}
+
+static void
+test_container(void **state)
+{
+    *state = test_container;
+
+    const char *data = " {\"a:c\" : {}}";
+    struct lyd_node *tree;
+    struct lyd_node_inner *cont;
+
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_CONTAINER, tree->schema->nodetype);
+    assert_string_equal("c", tree->schema->name);
+    cont = (struct lyd_node_inner*)tree;
+    assert_true(cont->flags & LYD_DEFAULT);
+    lyd_free_all(tree);
+
+    data = "{\"a:cp\" : {}}";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_int_equal(LYS_CONTAINER, tree->schema->nodetype);
+    assert_string_equal("cp", tree->schema->name);
+    cont = (struct lyd_node_inner*)tree;
+    assert_false(cont->flags & LYD_DEFAULT);
+    lyd_free_all(tree);
+
+    *state = NULL;
+}
+
+static void
+test_opaq(void **state)
+{
+    *state = test_opaq;
+
+    const char *data;
+    char *str;
+    struct lyd_node *tree;
+
+    struct ly_out *out;
+    assert_int_equal(LY_SUCCESS, ly_out_new_memory(&str, 0, &out));
+
+    /* invalid value, no flags */
+    data = "{\"a:foo3\":[null]}";
+    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("Invalid empty uint32 value. /a:foo3");
+    assert_null(tree);
+
+    /* opaq flag */
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, LYD_PARSE_OPAQ, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_null(tree->schema);
+    assert_string_equal(((struct lyd_node_opaq *)tree)->name, "foo3");
+    assert_string_equal(((struct lyd_node_opaq *)tree)->value, "");
+
+    lyd_print_tree(out, tree, LYD_JSON, 0);
+    assert_string_equal(str, data);
+    ly_out_reset(out);
+    lyd_free_all(tree);
+
+    /* missing key, no flags */
+    data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"d\":\"val_d\"}]}";
+    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("List instance is missing its key \"c\". /a:l1[a='val_a'][b='val_b']");
+    assert_null(tree);
+
+    /* opaq flag */
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, LYD_PARSE_OPAQ, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_null(tree->schema);
+    assert_string_equal(((struct lyd_node_opaq *)tree)->name, "l1");
+    assert_string_equal(((struct lyd_node_opaq *)tree)->value, "");
+
+    lyd_print_tree(out, tree, LYD_JSON, 0);
+    assert_string_equal(str, data);
+    ly_out_reset(out);
+    lyd_free_all(tree);
+
+    /* invalid key, no flags */
+    data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}";
+    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("Invalid int16 value \"val_c\". /a:l1/c");
+    assert_null(tree);
+
+    /* opaq flag */
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, LYD_PARSE_OPAQ, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_null(tree->schema);
+    assert_string_equal(((struct lyd_node_opaq *)tree)->name, "l1");
+    assert_string_equal(((struct lyd_node_opaq *)tree)->value, "");
+
+    lyd_print_tree(out, tree, LYD_JSON, 0);
+    assert_string_equal(str, data);
+    ly_out_reset(out);
+    lyd_free_all(tree);
+
+    data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":{\"val\":\"val_c\"}}]}";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, LYD_PARSE_OPAQ, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_null(tree->schema);
+    assert_string_equal(((struct lyd_node_opaq *)tree)->name, "l1");
+    assert_string_equal(((struct lyd_node_opaq *)tree)->value, "");
+
+    lyd_print_tree(out, tree, LYD_JSON, 0);
+    assert_string_equal(str, data);
+    ly_out_reset(out);
+    lyd_free_all(tree);
+
+    data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\"}]}";
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_JSON, LYD_PARSE_OPAQ, LYD_VALIDATE_PRESENT, &tree));
+    assert_non_null(tree);
+    assert_null(tree->schema);
+    assert_string_equal(((struct lyd_node_opaq *)tree)->name, "l1");
+    assert_string_equal(((struct lyd_node_opaq *)tree)->value, "");
+
+    lyd_print_tree(out, tree, LYD_JSON, 0);
+    assert_string_equal(str, data);
+    ly_out_reset(out);
+    lyd_free_all(tree);
+
+    ly_out_free(out, NULL, 1);
+
+    *state = NULL;
+}
+
+static void
+test_rpc(void **state)
+{
+    *state = test_rpc;
+
+    const char *data;
+    struct ly_in *in;
+    char *str;
+    struct lyd_node *tree, *op;
+    const struct lyd_node *node;
+
+    struct ly_out *out;
+    assert_int_equal(LY_SUCCESS, ly_out_new_memory(&str, 0, &out));
+
+    data = "{\"ietf-netconf:rpc\":{\"edit-config\":{"
+              "\"target\":{\"running\":[null]},"
+              "\"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_rpc(ctx, in, LYD_JSON, &tree, &op));
+    ly_in_free(in, 0);
+
+    assert_non_null(op);
+    assert_string_equal(op->schema->name, "edit-config");
+
+    assert_non_null(tree);
+    assert_null(tree->schema);
+    assert_string_equal(((struct lyd_node_opaq *)tree)->name, "rpc");
+    /* TODO support generic attributes in JSON ?
+    assert_non_null(((struct lyd_node_opaq *)tree)->attr);
+    */
+    node = lyd_node_children(tree, 0);
+    assert_string_equal(node->schema->name, "edit-config");
+    node = lyd_node_children(node, 0)->next;
+    assert_string_equal(node->schema->name, "config");
+
+    node = ((struct lyd_node_any *)node)->value.tree;
+    assert_non_null(node->schema);
+    assert_string_equal(node->schema->name, "cp");
+    node = lyd_node_children(node, 0);
+    /* z has no value */
+    assert_null(node->schema);
+    assert_string_equal(((struct lyd_node_opaq *)node)->name, "z");
+    node = node->parent->next;
+    /* l1 key c has invalid value so it is at the end */
+    assert_null(node->schema);
+    assert_string_equal(((struct lyd_node_opaq *)node)->name, "l1");
+
+    lyd_print_tree(out, tree, LYD_JSON, 0);
+    assert_string_equal(str, data);
+    ly_out_reset(out);
+    lyd_free_all(tree);
+
+    /* wrong namespace, element name, whatever... */
+    /* TODO */
+
+    ly_out_free(out, NULL, 1);
+
+    *state = NULL;
+}
+
+static void
+test_action(void **state)
+{
+    *state = test_action;
+
+    const char *data;
+    struct ly_in *in;
+    char *str;
+    struct lyd_node *tree, *op;
+    const struct lyd_node *node;
+
+    struct ly_out *out;
+    assert_int_equal(LY_SUCCESS, ly_out_new_memory(&str, 0, &out));
+
+    data = "{\"ietf-netconf:rpc\":{\"yang:action\":{\"a:c\":{\"act\":{\"al\":\"value\"}}}}}";
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+    assert_int_equal(LY_SUCCESS, lyd_parse_rpc(ctx, in, LYD_JSON, &tree, &op));
+    ly_in_free(in, 0);
+
+    assert_non_null(op);
+    assert_string_equal(op->schema->name, "act");
+
+    assert_non_null(tree);
+    assert_null(tree->schema);
+    assert_string_equal(((struct lyd_node_opaq *)tree)->name, "rpc");
+    assert_null(((struct lyd_node_opaq *)tree)->attr);
+    node = lyd_node_children(tree, 0);
+    assert_null(node->schema);
+    assert_string_equal(((struct lyd_node_opaq *)node)->name, "action");
+    assert_null(((struct lyd_node_opaq *)node)->attr);
+
+    lyd_print_tree(out, tree, LYD_JSON, 0);
+    assert_string_equal(str, data);
+    ly_out_reset(out);
+    lyd_free_all(tree);
+
+    /* wrong namespace, element name, whatever... */
+    /* TODO */
+
+    ly_out_free(out, NULL, 1);
+
+    *state = NULL;
+}
+
+static void
+test_notification(void **state)
+{
+    *state = test_notification;
+
+    const char *data;
+    struct ly_in *in;
+    char *str;
+    struct lyd_node *tree, *ntf;
+    const struct lyd_node *node;
+
+    struct ly_out *out;
+    assert_int_equal(LY_SUCCESS, ly_out_new_memory(&str, 0, &out));
+
+    data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2037-07-08T00: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_notif(ctx, in, LYD_JSON, &tree, &ntf));
+    ly_in_free(in, 0);
+
+    assert_non_null(ntf);
+    assert_string_equal(ntf->schema->name, "n1");
+
+    assert_non_null(tree);
+    assert_null(tree->schema);
+    assert_string_equal(((struct lyd_node_opaq *)tree)->name, "notification");
+    assert_null(((struct lyd_node_opaq *)tree)->attr);
+    node = lyd_node_children(tree, 0);
+    assert_null(node->schema);
+    assert_string_equal(((struct lyd_node_opaq *)node)->name, "eventTime");
+    assert_string_equal(((struct lyd_node_opaq *)node)->value, "2037-07-08T00:01:00Z");
+    assert_null(((struct lyd_node_opaq *)node)->attr);
+    node = node->next;
+    assert_non_null(node->schema);
+    assert_string_equal(node->schema->name, "c");
+
+    lyd_print_tree(out, tree, LYD_JSON, 0);
+    assert_string_equal(str, data);
+    ly_out_reset(out);
+    lyd_free_all(tree);
+
+    /* top-level notif without envelope */
+    data = "{\"a:n2\":{}}";
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+    assert_int_equal(LY_SUCCESS, lyd_parse_notif(ctx, in, LYD_JSON, &tree, &ntf));
+    ly_in_free(in, 0);
+
+    assert_non_null(ntf);
+    assert_string_equal(ntf->schema->name, "n2");
+
+    assert_non_null(tree);
+    assert_ptr_equal(ntf, tree);
+
+    lyd_print_tree(out, tree, LYD_JSON, 0);
+    assert_string_equal(str, data);
+    ly_out_reset(out);
+    lyd_free_all(tree);
+
+    /* wrong namespace, element name, whatever... */
+    /* TODO */
+
+    ly_out_free(out, NULL, 1);
+
+    *state = NULL;
+}
+
+static void
+test_reply(void **state)
+{
+    *state = test_reply;
+
+    const char *data;
+    struct ly_in *in;
+    char *str;
+    struct lyd_node *request, *tree, *op;
+    const struct lyd_node *node;
+
+    struct ly_out *out;
+    assert_int_equal(LY_SUCCESS, ly_out_new_memory(&str, 0, &out));
+
+    data = "{\"a:c\":{\"act\":{\"al\":\"value\"}}}";
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+    assert_int_equal(LY_SUCCESS, lyd_parse_rpc(ctx, in, LYD_JSON, &request, NULL));
+    ly_in_free(in, 0);
+
+    data = "{\"ietf-netconf:rpc-reply\":{\"a:al\":25}}";
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+    assert_int_equal(LY_SUCCESS, lyd_parse_reply(request, in, LYD_JSON, &tree, &op));
+    ly_in_free(in, 0);
+    lyd_free_all(request);
+
+    assert_non_null(op);
+    assert_string_equal(op->schema->name, "act");
+    node = lyd_node_children(op, 0);
+    assert_non_null(node->schema);
+    assert_string_equal(node->schema->name, "al");
+    assert_true(node->schema->flags & LYS_CONFIG_R);
+
+    assert_non_null(tree);
+    assert_null(tree->schema);
+    assert_string_equal(((struct lyd_node_opaq *)tree)->name, "rpc-reply");
+    node = lyd_node_children(tree, 0);
+    assert_non_null(node->schema);
+    assert_string_equal(node->schema->name, "c");
+
+    /* TODO print only rpc-reply node and then output subtree */
+    lyd_print_tree(out, lyd_node_children(op, 0), LYD_JSON, 0);
+    assert_string_equal(str, "{\"a:al\":25}");
+    ly_out_reset(out);
+    lyd_print_tree(out, lyd_node_children(tree, 0), LYD_JSON, 0);
+    assert_string_equal(str, "{\"a:c\":{\"act\":{\"al\":25}}}");
+    ly_out_reset(out);
+    lyd_free_all(tree);
+
+    /* wrong namespace, element name, whatever... */
+    /* TODO */
+
+    ly_out_free(out, NULL, 1);
+
+    *state = NULL;
+}
+
+int main(void)
+{
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_leaf, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_leaflist, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_anydata, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_list, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_container, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_opaq, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_rpc, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_action, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_notification, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_reply, setup, teardown),
+    };
+
+    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 b7a6952..077c8b4 100644
--- a/tests/utests/data/test_parser_xml.c
+++ b/tests/utests/data/test_parser_xml.c
@@ -154,8 +154,8 @@
 
     lyd_free_all(tree);
 
-    /* parse foo2 but make it implicit */
-    data = "<foo2 xmlns=\"urn:tests:a\" xmlns:wd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" wd:default=\"true\">default-val</foo2>";
+    /* parse foo2 but make it implicit, skip metadata xxx from missing schema */
+    data = "<foo2 xmlns=\"urn:tests:a\" xmlns:wd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" wd:default=\"true\" xmlns:x=\"urn:x\" x:xxx=\"false\">default-val</foo2>";
     assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
     assert_non_null(tree);
     assert_int_equal(LYS_LEAF, tree->schema->nodetype);
@@ -387,8 +387,8 @@
     /* opaq flag and fail */
     assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, "<a xmlns=\"ns\"><b>x</b><c xml:id=\"D\">1</c></a>", LYD_XML,
                                                    LYD_PARSE_OPAQ, LYD_VALIDATE_PRESENT, &tree));
+    logbuf_assert("Unknown XML prefix \"xml\". Line number 1.");
     assert_null(tree);
-
     ly_out_free(out, NULL, 1);
 
     *state = NULL;
diff --git a/tests/utests/data/test_types.c b/tests/utests/data/test_types.c
index 02ca2cf..cad0808 100644
--- a/tests/utests/data/test_types.c
+++ b/tests/utests/data/test_types.c
@@ -943,10 +943,10 @@
               "<a:inst xmlns:a=\"urn:tests:types\">/a:list2[a:id='a'][a:value='a']/a:id</a:inst>", LY_SUCCESS, "");
 
     /* key-predicate */
-    data = "/types:list2[types:id='a'][types:value='b']/types:id";
+    data = "/types:list2[id='a'][value='b']/id";
     assert_int_equal(LY_ENOTFOUND, lyd_value_validate(s->ctx, (const struct lyd_node_term*)tree->prev->prev, data, strlen(data),
                                                    tree, NULL));
-    logbuf_assert("Invalid instance-identifier \"/types:list2[types:id='a'][types:value='b']/types:id\" value - instance not found. /");
+    logbuf_assert("Invalid instance-identifier \"/types:list2[id='a'][value='b']/id\" value - instance not found. /");
     /* leaf-list-predicate */
     data = "/types:leaflisttarget[.='c']";
     assert_int_equal(LY_ENOTFOUND, lyd_value_validate(s->ctx, (const struct lyd_node_term*)tree->prev->prev, data, strlen(data),
diff --git a/tests/utests/data/test_validation.c b/tests/utests/data/test_validation.c
index f223a6d..309cc71 100644
--- a/tests/utests/data/test_validation.c
+++ b/tests/utests/data/test_validation.c
@@ -1346,7 +1346,7 @@
     "</cont>";
     assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_NO_STATE, 0, &tree));
     assert_null(tree);
-    logbuf_assert("Invalid state data node \"cont2\" found. Line number 1.");
+    logbuf_assert("Invalid state data node \"cont2\" found. /h:cont/cont2");
 
     assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT | LYD_VALIDATE_NO_STATE, &tree));
     assert_null(tree);