data types CHANGE validate instance-identifier values

Quite a big patch dut to the problems with instance-identifiers:
- different format in XML and JSON
- prefixes must be resolved correctly even in predicates to be able to
compare values in predicates with other values (even another
instance-identifier, prefixed identityrefs or, in contrast, strings
where 'something:' is not a prefix).
diff --git a/src/common.c b/src/common.c
index 76f3b84..3c36baa 100644
--- a/src/common.c
+++ b/src/common.c
@@ -425,9 +425,9 @@
 }
 
 LY_ERR
-ly_parse_instance_predicate(const char **pred, size_t limit,
-                             const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
-                             const char **errmsg)
+ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
+                            const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
+                            const char **errmsg)
 {
     LY_ERR ret = LY_EVALID;
     const char *in = *pred;
@@ -473,6 +473,11 @@
             *errmsg = "Invalid node-identifier.";
             goto error;
         }
+        if (format == LYD_XML && !(*prefix)) {
+            /* all node names MUST be qualified with explicit namespace prefix */
+            *errmsg = "Missing prefix of a node name.";
+            goto error;
+        }
         offset = in - *pred;
         in = *pred;
         expr = 2;
@@ -500,7 +505,7 @@
             goto error;
         }
         *value = &in[offset];
-        for (;offset < limit && in[offset] != quot; offset++);
+        for (;offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++);
         if (in[offset] == quot) {
             *value_len = &in[offset] - *value;
             offset++;
diff --git a/src/common.h b/src/common.h
index 437a90f..1c18a13 100644
--- a/src/common.h
+++ b/src/common.h
@@ -403,18 +403,19 @@
  * (even for error case, so it can be used to determine which substring caused failure).
  * @param[in] limit Limiting length of the @p pred. Function expects NULL terminated string which is not overread.
  * The limit value is not checked with each character, so it can be overread and the failure is detected later.
+ * @param[in] format Input format of the data containing the @p pred.
  * @param[out] prefix Start of the node-identifier's prefix if any, NULL in case of pos or leaf-list-predicate rules.
  * @param[out] prefix_len Length of the parsed @p prefix.
  * @param[out] id Start of the node-identifier's identifier string, NULL in case of pos rule, "." in case of leaf-list-predicate rule.
  * @param[out] id_len Length of the parsed @p id.
- * @param[out] value Start of the quoted-string (without quotation marks), NULL in case of pos rule.
+ * @param[out] value Start of the quoted-string (without quotation marks), not NULL in case of success.
  * @param[out] value_len Length of the parsed @p value.
  * @param[out] errmsg Error message string in case of error.
  * @return LY_SUCCESS in case a complete predicate was parsed.
  * @return LY_EVALID in case of invalid predicate form.
  * @return LY_EINVAL in case of reaching @p limit when parsing @p pred.
  */
-LY_ERR ly_parse_instance_predicate(const char **pred, size_t limit,
+LY_ERR ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
                                    const char **prefix, size_t *prefix_len, const char **id, size_t *id_len,
                                    const char **value, size_t *value_len, const char **errmsg);
 
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 8f625c7..6e15b60 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -254,7 +254,7 @@
                 value = "";
                 value_len = 0;
             }
-            ret = lyd_value_parse((struct lyd_node_term*)cur, value, value_len, dynamic, lydxml_resolve_prefix, ctx, NULL);
+            ret = lyd_value_parse((struct lyd_node_term*)cur, value, value_len, dynamic, lydxml_resolve_prefix, ctx, LYD_XML, NULL);
             if (ret == LY_EINCOMPLETE) {
                 ly_set_add(&ctx->incomplete_type_validation, cur, LY_SET_OPT_USEASLIST);
             } else if (ret) {
@@ -315,7 +315,8 @@
                 *tree = *result;
             }
             /* validate and store the value of the node */
-            ret = lyd_value_parse(node, node->value.canonized, strlen(node->value.canonized), 0, lydxml_resolve_prefix, ctx, trees);
+            ret = lyd_value_parse(node, node->value.canonized, node->value.canonized ? strlen(node->value.canonized) : 0, 0,
+                                  lydxml_resolve_prefix, ctx, LYD_XML, trees);
             LY_ARRAY_FREE(trees);
             if (ret) {
                 lyd_free_all(*result);
diff --git a/src/plugins_types.c b/src/plugins_types.c
index fb0d899..7564591 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -25,6 +25,8 @@
 #include "dict.h"
 #include "tree_schema.h"
 #include "tree_schema_internal.h"
+#include "tree_data_internal.h"
+#include "xml.h"
 #include "xpath.h"
 
 API LY_ERR
@@ -58,6 +60,58 @@
     return LY_EVALID;
 }
 
+API struct lyd_value_prefix *
+ly_type_get_prefixes(struct ly_ctx *ctx, const char *value, size_t value_len, ly_clb_resolve_prefix get_prefix, void *parser)
+{
+    LY_ERR ret;
+    const char *start, *stop;
+    struct lyd_value_prefix *prefixes = NULL;
+    const struct lys_module *mod;
+    unsigned int u;
+
+    for (stop = start = value; (size_t)(stop - value) <= value_len; stop++, start++) {
+        if (is_xmlqnamestartchar(*start)) {
+            int i = 1;
+            while (is_xmlqnamechar(stop[i])) {
+                i++;
+            }
+            if (stop[i] == ':') {
+                /* we have a possible prefix */
+                struct lyd_value_prefix *p;
+                size_t len = &stop[i] - start;
+                mod = NULL;
+
+                LY_ARRAY_FOR(prefixes, u) {
+                    if (!strncmp(prefixes[u].prefix, start, len) && prefixes[u].prefix[len] == '\0') {
+                        mod = prefixes[u].mod;
+                        break;
+                    }
+                }
+                if (!mod) {
+                    mod = get_prefix(ctx, start, len, parser);
+                    if (mod) {
+                        LY_ARRAY_NEW_GOTO(ctx, prefixes, p, ret, error);
+                        p->mod = mod;
+                        p->prefix = lydict_insert(ctx, start, len);
+                    }
+                } /* else the prefix already present */
+            }
+            start = stop = &stop[i];
+        }
+    }
+
+    return prefixes;
+
+error:
+    LY_ARRAY_FOR(prefixes, u) {
+        lydict_remove(ctx, prefixes[u].prefix);
+    }
+    LY_ARRAY_FREE(prefixes);
+
+    (void)ret;
+    return NULL;
+}
+
 API LY_ERR
 ly_type_parse_uint(const char *datatype, int base, uint64_t max, const char *value, size_t value_len, uint64_t *ret, struct ly_err_item **err)
 {
@@ -325,8 +379,8 @@
  */
 static LY_ERR
 ly_type_validate_int(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                     ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser),
-                     struct lyd_node *UNUSED(context_node), struct lyd_node **UNUSED(trees),
+                     ly_clb_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                     const void *UNUSED(context_node), struct lyd_node **UNUSED(trees),
                      const char **canonized, struct ly_err_item **err, void **priv)
 {
     LY_ERR ret;
@@ -415,8 +469,8 @@
  */
 static LY_ERR
 ly_type_validate_uint(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                      ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser),
-                      struct lyd_node *UNUSED(context_node), struct lyd_node **UNUSED(trees),
+                      ly_clb_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                      const void *UNUSED(context_node), struct lyd_node **UNUSED(trees),
                       const char **canonized, struct ly_err_item **err, void **priv)
 {
     LY_ERR ret;
@@ -487,8 +541,8 @@
  */
 static LY_ERR
 ly_type_validate_decimal64(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                           ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser),
-                           struct lyd_node *UNUSED(context_node), struct lyd_node **UNUSED(trees),
+                           ly_clb_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                           const void *UNUSED(context_node), struct lyd_node **UNUSED(trees),
                            const char **canonized, struct ly_err_item **err, void **priv)
 {
     int64_t d;
@@ -587,19 +641,21 @@
  */
 static LY_ERR
 ly_type_validate_binary(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                        ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser),
-                        struct lyd_node *UNUSED(context_node), struct lyd_node **UNUSED(trees),
+                        ly_clb_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                        const void *UNUSED(context_node), struct lyd_node **UNUSED(trees),
                         const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
 {
     size_t start = 0, stop = 0, count = 0, u, termination = 0;
     struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
     char *errmsg;
 
+    LY_CHECK_ARG_RET(ctx, value, LY_EINVAL);
+
     /* initiate */
     *err = NULL;
 
     /* validate characters and remember the number of octets for length validation */
-    if (value && value_len) {
+    if (value_len) {
         /* silently skip leading/trailing whitespaces */
         for (start = 0; (start < value_len) && isspace(value[start]); start++);
         for (stop = value_len - 1; stop > start && isspace(value[stop]); stop--);
@@ -654,7 +710,7 @@
     }
 
     if (options & LY_TYPE_OPTS_CANONIZE) {
-        if (start != 0 || stop != value_len) {
+        if (start != 0 || (value_len && stop != value_len - 1)) {
             *canonized = lydict_insert_zc(ctx, strndup(&value[start], stop + 1 - start));
         } else if (options & LY_TYPE_OPTS_DYNAMIC) {
             *canonized = lydict_insert_zc(ctx, (char*)value);
@@ -683,8 +739,8 @@
  */
 static LY_ERR
 ly_type_validate_string(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                        ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser),
-                        struct lyd_node *UNUSED(context_node), struct lyd_node **UNUSED(trees),
+                        ly_clb_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                        const void *UNUSED(context_node), struct lyd_node **UNUSED(trees),
                         const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
 {
     struct lysc_type_str *type_str = (struct lysc_type_str *)type;
@@ -722,8 +778,8 @@
  */
 static LY_ERR
 ly_type_validate_bits(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                      ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser),
-                      struct lyd_node *UNUSED(context_node), struct lyd_node **UNUSED(trees),
+                      ly_clb_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                      const void *UNUSED(context_node), struct lyd_node **UNUSED(trees),
                       const char **canonized, struct ly_err_item **err, void **priv)
 {
     LY_ERR ret = LY_EVALID;
@@ -916,8 +972,8 @@
  */
 static LY_ERR
 ly_type_validate_enum(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                      ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser),
-                      struct lyd_node *UNUSED(context_node), struct lyd_node **UNUSED(trees),
+                      ly_clb_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                      const void *UNUSED(context_node), struct lyd_node **UNUSED(trees),
                       const char **canonized, struct ly_err_item **err, void **priv)
 {
     unsigned int u, v;
@@ -1001,8 +1057,8 @@
  */
 static LY_ERR
 ly_type_validate_boolean(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
-                         ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser),
-                         struct lyd_node *UNUSED(context_node), struct lyd_node **UNUSED(trees),
+                         ly_clb_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                         const void *UNUSED(context_node), struct lyd_node **UNUSED(trees),
                          const char **canonized, struct ly_err_item **err, void **priv)
 {
     int8_t i;
@@ -1072,8 +1128,8 @@
  */
 static LY_ERR
 ly_type_validate_empty(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
-                       ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser),
-                       struct lyd_node *UNUSED(context_node), struct lyd_node **UNUSED(trees),
+                       ly_clb_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
+                       const void *UNUSED(context_node), struct lyd_node **UNUSED(trees),
                        const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
 {
     if (value_len) {
@@ -1089,6 +1145,18 @@
     return LY_SUCCESS;
 }
 
+/**
+ * @brief Comparison callback for built-in empty type.
+ *
+ * Implementation of the ly_type_compare_clb.
+ */
+static LY_ERR
+ly_type_compare_empty(const struct lyd_value *UNUSED(val1), const struct lyd_value *UNUSED(val2))
+{
+    /* empty has just one value, so empty data must be always the same */
+    return LY_SUCCESS;
+}
+
 API LY_ERR
 ly_type_identity_isderived(struct lysc_ident *base, struct lysc_ident *der)
 {
@@ -1112,8 +1180,8 @@
  */
 static LY_ERR
 ly_type_validate_identityref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                             ly_type_resolve_prefix get_prefix, void *parser,
-                             struct lyd_node *UNUSED(context_node), struct lyd_node **UNUSED(trees),
+                             ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT UNUSED(format),
+                             const void *UNUSED(context_node), struct lyd_node **UNUSED(trees),
                              const char **canonized, struct ly_err_item **err, void **priv)
 {
     struct lysc_type_identityref *type_ident = (struct lysc_type_identityref *)type;
@@ -1217,14 +1285,27 @@
 }
 
 /**
- * @brief Validation of instance-identifier - check presence of the specific node in the (data/schema) tree.
+ * @brief Comparison callback for built-in identityref type.
+ *
+ * Implementation of the ly_type_compare_clb.
+ */
+static LY_ERR
+ly_type_compare_identityref(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+    if (val1->ident == val2->ident) {
+        return LY_SUCCESS;
+    }
+    return LY_EVALID;
+}
+
+/**
+ * @brief Helper function for ly_type_validate_instanceid() to check presence of the specific node in the (data/schema) tree.
  *
  * In the case the instance-identifier type does not require instance (@p require_instance is 0) or the data @p trees
  * are not complete (called in the middle of data parsing), the @p token is checked only to match schema tree.
  * Otherwise, the provided data @p trees are used to find instance of the node specified by the token and @p node_d as
  * its parent.
  *
- * @param[in] ctx libyang context
  * @param[in] orig Complete instance-identifier expression for logging.
  * @param[in] orig_len Length of the @p orig string.
  * @param[in] options [Type plugin options ](@ref plugintypeopts) - only LY_TYPE_OPTS_INCOMPLETE_DATA is used.
@@ -1232,13 +1313,12 @@
  * If the flag is zero, the data tree is not needed and the @p token is checked only by checking the schema tree.
  * @param[in,out] token Pointer to the specific position inside the @p orig string where the node-identifier starts.
  * The pointer is updated to point after the processed node-identifier.
- * @param[in,out] prefixes [Sized array](@ref sizedarrays) of known mappings between prefix used in the @p orig and modules from the context.
+ * @param[in] prefixes [Sized array](@ref sizedarrays) of known mappings between prefix used in the @p orig and modules from the context.
+ * @param[in] format Format of the input YANG data, since XML and JSON format differs in case of instance-identifiers.
  * @param[in,out] node_s Parent schema node as input, resolved schema node as output. Alternative parameter for @p node_d
  * in case the instance is not available (@p trees are not yet complete) or required.
  * @param[in,out] node_d Parent data node as input, resolved data node instance as output. Alternative parameter for @p node_s
  * in case the instance is required and @p trees are complete.
- * @param[in] get_prefix Callback to resolve (map it to a schema) prefix used in the @p token.
- * @param[in] parser Context data for @p get_prefix callback.
  * @param[in] trees [Sized array](@ref sizedarrays)) of data trees where the data instance is supposed to be present.
  * @param[out] errmsg Error message in case of failure. Function does not log on its own, instead it creates error message. Caller is supposed to
  * free (or store somewhere) the returned message.
@@ -1246,55 +1326,48 @@
  * @return LY_EMEM or LY_EVALID in case of failure or when the node is not present in the schema/data tree.
  */
 static LY_ERR
-ly_type_validate_instanceid_checknodeid(struct ly_ctx *ctx, const char *orig, size_t orig_len, int options, int require_instance,
-                                        const char **token, struct lyd_value_prefix **prefixes,
+ly_type_validate_instanceid_checknodeid(const char *orig, size_t orig_len, int options, int require_instance,
+                                        const char **token, struct lyd_value_prefix *prefixes, LYD_FORMAT format,
                                         const struct lysc_node **node_s, const struct lyd_node **node_d,
-                                        ly_type_resolve_prefix get_prefix, void *parser, struct lyd_node **trees,
-                                        char **errmsg)
+                                        struct lyd_node **trees, char **errmsg)
 {
     const char *id, *prefix;
     size_t id_len, prefix_len;
     const struct lys_module *mod = NULL;
     unsigned int u;
-    int present_prefix = 0;
 
     if (ly_parse_nodeid(token, &prefix, &prefix_len, &id, &id_len)) {
-        asprintf(errmsg, "Invalid instance-identifier \"%.*s\" value at character %lu.",
-                 (int)orig_len, orig, *token - orig + 1);
+        asprintf(errmsg, "Invalid instance-identifier \"%.*s\" value at character %lu (%.*s).",
+                 (int)orig_len, orig, *token - orig + 1, (int)(orig_len - (*token - orig)), *token);
         return LY_EVALID;
     }
     if (!prefix || !prefix_len) {
-        asprintf(errmsg, "Invalid instance-identifier \"%.*s\" value - all node names (%.*s) MUST be qualified with explicit namespace prefix.",
-                 (int)orig_len, orig, (int)id_len + 1, &id[-1]);
-        return LY_EVALID;
-    }
-
-    /* map prefix to schema module */
-    LY_ARRAY_FOR(*prefixes, u) {
-        if (!strncmp((*prefixes)[u].prefix, prefix, prefix_len) && (*prefixes)[u].prefix[prefix_len] == '\0') {
-            mod = (*prefixes)[u].mod;
-            present_prefix = 1;
-            break;
+        if (format == LYD_XML || (!*node_s && !*node_d)) {
+            asprintf(errmsg, "Invalid instance-identifier \"%.*s\" value - all node names (%.*s) MUST be qualified with explicit namespace prefix.",
+                     (int)orig_len, orig, (int)id_len + 1, &id[-1]);
+            return LY_EVALID;
         }
-    }
-    if (!mod && get_prefix) {
-        mod = get_prefix(ctx, prefix, prefix_len, parser);
-    }
-    if (!mod) {
-        asprintf(errmsg, "Invalid instance-identifier \"%.*s\" value - unable to map prefix \"%.*s\" to YANG schema.",
-                 (int)orig_len, orig, (int)prefix_len, prefix);
-        return LY_EVALID;
-    }
-    if (!present_prefix) {
-        /* store the prefix record for later use */
-        struct lyd_value_prefix *p;
 
-        *errmsg = strdup("Memory allocation failed.");
-        LY_ARRAY_NEW_RET(ctx, *prefixes, p, LY_EMEM);
-        free(*errmsg);
-        *errmsg = NULL;
-        p->mod = mod;
-        p->prefix = lydict_insert(ctx, prefix, prefix_len);
+        /* non top-level node from JSON */
+        if (*node_d) {
+            mod = (*node_d)->schema->module;
+        } else {
+            mod = (*node_s)->module;
+        }
+    } else {
+        /* map prefix to schema module */
+        LY_ARRAY_FOR(prefixes, u) {
+            if (!strncmp(prefixes[u].prefix, prefix, prefix_len) && prefixes[u].prefix[prefix_len] == '\0') {
+                mod = prefixes[u].mod;
+                break;
+            }
+        }
+
+        if (!mod) {
+            asprintf(errmsg, "Invalid instance-identifier \"%.*s\" value - unable to map prefix \"%.*s\" to YANG schema.",
+                     (int)orig_len, orig, (int)prefix_len, prefix);
+            return LY_EVALID;
+        }
     }
 
     if ((options & LY_TYPE_OPTS_INCOMPLETE_DATA) || !require_instance) {
@@ -1336,30 +1409,173 @@
 }
 
 /**
+ * @brief Helper function for ly_type_validate_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.
+ */
+static const struct lys_module *
+ly_type_validate_instanceid_get_prefix(struct ly_ctx *UNUSED(ctx), const char *prefix, size_t prefix_len, void *private)
+{
+    struct lyd_value_prefix *prefixes = (struct lyd_value_prefix*)private;
+    unsigned int u;
+
+    LY_ARRAY_FOR(prefixes, u) {
+        if (!strncmp(prefixes[u].prefix, prefix, prefix_len) && prefixes[u].prefix[prefix_len] == '\0') {
+            return prefixes[u].mod;
+        }
+    }
+    return NULL;
+}
+
+/**
+ * @brief Helper function for ly_type_validate_instanceid() to prepare predicate's value structure into the lyd_value_path structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] key Schema key node of the predicate (list's key in case of key-predicate, leaf-list in case of leaf-list-predicate).
+ * @param[in] val Value string of the predicate.
+ * @param[in] val_len Length of the @p val.
+ * @param[in] prefixes [Sized array](@ref sizedarrays) of the prefix mappings used in the instance-identifier value.
+ * @param[in] format Input format of the data.
+ * @param[in,out] pred Prepared predicate structure where the predicate information will be added.
+ * @param[out] errmsg Error description in case the function fails. Caller is responsible to free the string.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_type_validate_instanceid_parse_predicate_value(struct ly_ctx *ctx, const struct lysc_node *key, const char *val, size_t val_len,
+                                                  struct lyd_value_prefix *prefixes, LYD_FORMAT format,
+                                                  struct lyd_value_path_predicate *pred, char **errmsg)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lysc_type *type;
+    struct ly_err_item *err = NULL;
+    void *priv = NULL;
+    int options = LY_TYPE_OPTS_VALIDATE | LY_TYPE_OPTS_CANONIZE | LY_TYPE_OPTS_STORE | LY_TYPE_OPTS_INCOMPLETE_DATA;
+
+    pred->value = calloc(1, sizeof *pred->value);
+
+    type = ((struct lysc_node_leaf*)key)->type;
+    if (type->plugin->validate) {
+        ret = type->plugin->validate(ctx, type, val, val_len, options,
+                                    ly_type_validate_instanceid_get_prefix, prefixes, format, key, NULL,
+                                    &pred->value->canonized, &err, &priv);
+        if (ret == LY_EINCOMPLETE) {
+            /* actually expected success without complete data */
+            ret = LY_SUCCESS;
+        } else if (ret) {
+            if (err) {
+                *errmsg = err->msg;
+                err->msg = NULL;
+                ly_err_free(err);
+            } else {
+                *errmsg = strdup("Type validation failed with unknown error.");
+            }
+            goto error;
+        }
+    } else {
+        pred->value->canonized = lydict_insert(ctx, val, val_len);
+    }
+    pred->value->plugin = type->plugin;
+
+    if (type->plugin->store) {
+        ret = type->plugin->store(ctx, type, options, pred->value, &err, &priv);
+        if (ret) {
+            if (err) {
+                *errmsg = err->msg;
+                err->msg = NULL;
+                ly_err_free(err);
+            } else {
+                *errmsg = strdup("Data storing failed with unknown error.");
+            }
+            goto error;
+        }
+    }
+
+error:
+    return ret;
+}
+
+/**
+ * @brief Helper function for ly_type_validate_instanceid() to correctly find the end of the predicate section.
+ *
+ * @param[in] predicate Start of the beginning section.
+ * @return Pointer to the end of the predicate section.
+ */
+static const char *
+ly_type_validate_instanceid_predicate_end(const char *predicate)
+{
+    size_t i = 0;
+    while (predicate[i] != ']') {
+        if (predicate[i] == '\'') {
+            i++;
+            while (predicate[i] != '\'') {
+                i++;
+            }
+        }
+        if (predicate[i] == '"') {
+            i++;
+            while (predicate[i] != '"') {
+                if (predicate[i] == '\\') {
+                    i += 2;
+                    continue;
+                }
+                i++;
+            }
+        }
+        i++;
+    }
+    return &predicate[i];
+}
+
+/**
  * @brief Validate value of the YANG built-in instance-identifier type.
  *
  * Implementation of the ly_type_validate_clb.
  */
 static LY_ERR
 ly_type_validate_instanceid(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                             ly_type_resolve_prefix get_prefix, void *parser,
-                             struct lyd_node *context_node, struct lyd_node **trees,
+                             ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,
+                             const void *context_node, struct lyd_node **trees,
                              const char **canonized, struct ly_err_item **err, void **priv)
 {
     LY_ERR ret = LY_EVALID;
     struct lysc_type_instanceid *type_inst = (struct lysc_type_instanceid *)type;
-    const char *id, *prefix, *val, *token;
+    const char *id, *prefix, *val, *token, *node_start;
     size_t id_len, prefix_len, val_len;
     char *errmsg = NULL;
     const struct lysc_node *node_s = NULL;
     const struct lyd_node *node_d = NULL;
     struct lyd_value_prefix *prefixes = NULL;
     unsigned int u;
+    struct lyd_value_path *target = NULL, *t;
+    struct lyd_value_path_predicate *pred;
+    struct ly_set predicates = {0};
+    uint64_t pos;
+    int i;
 
-    if (((struct lyd_node_term*)context_node)->value.prefixes) {
-        /* the second run, the first one ended with LY_EINCOMPLETE */
-        prefixes = ((struct lyd_node_term*)context_node)->value.prefixes;
-        get_prefix = NULL;
+    /* init */
+    *err = NULL;
+
+    if (!(options & LY_TYPE_OPTS_INCOMPLETE_DATA) && ((struct lyd_node_term*)context_node)->value.target) {
+        /* the second run, the first one ended with LY_EINCOMPLETE, but we have prepared the target structure */
+
+        if (!lyd_target(((struct lyd_node_term*)context_node)->value.target, trees)) {
+            /* TODO print instance-identifier */
+            asprintf(&errmsg, "Invalid instance-identifier \"%s\" value - required instance not found.",
+                     "TODO");
+            goto error;
+        }
+        *priv = ((struct lyd_node_term*)context_node)->value.target;
+        return LY_SUCCESS;
+    } else {
+        /* first run without prepared target, we will need all the prefixes used in the instance-identifier value */
+        prefixes = ly_type_get_prefixes(ctx, value, value_len, get_prefix, parser);
+    }
+
+    if (value[0] != '/') {
+        asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - instance-identifier must starts with '/'.",
+                 (int)value_len, value);
+        goto error;
     }
 
     /* parse the value and try to resolve it in:
@@ -1368,68 +1584,267 @@
     for(token = value; (size_t)(token - value) < value_len;) {
         if (token[0] == '/') {
             /* node identifier */
+            node_start = &token[1];
+
+            /* clean them to correctly check predicates presence restrictions */
+            pos = 0;
+            ly_set_clean(&predicates, NULL);
+
             token++;
-            if (ly_type_validate_instanceid_checknodeid(ctx, value, value_len, options, type_inst->require_instance,
-                                                        &token, &prefixes, &node_s, &node_d, get_prefix, parser, trees, &errmsg)) {
+            if (ly_type_validate_instanceid_checknodeid(value, value_len, options, type_inst->require_instance,
+                                                        &token, prefixes, format, &node_s, &node_d, trees, &errmsg)) {
                 goto error;
             }
 
+            if (node_d) {
+                node_s = node_d->schema;
+            }
+            if (token[0] == '[') {
+                /* predicate follows, this must be a list or leaf-list */
+                if (node_s->nodetype != LYS_LIST && node_s->nodetype != LYS_LEAFLIST) {
+                    asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - predicate \"%.*s\" for %s is not accepted.",
+                             (int)value_len, value, (int)(ly_type_validate_instanceid_predicate_end(token) - token) + 1, token,
+                             lys_nodetype2str(node_s->nodetype));
+                    goto error;
+                }
+            }
+
+            if (options & LY_TYPE_OPTS_STORE) {
+                /* prepare target path structure */
+                LY_ARRAY_NEW_GOTO(ctx, target, t, ret, error);
+                t->node = node_s;
+            }
         } else if (token[0] == '[') {
             /* predicate */
+            const char *pred_start = &token[0];
             const char *pred_errmsg = NULL;
             const struct lysc_node *key_s = node_s;
             const struct lyd_node *key_d = node_d;
 
-            if (ly_parse_instance_predicate(&token, value_len - (token - value), &prefix, &prefix_len, &id, &id_len, &val, &val_len, &pred_errmsg)) {
-                asprintf(&errmsg, "Invalid instance-identifier's predicate (%s).", pred_errmsg);
+            if (ly_parse_instance_predicate(&token, value_len - (token - value), format, &prefix, &prefix_len, &id, &id_len, &val, &val_len, &pred_errmsg)) {
+                asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value's predicate \"%.*s\" (%s).", (int)value_len, value,
+                         (int)(token - pred_start), pred_start, pred_errmsg);
                 goto error;
             }
-            if (prefix) {
+
+            if (options & LY_TYPE_OPTS_STORE) {
+                /* update target path structure by adding predicate info */
+                LY_ARRAY_NEW_GOTO(ctx, t->predicates, pred, ret, error);
+            }
+
+            if (prefix || (id && id[0] != '.')) {
                 /* key-predicate */
-                if (ly_type_validate_instanceid_checknodeid(ctx, value, value_len, options, type_inst->require_instance,
-                                                            &prefix, &prefixes, &key_s, &key_d, get_prefix, parser, trees, &errmsg)) {
+                const char *start, *t;
+                start = t = prefix ? prefix : id;
+
+                /* check that the parent is a list */
+                if (node_s->nodetype != LYS_LIST) {
+                    asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - key-predicate \"%.*s\" is accepted only for lists, not %s.",
+                             (int)value_len, value, (int)(token - pred_start), pred_start, lys_nodetype2str(node_s->nodetype));
+                    goto error;
+                }
+
+                /* resolve the key in predicate */
+                if (ly_type_validate_instanceid_checknodeid(value, value_len, options, type_inst->require_instance,
+                                                            &t, prefixes, format, &key_s, &key_d, trees, &errmsg)) {
                     goto error;
                 }
                 if (key_d) {
-                    /* TODO check value */
-                } else if (key_s) {
-                    /* TODO check type of the value with the type of the node */
-                } else {
-                    LOGINT(ctx);
+                    key_s = key_d->schema;
+                }
+
+                /* check the key in predicate is really a key */
+                if (!(key_s->flags & LYS_KEY)) {
+                    asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - node \"%s\" used in key-predicate \"%.*s\" must be a key.",
+                            (int)value_len, value, key_s->name, (int)(token - pred_start), pred_start);
                     goto error;
                 }
-            } else if (id) {
-                /* TODO leaf-list-predicate */
-            } else {
-                /* TODO pos predicate */
-            }
 
+                if (node_d) {
+                    while (node_d) {
+                        if (!lyd_value_compare((const struct lyd_node_term*)key_d, val, val_len, get_prefix, parser, format, trees)) {
+                            /* match */
+                            break;
+                        }
+                        /* go to another instance */
+                        t = start;
+                        node_d = lyd_search(node_d->next, node_s->module, node_s->name, strlen(node_s->name), LYS_LIST, NULL, 0);
+                        if (node_d) {
+                            key_d = node_d;
+                            if (ly_type_validate_instanceid_checknodeid(value, value_len, options, type_inst->require_instance,
+                                                                        &t, prefixes, format, &key_s, &key_d, trees, &errmsg)) {
+                                goto error;
+                            }
+                        }
+                    }
+                    if (!node_d) {
+                        asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - key-predicate \"%.*s\" does not match any \"%s\" instance.",
+                                 (int)value_len, value, (int)(token - pred_start), pred_start, node_s->name);
+                        goto error;
+                    }
+                } else {
+                    /* check value to the type */
+                    if (lys_value_validate(NULL, key_s, val, val_len, get_prefix, parser, format)) {
+                        asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - key-predicate \"%.*s\"'s key value is invalid.",
+                                 (int)value_len, value, (int)(token - pred_start), pred_start);
+                        goto error;
+                    }
+                }
+
+                if (options & LY_TYPE_OPTS_STORE) {
+                    /* update target path structure by adding predicate info */
+                    pred->type = 1;
+                    pred->key = key_s;
+                    LY_CHECK_GOTO(ly_type_validate_instanceid_parse_predicate_value(ctx, key_s, val, val_len, prefixes, format, pred, &errmsg), error);
+                }
+                i = predicates.count;
+                if (i != ly_set_add(&predicates, (void*)key_s, 0)) {
+                    /* the same key is used repeatedly */
+                    asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - key \"%s\" is referenced the second time in key-predicate \"%.*s\".",
+                             (int)value_len, value, key_s->name, (int)(token - pred_start), pred_start);
+                    goto error;
+                }
+                if (token[0] != '[') {
+                    /* now we should have all the keys */
+                    if (LY_ARRAY_SIZE(((struct lysc_node_list*)node_s)->keys) != predicates.count) {
+                        asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - missing %u key(s) for the list instance \"%.*s\".",
+                                 (int)value_len, value, LY_ARRAY_SIZE(((struct lysc_node_list*)node_s)->keys) - predicates.count,
+                                 (int)(token - node_start), node_start);
+                        goto error;
+                    }
+                }
+            } else if (id) {
+                /* leaf-list-predicate */
+                if (predicates.count) {
+                    asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - "
+                             "leaf-list-predicate (\"%.*s\") cannot be used repeatedly for a single node.",
+                             (int)value_len, value, (int)(token - pred_start), pred_start);
+                    goto error;
+                }
+
+                /* check that the parent is a leaf-list */
+                if (node_s->nodetype != LYS_LEAFLIST) {
+                    asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - leaf-list-predicate \"%.*s\" is accepted only for leaf-lists, not %s.",
+                             (int)value_len, value, (int)(token - pred_start), pred_start, lys_nodetype2str(node_s->nodetype));
+                    goto error;
+                }
+
+                if (key_d) {
+                    while (key_d) {
+                        if (!lyd_value_compare((const struct lyd_node_term*)key_d, val, val_len, get_prefix, parser, format, trees)) {
+                            /* match */
+                            break;
+                        }
+                        /* go to another instance */
+                        key_d = lyd_search(key_d->next, node_s->module, node_s->name, strlen(node_s->name), LYS_LEAFLIST, NULL, 0);
+                    }
+                    if (!key_d) {
+                        asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - leaf-list-predicate \"%.*s\" does not match any \"%s\" instance.",
+                                 (int)value_len, value, (int)(token - pred_start), pred_start, node_s->name);
+                        goto error;
+                    }
+                } else {
+                    /* check value to the type */
+                    if (lys_value_validate(NULL, key_s, val, val_len, get_prefix, parser, format)) {
+                        asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - leaf-list-predicate \"%.*s\"'s value is invalid.",
+                                 (int)value_len, value, (int)(token - pred_start), pred_start);
+                        goto error;
+                    }
+                }
+
+                if (options & LY_TYPE_OPTS_STORE) {
+                    /* update target path structure by adding predicate info */
+                    pred->type = 2;
+                    pred->key = node_s;
+                    LY_CHECK_GOTO(ly_type_validate_instanceid_parse_predicate_value(ctx, node_s, val, val_len, prefixes, format, pred, &errmsg), error);
+                }
+                ly_set_add(&predicates, (void*)node_s, 0);
+            } else {
+                /* pos predicate */
+                if (pos) {
+                    asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - "
+                             "position predicate (\"%.*s\") cannot be used repeatedly for a single node.",
+                             (int)value_len, value, (int)(token - pred_start), pred_start);
+                    goto error;
+                }
+
+                /* check that the parent is a list without keys
+                 * The check is actually commented, libyang does not want to be so strict here, since
+                 * we see usecases even for lists with keys and leaf-lists
+                if (node_s->nodetype != LYS_LIST || ((struct lysc_node_list*)node_s)->keys) {
+                    asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - position predicate \"%.*s\" is accepted only for lists without keys.",
+                             (int)value_len, value, (int)(token - pred_start), pred_start, lys_nodetype2str(node_s->nodetype));
+                    goto error;
+                }
+                */
+
+                if (ly_type_parse_uint("instance-identifier", 10, UINT64_MAX, val, val_len, &pos, err)) {
+                    goto error;
+                }
+                if (node_d) {
+                    /* get the correct instance */
+                    for (uint64_t u = pos; u > 1; u--) {
+                        node_d = lyd_search(node_d->next, node_s->module, node_s->name, strlen(node_s->name), node_s->nodetype, NULL, 0);
+                        if (!node_d) {
+                            asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - "
+                                     "position-predicate %"PRIu64" is bigger than number of instances in the data tree (%"PRIu64").",
+                                     (int)value_len, value, pos, pos - u + 1);
+                            goto error;
+                        }
+                    }
+                } else {
+                    /* check that there is not a limit below the position number */
+                    if (node_s->nodetype == LYS_LIST) {
+                        if (((struct lysc_node_list*)node_s)->max < pos) {
+                            asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - position-predicate %"PRIu64" is bigger than allowed max-elements (%u).",
+                                     (int)value_len, value, pos, ((struct lysc_node_list*)node_s)->max);
+                            goto error;
+                        }
+                    } else {
+                        if (((struct lysc_node_leaflist*)node_s)->max < pos) {
+                            asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - position-predicate %"PRIu64" is bigger than allowed max-elements (%u).",
+                                     (int)value_len, value, pos, ((struct lysc_node_leaflist*)node_s)->max);
+                            goto error;
+                        }
+                    }
+                }
+
+                if (options & LY_TYPE_OPTS_STORE) {
+                    /* update target path structure by adding predicate info */
+                    pred->type = 0;
+                    pred->position = pos;
+                }
+            }
         } else {
-            asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - unexpected character %lu.",
-                     (int)value_len, value, token - value + 1);
+            asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value at character %lu (%.*s).",
+                     (int)value_len, value, token - value + 1, (int)(value_len - (token - value)), token);
             goto error;
         }
     }
 
-    if ((options & LY_TYPE_OPTS_CANONIZE) && *canonized != value) {
-        if (options & LY_TYPE_OPTS_DYNAMIC) {
-            *canonized = lydict_insert_zc(ctx, (char*)value);
-            value = NULL;
-        } else {
-            *canonized = lydict_insert(ctx, value, value_len);
-        }
+    if ((options & LY_TYPE_OPTS_CANONIZE) && !(*canonized)) {
+        /* instance-identifier does not have a canonical form (lexical representation in in XML and JSON are
+         * even different) - to make it clear, the canonized form is represented as NULL to make the caller
+         * print it always via callback printer */
+        *canonized = NULL;
     }
-#if 0
+
     if (options & LY_TYPE_OPTS_STORE) {
-        *priv = ident;
+        *priv = target;
+    } else {
+        lyd_value_free_path(ctx, target);
     }
-#endif
+
+    LY_ARRAY_FOR(prefixes, u) {
+        lydict_remove(ctx, prefixes[u].prefix);
+    }
+    LY_ARRAY_FREE(prefixes);
+    ly_set_erase(&predicates, NULL);
+
     if (options & LY_TYPE_OPTS_DYNAMIC) {
         free((char*)value);
     }
 
-    *priv = prefixes;
-
     if ((options & LY_TYPE_OPTS_INCOMPLETE_DATA) && type_inst->require_instance) {
         return LY_EINCOMPLETE;
     } else {
@@ -1437,13 +1852,17 @@
     }
 
 error:
-    if (!((struct lyd_node_term*)context_node)->value.prefixes) {
-        LY_ARRAY_FOR(prefixes, u) {
-            lydict_remove(ctx, prefixes[u].prefix);
-        }
-        LY_ARRAY_FREE(prefixes);
+    LY_ARRAY_FOR(prefixes, u) {
+        lydict_remove(ctx, prefixes[u].prefix);
     }
-    *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_RESTRICTION, errmsg, NULL, NULL);
+    LY_ARRAY_FREE(prefixes);
+    ly_set_erase(&predicates, NULL);
+
+    lyd_value_free_path(ctx, target);
+
+    if (!*err) {
+        *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_RESTRICTION, errmsg, NULL, NULL);
+    }
     return ret;
 }
 
@@ -1459,7 +1878,7 @@
 {
     if (options & LY_TYPE_OPTS_VALIDATE) {
         /* the value was prepared by ly_type_validate_enum() */
-        value->prefixes = *priv;
+        value->target = *priv;
     } else {
         /* TODO if there is usecase for store without validate */
         LOGINT(NULL);
@@ -1470,6 +1889,56 @@
 }
 
 /**
+ * @brief Comparison callback checking the instance-identifier value.
+ *
+ * Implementation of the ly_type_compare_clb.
+ */
+static LY_ERR
+ly_type_compare_instanceid(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+    unsigned int u, v;
+
+    if (val1 == val2) {
+        return LY_SUCCESS;
+    } else if (!val1->target || !val2->target || LY_ARRAY_SIZE(val1->target) != LY_ARRAY_SIZE(val2->target)) {
+        return LY_EVALID;
+    }
+
+    LY_ARRAY_FOR(val1->target, u) {
+        struct lyd_value_path *s1 = &val1->target[u];
+        struct lyd_value_path *s2 = &val2->target[u];
+
+        if (s1->node != s2->node || (s1->predicates && !s2->predicates) || (!s1->predicates && s2->predicates) ||
+                (s1->predicates && LY_ARRAY_SIZE(s1->predicates) != LY_ARRAY_SIZE(s2->predicates))) {
+            return LY_EVALID;
+        }
+        if (s1->predicates) {
+            LY_ARRAY_FOR(s1->predicates, v) {
+                struct lyd_value_path_predicate *pred1 = &s1->predicates[v];
+                struct lyd_value_path_predicate *pred2 = &s2->predicates[v];
+
+                if (pred1->type != pred2->type) {
+                    return LY_EVALID;
+                }
+                if (pred1->type == 0) {
+                    /* position predicate */
+                    if (pred1->position != pred2->position) {
+                        return LY_EVALID;
+                    }
+                } else {
+                    /* key-predicate or leaf-list-predicate */
+                    if (pred1->key != pred2->key || ((struct lysc_node_leaf*)pred1->key)->type->plugin->compare(pred1->value, pred2->value)) {
+                        return LY_EVALID;
+                    }
+                }
+            }
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
  * @brief Free value of the YANG built-in instance-identifier type.
  *
  * Implementation of the ly_type_free_clb.
@@ -1477,35 +1946,45 @@
 static void
 ly_type_free_instanceid(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), struct lyd_value *value)
 {
-    unsigned int u;
+    lyd_value_free_path(ctx, value->target);
+    value->target = NULL;
+}
 
-    LY_ARRAY_FOR(value->prefixes, u) {
-        lydict_remove(ctx, value->prefixes[u].prefix);
+/**
+ * @brief Generic comparison callback checking the canonical value.
+ *
+ * Implementation of the ly_type_compare_clb.
+ */
+static LY_ERR
+ly_type_compare_canonical(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+    if (val1 == val2 || !strcmp(val1->canonized, val2->canonized)) {
+        return LY_SUCCESS;
     }
-    LY_ARRAY_FREE(value->prefixes);
-    value->prefixes = NULL;
+
+    return LY_EVALID;
 }
 
 struct lysc_type_plugin ly_builtin_type_plugins[LY_DATA_TYPE_COUNT] = {
     {0}, /* LY_TYPE_UNKNOWN */
-    {.type = LY_TYPE_BINARY, .validate = ly_type_validate_binary, .store = NULL, .free = NULL},
-    {.type = LY_TYPE_UINT8, .validate = ly_type_validate_uint, .store = ly_type_store_uint, .free = NULL},
-    {.type = LY_TYPE_UINT16, .validate = ly_type_validate_uint, .store = ly_type_store_uint, .free = NULL},
-    {.type = LY_TYPE_UINT32, .validate = ly_type_validate_uint, .store = ly_type_store_uint, .free = NULL},
-    {.type = LY_TYPE_UINT64, .validate = ly_type_validate_uint, .store = ly_type_store_uint, .free = NULL},
-    {.type = LY_TYPE_STRING, .validate = ly_type_validate_string, .store = NULL, .free = NULL},
-    {.type = LY_TYPE_BITS, .validate = ly_type_validate_bits, .store = ly_type_store_bits, .free = ly_type_free_bits},
-    {.type = LY_TYPE_BOOL, .validate = ly_type_validate_boolean, .store = ly_type_store_boolean, .free = NULL},
-    {.type = LY_TYPE_DEC64, .validate = ly_type_validate_decimal64, .store = ly_type_store_decimal64, .free = NULL},
-    {.type = LY_TYPE_EMPTY, .validate = ly_type_validate_empty, .store = NULL, .free = NULL},
-    {.type = LY_TYPE_ENUM, .validate = ly_type_validate_enum, .store = ly_type_store_enum, .free = NULL},
-    {.type = LY_TYPE_IDENT, .validate = ly_type_validate_identityref, .store = ly_type_store_identityref, .free = NULL},
-    {.type = LY_TYPE_INST, .validate = ly_type_validate_instanceid, .store = ly_type_store_instanceid, .free = ly_type_free_instanceid},
+    {.type = LY_TYPE_BINARY, .validate = ly_type_validate_binary, .store = NULL, .compare = ly_type_compare_canonical, .free = NULL},
+    {.type = LY_TYPE_UINT8, .validate = ly_type_validate_uint, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .free = NULL},
+    {.type = LY_TYPE_UINT16, .validate = ly_type_validate_uint, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .free = NULL},
+    {.type = LY_TYPE_UINT32, .validate = ly_type_validate_uint, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .free = NULL},
+    {.type = LY_TYPE_UINT64, .validate = ly_type_validate_uint, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .free = NULL},
+    {.type = LY_TYPE_STRING, .validate = ly_type_validate_string, .store = NULL, .compare = ly_type_compare_canonical, .free = NULL},
+    {.type = LY_TYPE_BITS, .validate = ly_type_validate_bits, .store = ly_type_store_bits, .compare = ly_type_compare_canonical, .free = ly_type_free_bits},
+    {.type = LY_TYPE_BOOL, .validate = ly_type_validate_boolean, .store = ly_type_store_boolean, .compare = ly_type_compare_canonical, .free = NULL},
+    {.type = LY_TYPE_DEC64, .validate = ly_type_validate_decimal64, .store = ly_type_store_decimal64, .compare = ly_type_compare_canonical, .free = NULL},
+    {.type = LY_TYPE_EMPTY, .validate = ly_type_validate_empty, .store = NULL, .compare = ly_type_compare_empty, .free = NULL},
+    {.type = LY_TYPE_ENUM, .validate = ly_type_validate_enum, .store = ly_type_store_enum, .compare = ly_type_compare_canonical, .free = NULL},
+    {.type = LY_TYPE_IDENT, .validate = ly_type_validate_identityref, .store = ly_type_store_identityref, .compare = ly_type_compare_identityref, .free = NULL},
+    {.type = LY_TYPE_INST, .validate = ly_type_validate_instanceid, .store = ly_type_store_instanceid, .compare = ly_type_compare_instanceid, .free = ly_type_free_instanceid},
     {0}, /* TODO LY_TYPE_LEAFREF */
     {0}, /* TODO LY_TYPE_UNION */
-    {.type = LY_TYPE_INT8, .validate = ly_type_validate_int, .store = ly_type_store_int, .free = NULL},
-    {.type = LY_TYPE_INT16, .validate = ly_type_validate_int, .store = ly_type_store_int, .free = NULL},
-    {.type = LY_TYPE_INT32, .validate = ly_type_validate_int, .store = ly_type_store_int, .free = NULL},
-    {.type = LY_TYPE_INT64, .validate = ly_type_validate_int, .store = ly_type_store_int, .free = NULL},
+    {.type = LY_TYPE_INT8, .validate = ly_type_validate_int, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = NULL},
+    {.type = LY_TYPE_INT16, .validate = ly_type_validate_int, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = NULL},
+    {.type = LY_TYPE_INT32, .validate = ly_type_validate_int, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = NULL},
+    {.type = LY_TYPE_INT64, .validate = ly_type_validate_int, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = NULL},
 };
 
diff --git a/src/plugins_types.h b/src/plugins_types.h
index f0df0fb..5556817 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -82,20 +82,6 @@
  */
 
 /**
- * @brief Callback provided by the data parsers to type plugins to resolve (format-specific) mapping between prefixes used in the value strings
- * to the YANG schemas.
- *
- * XML uses XML namespaces, JSON uses schema names as prefixes.
- *
- * @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] parser Internal parser's data.
- * @return Pointer to the YANG schema identified by the provided prefix or NULL if no mapping found.
- */
-typedef const struct lys_module *(*ly_type_resolve_prefix)(struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *parser);
-
-/**
  * @brief Callback to validate the given @p value according to the given @p type. Optionaly, it can be requested to canonize the value.
  *
  * Note that the \p value string is not necessarily zero-terminated. The provided \p value_len is always correct.
@@ -103,12 +89,15 @@
  * @param[in] ctx libyang Context
  * @param[in] type Type of the value being canonized.
  * @param[in] value Lexical representation of the value to be validated (and canonized).
+ *            It is never NULL, empty string is represented as "" with zero @p value_len.
  * @param[in] value_len Length of the given \p value.
  * @param[in] options [Type plugin options ](@ref plugintypeopts).
  *
  * @param[in] get_prefix Parser-specific getter to resolve prefixes used in the value strings.
  * @param[in] parser Parser's data for @p get_prefix
- * @param[in] context_node The @p value's node for the case that the require-instance restriction is supposed to be resolved.
+ * @param[in] format Input format of the data.
+ * @param[in] context_node The @p value's node for the case that the require-instance restriction is supposed to be resolved. This argument is of
+ *            lys_node (in case LY_TYPE_OPTS_INCOMPLETE_DATA set in @p options) or lyd_node structure.
  * @param[in] trees ([Sized array](@ref sizedarrays)) of external data trees (e.g. when validating RPC/Notification) where the required data
  *            instance can be placed.
  *
@@ -121,8 +110,8 @@
  * @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_validate_clb)(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                                       ly_type_resolve_prefix get_prefix, void *parser,
-                                       struct lyd_node *context_node, struct lyd_node **trees,
+                                       ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,
+                                       const void *context_node, struct lyd_node **trees,
                                        const char **canonized, struct ly_err_item **err, void **priv);
 
 /**
@@ -142,6 +131,18 @@
                                     struct lyd_value *value, struct ly_err_item **err, void **priv);
 
 /**
+ * @brief Callback for comparing 2 values of the same type.
+ *
+ * Caller is responsible to provide values of the SAME type.
+ *
+ * @param[in] val1 First value to compare.
+ * @param[in] val2 Second value to compare.
+ * @return LY_SUCCESS if values are same (according to the type's definition of being same).
+ * @return LY_EVALID if values differ.
+ */
+typedef LY_ERR (*ly_type_compare_clb)(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
  * @brief Callback for freeing the user type values stored by ly_type_store_clb().
  *
  *
@@ -162,7 +163,8 @@
     LY_DATA_TYPE type;               /**< implemented type, use LY_TYPE_UNKNOWN for derived data types */
     ly_type_validate_clb validate;   /**< function to validate and canonize given value */
     ly_type_store_clb store;         /**< function to store the value in the type-specific way */
-    ly_type_free_clb free;           /**< function to free the type-spceific way stored value */
+    ly_type_compare_clb compare;     /**< comparison callback to compare 2 values of the same type */
+    ly_type_free_clb free;           /**< optional function to free the type-spceific way stored value */
     uint32_t flags;                  /**< [type flags ](@ref plugintypeflags). */
 };
 
@@ -253,4 +255,17 @@
  */
 LY_ERR ly_type_validate_patterns(struct lysc_pattern **patterns, const char *str, size_t str_len, struct ly_err_item **err);
 
+/**
+ * @brief Helper function for type validation callbacks to prepare list of all possible prefixes used in the value string.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] value Value string to be parsed.
+ * @param[in] value_len Length of the @p value string.
+ * @param[in] get_prefix Parser-specific getter to resolve prefixes used in the value strings.
+ * @param[in] parser Parser's data for @p get_prefix.
+ * @return Created [sized array](@ref sizedarrays) of prefix mappings, NULL in case of error.
+ */
+struct lyd_value_prefix *ly_type_get_prefixes(struct ly_ctx *ctx, const char *value, size_t value_len,
+                                              ly_clb_resolve_prefix get_prefix, void *parser);
+
 #endif /* LY_PLUGINS_TYPES_H_ */
diff --git a/src/tree.h b/src/tree.h
index 65c2405..5210ce2 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -156,6 +156,20 @@
  */
 extern const char* ly_data_type2str[LY_DATA_TYPE_COUNT];
 
+/**
+ * @brief Callback provided by the data parsers to type plugins to resolve (format-specific) mapping between prefixes used in the value strings
+ * to the YANG schemas.
+ *
+ * XML uses XML namespaces, JSON uses schema names as prefixes.
+ *
+ * @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_clb_resolve_prefix)(struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *private);
+
 /** @} */
 
 #ifdef __cplusplus
diff --git a/src/tree_data.c b/src/tree_data.c
index eb551e2..02059b9 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -14,6 +14,7 @@
 
 #include "common.h"
 
+#include <assert.h>
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -87,6 +88,197 @@
     return NULL;
 }
 
+LY_ERR
+lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic,
+                ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, struct lyd_node **trees)
+{
+    LY_ERR ret = LY_SUCCESS, rc;
+    struct ly_err_item *err = NULL;
+    struct ly_ctx *ctx;
+    struct lysc_type *type;
+    void *priv = NULL;
+    int options = LY_TYPE_OPTS_VALIDATE | LY_TYPE_OPTS_CANONIZE | LY_TYPE_OPTS_STORE |
+            (dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+    assert(node);
+
+    ctx = node->schema->module->ctx;
+    type = ((struct lysc_node_leaf*)node->schema)->type;
+    if (type->plugin->validate) {
+        rc = type->plugin->validate(ctx, type, value, value_len, options, get_prefix, parser, format,
+                                    trees ? (void*)node : (void*)node->schema, trees,
+                                    &node->value.canonized, &err, &priv);
+        if (rc == LY_EINCOMPLETE) {
+            ret = rc;
+            /* continue with storing, just remember what to return if storing is ok */
+        } else if (rc) {
+            ret = rc;
+            if (err) {
+                ly_err_print(err);
+                LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
+                ly_err_free(err);
+            }
+            goto error;
+        }
+    } else if (dynamic) {
+        node->value.canonized = lydict_insert_zc(ctx, (char*)value);
+    } else {
+        node->value.canonized = lydict_insert(ctx, value, value_len);
+    }
+    node->value.plugin = type->plugin;
+
+    if (type->plugin->store) {
+        rc = type->plugin->store(ctx, type, options, &node->value, &err, &priv);
+        if (rc) {
+            ret = rc;
+            if (err) {
+                ly_err_print(err);
+                LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
+                ly_err_free(err);
+            }
+            goto error;
+        }
+    }
+
+error:
+    return ret;
+}
+
+API LY_ERR
+lys_value_validate(struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
+                   ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format)
+{
+    LY_ERR rc = LY_SUCCESS;
+    struct ly_err_item *err = NULL;
+    struct lysc_type *type;
+    void *priv = NULL;
+    int options = LY_TYPE_OPTS_VALIDATE | LY_TYPE_OPTS_INCOMPLETE_DATA;
+
+    LY_CHECK_ARG_RET(ctx, node, value, LY_EINVAL);
+
+    if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+        LOGARG(ctx, node);
+        return LY_EINVAL;
+    }
+
+    type = ((struct lysc_node_leaf*)node)->type;
+
+    if (type->plugin->validate) {
+        rc = type->plugin->validate(ctx ? ctx : node->module->ctx, type, value, value_len, options,
+                                     get_prefix, get_prefix_data, format, node, NULL, NULL, &err, &priv);
+        if (rc == LY_EINCOMPLETE) {
+            /* actually success since we do not provide the context tree and call validation with
+             * LY_TYPE_OPTS_INCOMPLETE_DATA */
+            rc = LY_SUCCESS;
+        } else if (rc && err) {
+            if (ctx) {
+                /* log only in case the ctx was provided as input parameter */
+                ly_err_print(err);
+                LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
+            }
+            ly_err_free(err);
+        }
+    }
+
+    return rc;
+}
+
+API LY_ERR
+lyd_value_validate(struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
+                   ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, struct lyd_node **trees)
+{
+    LY_ERR rc;
+    struct ly_err_item *err = NULL;
+    struct lysc_type *type;
+    void *priv = NULL;
+    int options = LY_TYPE_OPTS_VALIDATE | LY_TYPE_OPTS_STORE | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+
+    LY_CHECK_ARG_RET(ctx, node, value, LY_EINVAL);
+
+    type = ((struct lysc_node_leaf*)node->schema)->type;
+
+    if (type->plugin->validate) {
+        rc = type->plugin->validate(ctx ? ctx : node->schema->module->ctx, type, value, value_len, options,
+                                     get_prefix, get_prefix_data, format, trees ? (void*)node : (void*)node->schema, trees,
+                                     NULL, &err, &priv);
+        if (rc == LY_EINCOMPLETE) {
+            return rc;
+        } else if (rc) {
+            if (err) {
+                if (ctx) {
+                    ly_err_print(err);
+                    LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
+                }
+                ly_err_free(err);
+            }
+            return rc;
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+API LY_ERR
+lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t value_len,
+                  ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, struct lyd_node **trees)
+{
+    LY_ERR ret = LY_SUCCESS, rc;
+    struct ly_err_item *err = NULL;
+    struct ly_ctx *ctx;
+    struct lysc_type *type;
+    void *priv = NULL;
+    struct lyd_value data = {0};
+    int options = LY_TYPE_OPTS_VALIDATE | LY_TYPE_OPTS_CANONIZE | LY_TYPE_OPTS_STORE | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+
+    LY_CHECK_ARG_RET(node ? node->schema->module->ctx : NULL, node, value, LY_EINVAL);
+
+    ctx = node->schema->module->ctx;
+    type = ((struct lysc_node_leaf*)node->schema)->type;
+    if (type->plugin->validate) {
+        rc = type->plugin->validate(ctx, type, value, value_len, options,
+                                     get_prefix, get_prefix_data, format, (struct lyd_node*)node, trees,
+                                     &data.canonized, &err, &priv);
+        if (rc == LY_EINCOMPLETE) {
+            ret = rc;
+            /* continue with comparing, just remember what to return if storing is ok */
+        } else if (rc) {
+            /* value to compare is invalid */
+            ret = LY_EINVAL;
+            if (err) {
+                ly_err_free(err);
+            }
+            goto cleanup;
+        }
+    } else {
+        data.canonized = lydict_insert(ctx, value, value_len);
+    }
+
+    /* store the value to compare into a dummy storage to do a comparison */
+    if (type->plugin->store) {
+        if (type->plugin->store(ctx, type, options, &data, &err, &priv)) {
+            ret = LY_EINVAL;
+            if (err) {
+                ly_err_free(err);
+            }
+            goto cleanup;
+        }
+    }
+
+    /* compare data */
+    if (type->plugin->compare) {
+        ret = type->plugin->compare(&node->value, &data);
+    } else if (data.canonized != node->value.canonized) {
+        ret = LY_EVALID;
+    }
+
+cleanup:
+    if (type->plugin->free) {
+        type->plugin->free(ctx, type, &data);
+    }
+    lydict_remove(ctx, data.canonized);
+
+    return ret;
+}
+
 static struct lyd_node *
 lyd_parse_mem_(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, va_list ap)
 {
@@ -251,3 +443,82 @@
 
     return result;
 }
+
+API const struct lyd_node_term *
+lyd_target(struct lyd_value_path *path, struct lyd_node **trees)
+{
+    unsigned int u, v, x;
+    const struct lyd_node *node = NULL, *parent = NULL, *start_search;
+    uint64_t pos = 1;
+
+    LY_CHECK_ARG_RET(NULL, path, trees, NULL);
+
+    LY_ARRAY_FOR(path, u) {
+        if (parent) {
+            start_search = lyd_node_children(parent);
+search_inner:
+            node = lyd_search(start_search, path[u].node->module, path[u].node->name, strlen(path[u].node->name), path[u].node->nodetype, NULL, 0);
+        } else {
+            LY_ARRAY_FOR(trees, v) {
+                start_search = trees[v];
+search_toplevel:
+                /* WARNING! to use search_toplevel label correctly, variable v must be preserved and not changed! */
+                node = lyd_search(start_search, path[u].node->module, path[u].node->name, strlen(path[u].node->name), path[u].node->nodetype, NULL, 0);
+                if (node) {
+                    break;
+                }
+            }
+        }
+        if (!node) {
+            return NULL;
+        }
+
+        /* check predicate if any */
+        LY_ARRAY_FOR(path[u].predicates, x) {
+            if (path[u].predicates[x].type == 0) {
+                /* position predicate */
+                if (pos != path[u].predicates[x].position) {
+                    pos++;
+                    goto search_repeat;
+                }
+                /* done, no more predicates are allowed here */
+                break;
+            } else if (path[u].predicates[x].type == 1) {
+                /* key-predicate */
+                struct lysc_type *type = ((struct lysc_node_leaf*)path[u].predicates[x].key)->type;
+                const struct lyd_node *key = lyd_search(lyd_node_children(node), path[u].predicates[x].key->module,
+                                                        path[u].predicates[x].key->name, strlen(path[u].predicates[x].key->name),
+                                                        LYS_LEAF, NULL, 0);
+                if (!key) {
+                    /* probably error and we shouldn't be here due to previous checks when creating path */
+                    goto search_repeat;
+                }
+                if (type->plugin->compare(&((struct lyd_node_term*)key)->value, path[u].predicates[x].value)) {
+                    goto search_repeat;
+                }
+            } else if (path[u].predicates[x].type == 2) {
+                /* leaf-list-predicate */
+                struct lysc_type *type = ((struct lysc_node_leaf*)path[u].node)->type;
+                if (type->plugin->compare(&((struct lyd_node_term*)node)->value, path[u].predicates[x].value)) {
+                    goto search_repeat;
+                }
+            } else {
+                LOGINT(NULL);
+            }
+        }
+
+        parent = node;
+    }
+
+    return (const struct lyd_node_term*)node;
+
+search_repeat:
+    start_search = node->next;
+    if (parent) {
+        goto search_inner;
+    } else {
+        goto search_toplevel;
+    }
+}
+
+
diff --git a/src/tree_data.h b/src/tree_data.h
index ec23aac..84021b5 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -181,6 +181,21 @@
             const char *prefix;           /**< prefix string used in the canonized string to identify the mod of the YANG schema */
             const struct lys_module *mod; /**< YANG schema module identified by the prefix string */
         } *prefixes;                 /**< list of mappings between prefix in canonized value to a YANG schema ([sized array](@ref sizedarrays)) */
+
+        struct lyd_value_path {
+            const struct lysc_node *node;
+            struct lyd_value_path_predicate {
+                union {
+                    struct {
+                        const struct lysc_node *key;
+                        struct lyd_value *value;
+                    };
+                    uint64_t position;
+                };
+                uint8_t type; /**< 0 - position, 1 - key-predicate, 2 - leaf-list-predicate */
+            } *predicates;
+        } *target;
+
         int8_t int8;                 /**< 8-bit signed integer */
         int16_t int16;               /**< 16-bit signed integer */
         int32_t int32;               /**< 32-bit signed integer */
@@ -192,6 +207,11 @@
         void *ptr;                   /**< generic data type structure used to store the data */
     };  /**< The union is just a list of shorthands to possible values stored by a type's plugin. libyang works only with the canonized string,
              this specific data type storage is just to simplify use of the values by the libyang users. */
+
+    struct lysc_type_plugin *plugin; /**< pointer to the type plugin which stored the data. This pointer can differ from the pointer
+                                          in the lysc_type structure since the plugin itself can use other plugins for storing data.
+                                          Speaking about built-in types, this is the case of leafref which stores data as its target type.
+                                          Similarly union types stores the currently used type plugin in its internal lyd_value structure. */
 };
 
 /**
@@ -409,7 +429,10 @@
  * @param[in] name Name of the node to find (mandatory argument).
  * @param[in] name_len Optional length of the @p name argument in case it is not NULL-terminated string.
  * @param[in] nodetype Optional mask for the nodetype of the node to find, 0 is understood as all nodetypes.
- * @param[in] value Optional restriction for lyd_node_term nodes to select node with the specific value.
+ * @param[in] value Optional restriction for lyd_node_term nodes to select node with the specific value. Note that this
+ * search restriction is limited to compare canonical representation of the type. Some of the types have no canonical
+ * representation and 2 different strings can represent the same value (e.g. different prefixes of the same namespace in instance-identifiers).
+ * In this case there is more advanced lyd_value_compare() to check if the values matches.
  * @param[in] value_len Optional length of the @p value argument in case it is not NULL-terminated string.
  * @return The sibling node of the @p first (or itself), satisfying the given restrictions.
  * @return NULL in case there is no node satisfying the restrictions.
@@ -568,6 +591,55 @@
  */
 void lyd_free_attr(struct ly_ctx *ctx, struct lyd_attr *attr, int recursive);
 
+/**
+ * @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value.
+ *
+ * The given node is not modified in any way - it is just checked if the @p value can be set to the node.
+ *
+ * If there is no data node instance and you are fine with checking just the type's restrictions without the
+ * data tree context (e.g. for the case of require-instance restriction), use lys_value_validate().
+ *
+ * @param[in] ctx libyang context for logging (function does not log errors when @p ctx is NULL)
+ * @param[in] node Data node for the @p value.
+ * @param[in] value String value to be checked.
+ * @param[in] value_len Length of the given @p value (mandatory).
+ * @param[in] get_prefix Callback function to resolve prefixes used in the @p value string.
+ * @param[in] get_prefix_data Private data for the @p get_prefix callback.
+ * @param[in] format Input format of the data.
+ * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
+ *            data instance (leafref target, instance-identifier) can be placed. NULL in case the data tree are not yet complete,
+ *            then LY_EINCOMPLETE can be returned.
+ * @return LY_SUCCESS on success
+ * @return LY_EINCOMPLETE in case the @p trees is not provided and it was needed to finish the validation (e.g. due to require-instance).
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lyd_value_validate(struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
+                          ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, struct lyd_node **trees);
+
+/**
+ * @brief Compare the node's value with the given string value. The string value is first validated according to the node's type.
+ *
+ * @param[in] node Data node to compare.
+ * @param[in] value String value to be compared. It does not need to be in a canonical form - as part of the process,
+ * it is validated and canonized if possible.
+ * @param[in] value_len Length of the given @p value (mandatory).
+ * @param[in] get_prefix Callback function to resolve prefixes used in the @p value string.
+ * @param[in] get_prefix_data Private data for the @p get_prefix callback.
+ * @param[in] format Input format of the data.
+ * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
+ *            data instance (leafref target, instance-identifier) can be placed. NULL in case the data tree are not yet complete,
+ *            then LY_EINCOMPLETE can be returned in case the validation was not completed, but values matches.
+ * @return LY_SUCCESS on success
+ * @return LY_EINCOMPLETE in case of success when the @p trees is not provided and it was needed to finish the validation of
+ * the given string @p value (e.g. due to require-instance).
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t value_len,
+                         ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, struct lyd_node **trees);
+
+
+const struct lyd_node_term *lyd_target(struct lyd_value_path *path, struct lyd_node **trees);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
index f594872..6e28fdb 100644
--- a/src/tree_data_free.c
+++ b/src/tree_data_free.c
@@ -25,6 +25,27 @@
 #include "tree_data_internal.h"
 #include "plugins_types.h"
 
+void
+lyd_value_free_path(struct ly_ctx *ctx, struct lyd_value_path *path)
+{
+    unsigned int u, v;
+
+    LY_ARRAY_FOR(path, u) {
+        LY_ARRAY_FOR(path[u].predicates, v) {
+            if (path[u].predicates[v].type > 0) {
+                struct lysc_type *t = ((struct lysc_node_leaf*)path[u].predicates[v].key)->type;
+                if (t->plugin->free) {
+                    t->plugin->free(ctx, t, path[u].predicates[v].value);
+                }
+                lydict_remove(ctx, path[u].predicates[v].value->canonized);
+                free(path[u].predicates[v].value);
+            }
+        }
+        LY_ARRAY_FREE(path[u].predicates);
+    }
+    LY_ARRAY_FREE(path);
+}
+
 API LY_ERR
 lyd_unlink_tree(struct lyd_node *node)
 {
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index b30dd98..256c442 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -82,58 +82,3 @@
 
     return LY_SUCCESS;
 }
-
-LY_ERR
-lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic,
-                ly_type_resolve_prefix get_prefix, void *parser, struct lyd_node **trees)
-{
-    LY_ERR ret = LY_SUCCESS, rc;
-    struct ly_err_item *err = NULL;
-    struct ly_ctx *ctx;
-    struct lysc_type *type;
-    void *priv = NULL;
-    int options = LY_TYPE_OPTS_VALIDATE | LY_TYPE_OPTS_CANONIZE | LY_TYPE_OPTS_STORE |
-            (dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
-    assert(node);
-
-    ctx = node->schema->module->ctx;
-    type = ((struct lysc_node_leaf*)node->schema)->type;
-    if (type->plugin->validate) {
-        rc = type->plugin->validate(ctx, type, value, value_len, options,
-                                     get_prefix, parser, (struct lyd_node*)node, trees,
-                                     &node->value.canonized, &err, &priv);
-        if (rc == LY_EINCOMPLETE) {
-            ret = rc;
-            /* continue with storing, just remember what to return if storing is ok */
-        } else if (rc) {
-            ret = rc;
-            if (err) {
-                ly_err_print(err);
-                LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
-                ly_err_free(err);
-            }
-            goto error;
-        }
-    } else if (dynamic) {
-        node->value.canonized = lydict_insert_zc(ctx, (char*)value);
-    } else {
-        node->value.canonized = lydict_insert(ctx, value, value_len);
-    }
-
-    if (type->plugin->store) {
-        rc = type->plugin->store(ctx, type, options, &node->value, &err, &priv);
-        if (rc) {
-            ret = rc;
-            if (err) {
-                ly_err_print(err);
-                LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
-                ly_err_free(err);
-            }
-            goto error;
-        }
-    }
-
-error:
-    return ret;
-}
-
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index 7abb4b5..668f9a0 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -42,11 +42,12 @@
  * @brief Validate, canonize and store the given @p value into the node according to the node's type's rules.
  *
  * @param[in] node Data node for with the @p value.
- * @param[in] value String value to be parsed.
+ * @param[in] value String value to be parsed, must not be NULL.
  * @param[in] value_len Length of the give @p value (mandatory).
  * @param[in] dynamic Flag if @p value is a dynamically allocated memory and should be directly consumed/freed inside the function.
- * @param[in] get_prefix Parser-specific getter to resolve prefixes used in the value strings.
+ * @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.
  * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
  *            data instance (leafref target, instance-identifier) can be placed. NULL in case the data tree are not yet complete,
  *            then LY_EINCOMPLETE can be returned.
@@ -55,7 +56,7 @@
  * @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,
-                       ly_type_resolve_prefix get_prefix, void *parser, struct lyd_node **trees);
+                       ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,struct lyd_node **trees);
 
 /**
  * @brief Parse XML string as YANG data tree.
@@ -84,4 +85,12 @@
 
 /** @} datahash */
 
+/**
+ * @brief Free path (target) structure of the lyd_value.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] path The structure ([sized array](@ref sizedarrays)) to free.
+ */
+void lyd_value_free_path(struct ly_ctx *ctx, struct lyd_value_path *path);
+
 #endif /* LY_TREE_DATA_INTERNAL_H_ */
diff --git a/src/tree_schema.h b/src/tree_schema.h
index f5ddc35..8ed59a4 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -24,6 +24,7 @@
 #include "log.h"
 #include "tree.h"
 #include "extensions.h"
+#include "tree_data.h"
 
 struct ly_ctx;
 
@@ -1640,6 +1641,25 @@
  */
 const struct lysc_iffeature *lys_is_disabled(const struct lysc_node *node, int recursive);
 
+/**
+ * @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value.
+ *
+ * This function check just the type's restriction, if you want to check also the data tree context (e.g. in case of
+ * require-instance restriction), use lyd_value_validate().
+ *
+ * @param[in] ctx libyang context for logging (function does not log errors when @p ctx is NULL)
+ * @param[in] node Schema node for the @p value.
+ * @param[in] value String value to be checked.
+ * @param[in] value_len Length of the given @p value (mandatory).
+ * @param[in] get_prefix Callback function to resolve prefixes used in the @p value string.
+ * @param[in] get_prefix_data Private data for the @p get_prefix callback.
+ * @param[in] format Input format of the @p value.
+ * @return LY_SUCCESS on success
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lys_value_validate(struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
+                          ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format);
+
 /** @} */
 
 #ifdef __cplusplus