plugins types REFACTOR split store callback into store and resolve cb (#1220)

Refs #1161

Co-authored-by: Radek Krejci <rkrejci@cesnet.cz>
diff --git a/src/diff.c b/src/diff.c
index 0c28bf4..dff6acb 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -1127,7 +1127,7 @@
             meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
             LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_match)), LY_EINT);
             str_val = meta->value.canonical;
-            ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val), NULL);
+            ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
             if (!ret) {
                 /* values are the same, remove orig-value meta and set oper to NONE */
                 lyd_free_meta_single(meta);
diff --git a/src/parser.c b/src/parser.c
index 6b6ab74..c915fc1 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -407,31 +407,30 @@
 
 LY_ERR
 lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const char *value, size_t value_len,
-        ly_bool *dynamic, uint32_t value_hints, LY_PREFIX_FORMAT format, void *prefix_data, struct lyd_node **node)
+        ly_bool *dynamic, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints, struct lyd_node **node)
 {
-    LY_ERR ret;
+    ly_bool incomplete;
 
-    ret = lyd_create_term(schema, value, value_len, dynamic, value_hints, format, prefix_data, node);
-    if (ret == LY_EINCOMPLETE) {
-        if (!(lydctx->parse_options & LYD_PARSE_ONLY)) {
-            LY_CHECK_RET(ly_set_add(&lydctx->unres_node_type, *node, LY_SET_OPT_USEASLIST, NULL));
-        }
-        ret = LY_SUCCESS;
+    LY_CHECK_RET(lyd_create_term(schema, value, value_len, dynamic, format, prefix_data, hints, &incomplete, node));
+
+    if (incomplete && !(lydctx->parse_options & LYD_PARSE_ONLY)) {
+        LY_CHECK_RET(ly_set_add(&lydctx->unres_node_type, *node, LY_SET_OPT_USEASLIST, NULL));
     }
-    return ret;
+    return LY_SUCCESS;
 }
 
 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, ly_bool *dynamic, uint32_t value_hints,
-        LY_PREFIX_FORMAT format, void *prefix_data, const struct lysc_node *ctx_snode)
+        const char *name, size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, LY_PREFIX_FORMAT format,
+        void *prefix_data, uint32_t hints)
 {
-    LY_ERR ret;
-    ret = lyd_create_meta(parent, meta, mod, name, name_len, value, value_len, dynamic, value_hints, format, prefix_data,
-                          ctx_snode);
-    if (ret == LY_EINCOMPLETE) {
+    ly_bool incomplete;
+
+    LY_CHECK_RET(lyd_create_meta(parent, meta, mod, name, name_len, value, value_len, dynamic, format, prefix_data,
+            hints, &incomplete));
+
+    if (incomplete && !(lydctx->parse_options & LYD_PARSE_ONLY)) {
         LY_CHECK_RET(ly_set_add(&lydctx->unres_meta_type, *meta, LY_SET_OPT_USEASLIST, NULL));
-        ret = LY_SUCCESS;
     }
-    return ret;
+    return LY_SUCCESS;
 }
diff --git a/src/parser_internal.h b/src/parser_internal.h
index 87553f9..79496c7 100644
--- a/src/parser_internal.h
+++ b/src/parser_internal.h
@@ -168,20 +168,19 @@
  * @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.
+ * @param[in] 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,
-        ly_bool *dynamic, uint32_t value_hints, LY_PREFIX_FORMAT format, void *prefix_data, struct lyd_node **node);
+        ly_bool *dynamic, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints, 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.
+ * @param[in] hints [Value hint](@ref lydvalhints) 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, ly_bool *dynamic, uint32_t value_hints, LY_PREFIX_FORMAT format,
-        void *prefix_data, const struct lysc_node *ctx_snode);
+        size_t value_len, ly_bool *dynamic, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints);
 
 #endif /* LY_PARSER_INTERNAL_H_ */
diff --git a/src/parser_json.c b/src/parser_json.c
index 1e148a8..38e7d97 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -195,7 +195,7 @@
     *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 || (parent && !parent->schema && (((struct lyd_node_opaq *)parent)->hints & LYD_NODEHINT_ENVELOPE))) {
         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);
@@ -220,7 +220,7 @@
         }
     }
 
-    if (parent && !parent->schema && (((struct lyd_node_opaq *)parent)->hint & LYD_NODE_OPAQ_ISENVELOPE)) {
+    if (parent && !parent->schema && (((struct lyd_node_opaq *)parent)->hints & LYD_NODEHINT_ENVELOPE)) {
         /* ignore the envelope parent when searchinf for the schema node */
         parent = NULL;
     }
@@ -449,13 +449,13 @@
         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;
+        *type_hint_p = LYD_VALHINT_EMPTY;
     } else if (*status_p == LYJSON_STRING) {
-        *type_hint_p = LYD_NODE_OPAQ_ISSTRING;
+        *type_hint_p = LYD_VALHINT_STRING | LYD_VALHINT_NUM64;
     } else if (*status_p == LYJSON_NUMBER) {
-        *type_hint_p = LYD_NODE_OPAQ_ISNUMBER;
+        *type_hint_p = LYD_VALHINT_DECNUM;
     } else if (*status_p == LYJSON_FALSE || *status_p == LYJSON_TRUE) {
-        *type_hint_p = LYD_NODE_OPAQ_ISBOOLEAN;
+        *type_hint_p = LYD_VALHINT_BOOLEAN;
     } else if (*status_p == LYJSON_NULL) {
         *type_hint_p = 0;
     } else {
@@ -583,7 +583,7 @@
                     continue;
                 }
 
-                if (((struct lyd_node_opaq *)node)->hint & LYD_NODE_OPAQ_ISLIST) {
+                if (((struct lyd_node_opaq *)node)->hints & LYD_NODEHINT_LIST) {
                     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;
@@ -603,11 +603,12 @@
                     ly_bool 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);
+                    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));
+                    ret = lyd_create_attr(node, NULL, lydctx->jsonctx->ctx, meta->name, strlen(meta->name), meta->value,
+                            ly_strlen(meta->value), &dynamic, LYD_JSON, meta->hints, 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);
                 }
 
@@ -641,7 +642,7 @@
                     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, LY_PREF_JSON, NULL, snode);
+                                                     &dynamic, LY_PREF_JSON, NULL, meta->hints);
                         LY_CHECK_GOTO(ret, cleanup);
                     } else if (lydctx->parse_options & LYD_PARSE_STRICT) {
                         LOGVAL(lydctx->jsonctx->ctx, LY_VLOG_LYD, node, LYVE_REFERENCE,
@@ -824,7 +825,8 @@
             /* 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, LY_PREF_JSON, NULL, snode);
+                                         lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_PREF_JSON, NULL,
+                                         LYD_HINT_DATA);
             LY_CHECK_GOTO(ret, cleanup);
 
             /* add/correct flags */
@@ -842,7 +844,7 @@
 
             /* 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);
+                                  &lydctx->jsonctx->dynamic, LYD_JSON, 0, val_prefs, prefix, prefix_len, module_name, module_name_len);
             LY_CHECK_GOTO(ret, cleanup);
         }
         /* next member */
@@ -951,8 +953,8 @@
 
     /* 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);
+    ret = lyd_create_opaq(lydctx->jsonctx->ctx, name, name_len, value, value_len, &dynamic, LYD_JSON, type_hint,
+            val_prefs, prefix, prefix_len, module_name, module_name_len, node_p);
     if (dynamic) {
         free((char *)value);
     }
@@ -967,7 +969,7 @@
     } 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;
+        ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST;
 
         if (*status_inner_p == LYJSON_OBJECT || *status_inner_p == LYJSON_OBJECT_EMPTY) {
             /* but first process children of the object in the array */
@@ -1089,18 +1091,18 @@
         enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
 {
     LY_ERR ret;
-    uint32_t type_hint;
+    uint32_t type_hints;
     uint32_t prev_opts;
     struct lyd_node *tree = NULL;
 
-    ret = lydjson_data_check_opaq(lydctx, snode, &type_hint);
+    ret = lydjson_data_check_opaq(lydctx, snode, &type_hints);
     if (ret == LY_SUCCESS) {
         assert(snode->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER | LYD_NODE_ANY));
         if (snode->nodetype & LYD_NODE_TERM) {
             /* 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,
-                                        LY_PREF_JSON, NULL, node);
+                                         lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_PREF_JSON, NULL,
+                                         type_hints, node);
             LY_CHECK_RET(ret);
 
             /* move JSON parser */
@@ -1178,8 +1180,10 @@
                                  status, status, first_p, node);
         LY_CHECK_RET(ret);
 
-        if (snode->nodetype & (LYS_LEAFLIST | LYS_LIST)) {
-            ((struct lyd_node_opaq *)*node)->hint |= LYD_NODE_OPAQ_ISLIST;
+        if (snode->nodetype == LYS_LIST) {
+            ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LIST;
+        } else if (snode->nodetype == LYS_LEAFLIST) {
+            ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LEAFLIST;
         }
     }
 
@@ -1503,12 +1507,12 @@
     /* 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);
+    ret = lyd_create_opaq(jsonctx->ctx, "notification", 12, "", 0, NULL, LYD_JSON, LYD_NODEHINT_ENVELOPE, 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);
+    ret = lyd_create_opaq(jsonctx->ctx, "eventTime", 9, value, value_len, &dynamic, LYD_JSON, LYD_VALHINT_STRING, NULL,
+                          NULL, 0, "ietf-restconf", 13, &et);
     LY_CHECK_GOTO(ret, cleanup);
     /* insert eventTime into notification */
     lyd_insert_node(*envp_p, NULL, et);
@@ -1647,9 +1651,8 @@
     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);
+    ret = lyd_create_opaq(jsonctx->ctx, object_id, strlen(object_id), "", 0, NULL, LYD_JSON, LYD_NODEHINT_ENVELOPE,
+                          NULL, NULL, 0, module_key, ly_strlen(module_key), envp_p);
     LY_CHECK_GOTO(ret, cleanup);
 
     if (parent) {
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index 9395a13..ea2a7aa 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -337,12 +337,11 @@
  * @brief Parse YANG node metadata.
  *
  * @param[in] lybctx LYB context.
- * @param[in] sparent Schema parent node.
  * @param[out] meta Parsed metadata.
  * @return LY_ERR value.
  */
 static LY_ERR
-lyb_parse_metadata(struct lyd_lyb_ctx *lybctx, const struct lysc_node *sparent, struct lyd_meta **meta)
+lyb_parse_metadata(struct lyd_lyb_ctx *lybctx, struct lyd_meta **meta)
 {
     LY_ERR ret = LY_SUCCESS;
     ly_bool dynamic;
@@ -381,7 +380,7 @@
 
         /* create metadata */
         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, LY_PREF_JSON, NULL, sparent);
+                                     ly_strlen(meta_value), &dynamic, LY_PREF_JSON, NULL, LYD_HINT_DATA);
 
         /* free strings */
         free(meta_name);
@@ -507,8 +506,8 @@
         dynamic = 1;
 
         /* attr2 is always changed to the created attribute */
-        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));
+        ret = lyd_create_attr(NULL, &attr2, lybctx->ctx, name, strlen(name), value, ly_strlen(value), &dynamic, format,
+                              0, val_prefs, prefix, ly_strlen(prefix), module_name, ly_strlen(module_name));
         LY_CHECK_GOTO(ret, cleanup);
 
         free(prefix);
@@ -714,7 +713,7 @@
 
     /* create metadata/attributes */
     if (snode) {
-        ret = lyb_parse_metadata(lybctx, snode, &meta);
+        ret = lyb_parse_metadata(lybctx, &meta);
         LY_CHECK_GOTO(ret, cleanup);
     } else {
         ret = lyb_parse_attributes(lybctx->lybctx, &attr);
@@ -747,7 +746,7 @@
         dynamic = 1;
 
         /* create node */
-        ret = lyd_create_opaq(ctx, name, strlen(name), value, strlen(value), &dynamic, 0, format, val_prefs, prefix,
+        ret = lyd_create_opaq(ctx, name, strlen(name), value, strlen(value), &dynamic, format, 0, val_prefs, prefix,
                               ly_strlen(prefix), module_key, ly_strlen(module_key), &node);
         LY_CHECK_GOTO(ret, cleanup);
 
@@ -763,8 +762,8 @@
         dynamic = 1;
 
         /* create node */
-        ret = lyd_parser_create_term((struct lyd_ctx *)lybctx, snode, value, ly_strlen(value), &dynamic, 0,
-                                     LY_PREF_JSON, NULL, &node);
+        ret = lyd_parser_create_term((struct lyd_ctx *)lybctx, snode, value, ly_strlen(value), &dynamic, LY_PREF_JSON,
+                                     NULL, LYD_HINT_DATA, &node);
         if (dynamic) {
             free(value);
             dynamic = 0;
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 914b643..c4f0229 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -62,7 +62,7 @@
 }
 
 static LY_ERR
-lydxml_metadata(struct lyd_xml_ctx *lydctx, const struct lysc_node *sparent, struct lyd_meta **meta)
+lydxml_metadata(struct lyd_xml_ctx *lydctx, struct lyd_meta **meta)
 {
     LY_ERR ret = LY_EVALID;
     const struct lyxml_ns *ns;
@@ -119,7 +119,7 @@
 
         /* create metadata */
         ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, NULL, meta, mod, name, name_len, xmlctx->value,
-                                     xmlctx->value_len, &xmlctx->dynamic, 0, LY_PREF_XML, &xmlctx->ns, sparent);
+                xmlctx->value_len, &xmlctx->dynamic, LY_PREF_XML, &xmlctx->ns, LYD_HINT_DATA);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* next attribute */
@@ -181,7 +181,7 @@
 
         /* attr2 is always changed to the created attribute */
         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,
+                              &xmlctx->dynamic, LYD_XML, 0, val_prefs, prefix, prefix_len,
                               ns ? ns->uri : NULL, ns ? strlen(ns->uri) : 0);
         LY_CHECK_GOTO(ret, cleanup);
 
@@ -448,7 +448,7 @@
     /* create metadata/attributes */
     if (xmlctx->status == LYXML_ATTRIBUTE) {
         if (snode) {
-            ret = lydxml_metadata(lydctx, snode, &meta);
+            ret = lydxml_metadata(lydctx, &meta);
             LY_CHECK_GOTO(ret, error);
         } else {
             assert(lydctx->parse_options & LYD_PARSE_OPAQ);
@@ -472,8 +472,8 @@
         }
 
         /* create node */
-        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), &node);
+        ret = lyd_create_opaq(ctx, name, name_len, xmlctx->value, xmlctx->value_len, &xmlctx->dynamic, LYD_XML,
+                              LYD_HINT_DATA, val_prefs, prefix, prefix_len, ns->uri, strlen(ns->uri), &node);
         LY_CHECK_GOTO(ret, error);
 
         /* parser next */
@@ -487,7 +487,7 @@
     } else if (snode->nodetype & LYD_NODE_TERM) {
         /* create node */
         LY_CHECK_GOTO(ret = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, xmlctx->value, xmlctx->value_len,
-                                        &xmlctx->dynamic, 0, LY_PREF_XML, &xmlctx->ns, &node), error);
+                                        &xmlctx->dynamic, LY_PREF_XML, &xmlctx->ns, LYD_HINT_DATA, &node), error);
 
         if (parent && (node->schema->flags & LYS_KEY)) {
             /* check the key order, the anchor must never be a key */
@@ -701,8 +701,8 @@
     LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
 
     /* create node */
-    ret = lyd_create_opaq(xmlctx->ctx, name, strlen(name), "", 0, NULL, 0, LYD_XML, NULL, prefix, prefix_len,
-                          uri, strlen(uri), envp);
+    ret = lyd_create_opaq(xmlctx->ctx, name, strlen(name), "", 0, NULL, LYD_XML, LYD_NODEHINT_ENVELOPE, NULL, prefix,
+            prefix_len, uri, strlen(uri), envp);
     LY_CHECK_GOTO(ret, cleanup);
 
     /* assign atributes */
@@ -872,8 +872,8 @@
     }*/
 
     /* create node */
-    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);
+    ret = lyd_create_opaq(xmlctx->ctx, "eventTime", 9, xmlctx->value, xmlctx->value_len, NULL, LYD_XML,
+            LYD_NODEHINT_ENVELOPE, NULL, prefix, prefix_len, ns->uri, strlen(ns->uri), &et);
     LY_CHECK_GOTO(ret, cleanup);
 
     /* assign atributes */
diff --git a/src/path.c b/src/path.c
index 07cd775..fe6adeb 100644
--- a/src/path.c
+++ b/src/path.c
@@ -490,8 +490,9 @@
 
             /* Literal */
             assert(expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL);
-            LY_CHECK_RET(lyd_value_store(&p->value, key, expr->expr + expr->tok_pos[*tok_idx] + 1,
-                                         expr->tok_len[*tok_idx] - 2, NULL, format, prefix_data));
+            LY_CHECK_RET(lyd_value_store(ctx, &p->value, ((struct lysc_node_leaf *)key)->type,
+                    expr->expr + expr->tok_pos[*tok_idx] + 1, expr->tok_len[*tok_idx] - 2, NULL, format, prefix_data,
+                    LYD_HINT_DATA, key, NULL, LY_VLOG_LYSC, key));
             ++(*tok_idx);
 
             /* ']' */
@@ -533,8 +534,9 @@
 
         assert(expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL);
         /* store the value */
-        LY_CHECK_RET(lyd_value_store(&p->value, ctx_node, expr->expr + expr->tok_pos[*tok_idx] + 1,
-                                     expr->tok_len[*tok_idx] - 2, NULL, format, prefix_data));
+        LY_CHECK_RET(lyd_value_store(ctx, &p->value, ((struct lysc_node_leaflist *)ctx_node)->type,
+                expr->expr + expr->tok_pos[*tok_idx] + 1, expr->tok_len[*tok_idx] - 2, NULL, format, prefix_data,
+                LYD_HINT_DATA, ctx_node, NULL, LY_VLOG_LYSC, ctx_node));
         ++(*tok_idx);
 
         /* ']' */
diff --git a/src/plugins_types.c b/src/plugins_types.c
index ed2981a..e4ce746 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -511,39 +511,124 @@
     }
 }
 
+static int
+type_get_hints_base(uint32_t hints)
+{
+    /* set allowed base */
+    switch (hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM)) {
+    case LYD_VALHINT_DECNUM:
+        return 10;
+    case LYD_VALHINT_OCTNUM:
+        return 8;
+    case LYD_VALHINT_HEXNUM:
+        return 16;
+    default:
+        break;
+    }
+
+    return 0;
+}
+
+/**
+ * @brief Answer if the type is suitable for the parser's hit (if any) in the specified format
+ */
+static LY_ERR
+type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_DATA_TYPE type, int *base, struct ly_err_item **err)
+{
+    char *msg;
+
+    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 (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM))) {
+            asprintf(&msg, "Invalid non-number-encoded %s value \"%.*s\".", lys_datatype2str(type), (int)value_len, value);
+            *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_DATA, msg, NULL, NULL);
+            return LY_EVALID;
+        }
+        *base = type_get_hints_base(hints);
+        break;
+    case LY_TYPE_UINT64:
+    case LY_TYPE_INT64:
+        if (!(hints & LYD_VALHINT_NUM64)) {
+            asprintf(&msg, "Invalid non-num64-encoded %s value \"%.*s\".", lys_datatype2str(type), (int)value_len, value);
+            *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_DATA, msg, NULL, NULL);
+            return LY_EVALID;
+        }
+        *base = type_get_hints_base(hints);
+        break;
+    case LY_TYPE_STRING:
+    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 (!(hints & LYD_VALHINT_STRING)) {
+            asprintf(&msg, "Invalid non-string-encoded %s value \"%.*s\".", lys_datatype2str(type), (int)value_len, value);
+            *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_DATA, msg, NULL, NULL);
+            return LY_EVALID;
+        }
+        break;
+    case LY_TYPE_BOOL:
+        if (!(hints & LYD_VALHINT_BOOLEAN)) {
+            asprintf(&msg, "Invalid non-boolean-encoded %s value \"%.*s\".", lys_datatype2str(type), (int)value_len, value);
+            *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_DATA, msg, NULL, NULL);
+            return LY_EVALID;
+        }
+        break;
+    case LY_TYPE_EMPTY:
+        if (!(hints & LYD_VALHINT_EMPTY)) {
+            asprintf(&msg, "Invalid non-empty-encoded %s value \"%.*s\".", lys_datatype2str(type), (int)value_len, value);
+            *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_DATA, msg, NULL, NULL);
+            return LY_EVALID;
+        }
+        break;
+    case LY_TYPE_UNKNOWN:
+    case LY_TYPE_LEAFREF:
+    case LY_TYPE_UNION:
+        LOGINT_RET(NULL);
+    }
+
+    return LY_SUCCESS;
+}
+
 /**
  * @brief Validate, canonize and store value of the YANG built-in signed integer types.
  *
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_int(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, uint32_t options,
-        LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), const void *UNUSED(context_node),
-        const struct lyd_node *UNUSED(tree), struct lyd_value *storage, struct ly_err_item **err)
+ly_type_store_int(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+        uint32_t options, LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct ly_err_item **err)
 {
     LY_ERR ret;
     int64_t num;
+    int base;
     char *str;
     struct lysc_type_num *type_num = (struct lysc_type_num *)type;
 
-    if (options & LY_TYPE_OPTS_SECOND_CALL) {
-        return LY_SUCCESS;
-    }
+    /* check hints */
+    LY_CHECK_RET(type_check_hints(hints, value, value_len, type->basetype, &base, err));
 
     switch (type->basetype) {
     case LY_TYPE_INT8:
-        LY_CHECK_RET(ly_type_parse_int("int8", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, INT64_C(-128), INT64_C(127), value, value_len, &num, err));
+        LY_CHECK_RET(ly_type_parse_int("int8", base, INT64_C(-128), INT64_C(127), value, value_len, &num, err));
         break;
     case LY_TYPE_INT16:
-        LY_CHECK_RET(ly_type_parse_int("int16", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, INT64_C(-32768), INT64_C(32767), value, value_len, &num, err));
+        LY_CHECK_RET(ly_type_parse_int("int16", base, INT64_C(-32768), INT64_C(32767), value, value_len, &num, err));
         break;
     case LY_TYPE_INT32:
-        LY_CHECK_RET(ly_type_parse_int("int32", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10,
-                                       INT64_C(-2147483648), INT64_C(2147483647), value, value_len, &num, err));
+        LY_CHECK_RET(ly_type_parse_int("int32", base, INT64_C(-2147483648), INT64_C(2147483647), value, value_len, &num,
+                err));
         break;
     case LY_TYPE_INT64:
-        LY_CHECK_RET(ly_type_parse_int("int64", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10,
-                                       INT64_C(-9223372036854775807) - INT64_C(1), INT64_C(9223372036854775807), value, value_len, &num, err));
+        LY_CHECK_RET(ly_type_parse_int("int64", base, INT64_C(-9223372036854775807) - INT64_C(1),
+                INT64_C(9223372036854775807), value, value_len, &num, err));
         break;
     default:
         LOGINT_RET(ctx);
@@ -573,31 +658,31 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_uint(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, uint32_t options,
-        LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), const void *UNUSED(context_node),
-        const struct lyd_node *UNUSED(tree), struct lyd_value *storage, struct ly_err_item **err)
+ly_type_store_uint(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+        uint32_t options, LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct ly_err_item **err)
 {
     LY_ERR ret;
     uint64_t num;
+    int base;
     struct lysc_type_num *type_num = (struct lysc_type_num *)type;
     char *str;
 
-    if (options & LY_TYPE_OPTS_SECOND_CALL) {
-        return LY_SUCCESS;
-    }
+    /* check hints */
+    LY_CHECK_RET(type_check_hints(hints, value, value_len, type->basetype, &base, err));
 
     switch (type->basetype) {
     case LY_TYPE_UINT8:
-        LY_CHECK_RET(ly_type_parse_uint("uint8", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, UINT64_C(255), value, value_len, &num, err));
+        LY_CHECK_RET(ly_type_parse_uint("uint8", base, UINT64_C(255), value, value_len, &num, err));
         break;
     case LY_TYPE_UINT16:
-        LY_CHECK_RET(ly_type_parse_uint("uint16", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, UINT64_C(65535), value, value_len, &num, err));
+        LY_CHECK_RET(ly_type_parse_uint("uint16", base, UINT64_C(65535), value, value_len, &num, err));
         break;
     case LY_TYPE_UINT32:
-        LY_CHECK_RET(ly_type_parse_uint("uint32", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, UINT64_C(4294967295), value, value_len, &num, err));
+        LY_CHECK_RET(ly_type_parse_uint("uint32", base, UINT64_C(4294967295), value, value_len, &num, err));
         break;
     case LY_TYPE_UINT64:
-        LY_CHECK_RET(ly_type_parse_uint("uint64", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, UINT64_C(18446744073709551615), value, value_len, &num, err));
+        LY_CHECK_RET(ly_type_parse_uint("uint64", base, UINT64_C(18446744073709551615), value, value_len, &num, err));
         break;
     default:
         LOGINT_RET(ctx);
@@ -627,23 +712,22 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_decimal64(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, uint32_t options,
-        LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), const void *UNUSED(context_node),
-        const struct lyd_node *UNUSED(tree), struct lyd_value *storage, struct ly_err_item **err)
+ly_type_store_decimal64(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+        uint32_t options, LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct ly_err_item **err)
 {
     int64_t d;
     struct lysc_type_dec *type_dec = (struct lysc_type_dec *)type;
     char buf[22];
 
-    if (options & LY_TYPE_OPTS_SECOND_CALL) {
-        return LY_SUCCESS;
-    }
-
     if (!value || !value[0] || !value_len) {
         *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_DATA, strdup("Invalid empty decimal64 value."), NULL, NULL);
         return LY_EVALID;
     }
 
+    /* check hints */
+    LY_CHECK_RET(type_check_hints(hints, value, value_len, type->basetype, NULL, err));
+
     LY_CHECK_RET(ly_type_parse_dec64(type_dec->fraction_digits, value, value_len, &d, err));
     /* prepare canonized value */
     if (d) {
@@ -693,9 +777,9 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_binary(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, uint32_t options,
-        LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), const void *UNUSED(context_node),
-        const struct lyd_node *UNUSED(tree), struct lyd_value *storage, struct ly_err_item **err)
+ly_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+        uint32_t options, LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct ly_err_item **err)
 {
     size_t start = 0, stop = 0, count = 0, u, termination = 0;
     struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
@@ -707,9 +791,8 @@
     /* initiate */
     *err = NULL;
 
-    if (options & LY_TYPE_OPTS_SECOND_CALL) {
-        return LY_SUCCESS;
-    }
+    /* check hints */
+    LY_CHECK_RET(type_check_hints(hints, value, value_len, type->basetype, NULL, err));
 
     /* validate characters and remember the number of octets for length validation */
     if (value_len) {
@@ -796,15 +879,14 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_string(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, uint32_t options,
-        LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), const void *UNUSED(context_node),
-        const struct lyd_node *UNUSED(tree), struct lyd_value *storage, struct ly_err_item **err)
+ly_type_store_string(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+        uint32_t options, LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct ly_err_item **err)
 {
     struct lysc_type_str *type_str = (struct lysc_type_str *)type;
 
-    if (options & LY_TYPE_OPTS_SECOND_CALL) {
-        return LY_SUCCESS;
-    }
+    /* check hints */
+    LY_CHECK_RET(type_check_hints(hints, value, value_len, type->basetype, NULL, err));
 
     /* length restriction of the string */
     if (type_str->length) {
@@ -836,9 +918,9 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_bits(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, uint32_t options,
-        LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), const void *UNUSED(context_node),
-        const struct lyd_node *UNUSED(tree), struct lyd_value *storage, struct ly_err_item **err)
+ly_type_store_bits(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+        uint32_t options, LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct ly_err_item **err)
 {
     LY_ERR r, ret = LY_EVALID;
     size_t item_len;
@@ -856,9 +938,8 @@
     const char *can = NULL;
     int rc = 0;
 
-    if (options & LY_TYPE_OPTS_SECOND_CALL) {
-        return LY_SUCCESS;
-    }
+    /* check hints */
+    LY_CHECK_RET(type_check_hints(hints, value, value_len, type->basetype, NULL, err));
 
     /* remember the present items for further work */
     LY_CHECK_RET(ly_set_new(&items));
@@ -1028,18 +1109,17 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_enum(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, uint32_t options,
-        LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), const void *UNUSED(context_node),
-        const struct lyd_node *UNUSED(tree), struct lyd_value *storage, struct ly_err_item **err)
+ly_type_store_enum(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+        uint32_t options, LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct ly_err_item **err)
 {
     LY_ARRAY_COUNT_TYPE u, v;
     char *errmsg = NULL;
     struct lysc_type_enum *type_enum = (struct lysc_type_enum *)type;
     int rc = 0;
 
-    if (options & LY_TYPE_OPTS_SECOND_CALL) {
-        return LY_SUCCESS;
-    }
+    /* check hints */
+    LY_CHECK_RET(type_check_hints(hints, value, value_len, type->basetype, NULL, err));
 
     /* find the matching enumeration value item */
     LY_ARRAY_FOR(type_enum->enums, u) {
@@ -1090,16 +1170,14 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_boolean(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len,
-        uint32_t options, LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data),
-        const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
-        struct lyd_value *storage, struct ly_err_item **err)
+ly_type_store_boolean(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+        uint32_t options, LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct ly_err_item **err)
 {
     int8_t i;
 
-    if (options & LY_TYPE_OPTS_SECOND_CALL) {
-        return LY_SUCCESS;
-    }
+    /* check hints */
+    LY_CHECK_RET(type_check_hints(hints, value, value_len, type->basetype, NULL, err));
 
     if (value_len == 4 && !strncmp(value, "true", 4)) {
         i = 1;
@@ -1133,14 +1211,12 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_empty(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, uint32_t options,
-        LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data),
-        const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
-        struct lyd_value *storage, struct ly_err_item **err)
+ly_type_store_empty(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+        uint32_t UNUSED(options), LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct ly_err_item **err)
 {
-    if (options & LY_TYPE_OPTS_SECOND_CALL) {
-        return LY_SUCCESS;
-    }
+    /* check hints */
+    LY_CHECK_RET(type_check_hints(hints, value, value_len, type->basetype, NULL, err));
 
     if (value_len) {
         char *errmsg;
@@ -1197,9 +1273,9 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_identityref(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, uint32_t options,
-        LY_PREFIX_FORMAT format, void *prefix_data, const void *UNUSED(context_node),
-        const struct lyd_node *UNUSED(tree), struct lyd_value *storage, struct ly_err_item **err)
+ly_type_store_identityref(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+        uint32_t options, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct ly_err_item **err)
 {
     struct lysc_type_identityref *type_ident = (struct lysc_type_identityref *)type;
     const char *id_name, *prefix = value;
@@ -1211,9 +1287,8 @@
     int rc = 0;
     ly_bool dyn;
 
-    if (options & LY_TYPE_OPTS_SECOND_CALL) {
-        return LY_SUCCESS;
-    }
+    /* check hints */
+    LY_CHECK_RET(type_check_hints(hints, value, value_len, type->basetype, NULL, err));
 
     /* locate prefix if any */
     for (prefix_len = 0; prefix_len < value_len && value[prefix_len] != ':'; ++prefix_len) {}
@@ -1334,31 +1409,24 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_instanceid(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, uint32_t options,
-        LY_PREFIX_FORMAT format, void *prefix_data, const void *context_node, const struct lyd_node *tree,
+ly_type_store_instanceid(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+        uint32_t options, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
         struct lyd_value *storage, struct ly_err_item **err)
 {
-    LY_ERR ret = LY_EVALID;
+    LY_ERR ret = LY_SUCCESS;
     struct lysc_type_instanceid *type_inst = (struct lysc_type_instanceid *)type;
     char *errmsg = NULL, *str;
     struct ly_path *path = NULL;
     struct lyxp_expr *exp = NULL;
-    const struct lysc_node *ctx_scnode;
     int rc = 0;
     uint32_t prefix_opt = 0;
     ly_bool dyn;
 
     /* init */
     *err = NULL;
-    ctx_scnode = (options & (LY_TYPE_OPTS_SCHEMA | LY_TYPE_OPTS_INCOMPLETE_DATA)) ?
-            (struct lysc_node *)context_node : ((struct lyd_node *)context_node)->schema;
 
-    if ((options & LY_TYPE_OPTS_SCHEMA) && (options & LY_TYPE_OPTS_INCOMPLETE_DATA)) {
-        /* we have incomplete schema tree */
-        /* HACK temporary storing of the original value */
-        LY_CHECK_RET(lydict_insert(ctx, value_len ? value : "", value_len, &storage->canonical));
-        goto cleanup;
-    }
+    /* check hints */
+    LY_CHECK_RET(type_check_hints(hints, value, value_len, type->basetype, NULL, err));
 
     switch (format) {
     case LY_PREF_SCHEMA:
@@ -1370,27 +1438,8 @@
         break;
     }
 
-    if (!(options & LY_TYPE_OPTS_SCHEMA) && (options & LY_TYPE_OPTS_SECOND_CALL)) {
-        /* 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)) {
-            /* in error message we print the JSON format of the instance-identifier - in case of XML, it is not possible
-             * to get the exactly same string as original, JSON is less demanding and still well readable/understandable. */
-            ly_bool dynamic = 0;
-            const char *id = storage->realtype->plugin->print(storage, LY_PREF_JSON, NULL, &dynamic);
-            rc = asprintf(&errmsg, "Invalid instance-identifier \"%s\" value - required instance not found.", id);
-            if (dynamic) {
-                free((char *)id);
-            }
-            /* we have to clean up the storage */
-            type->plugin->free(ctx, storage);
-
-            goto error;
-        }
-        return LY_SUCCESS;
-    }
-
     /* parse the value */
-    ret = ly_path_parse(ctx, ctx_scnode, value, value_len, LY_PATH_BEGIN_ABSOLUTE, LY_PATH_LREF_FALSE,
+    ret = ly_path_parse(ctx, ctx_node, value, value_len, LY_PATH_BEGIN_ABSOLUTE, LY_PATH_LREF_FALSE,
                         prefix_opt, LY_PATH_PRED_SIMPLE, &exp);
     if (ret) {
         rc = asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - syntax error.", (int)value_len, value);
@@ -1398,25 +1447,13 @@
     }
 
     /* resolve it on schema tree */
-    ret = ly_path_compile(ctx, ctx_scnode->module, NULL, exp, LY_PATH_LREF_FALSE, lysc_is_output(ctx_scnode) ?
-                          LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_SINGLE, format, prefix_data, &path);
+    ret = ly_path_compile(ctx, NULL, ctx_node, exp, LY_PATH_LREF_FALSE, lysc_is_output(ctx_node) ?
+            LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_SINGLE, format, prefix_data, &path);
     if (ret) {
         rc = asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - semantic error.", (int)value_len, value);
         goto error;
     }
 
-    /* find it in data */
-    if (!(options & LY_TYPE_OPTS_INCOMPLETE_DATA) && !(options & LY_TYPE_OPTS_SCHEMA) && type_inst->require_instance) {
-        ret = ly_path_eval(path, tree, NULL);
-        if (ret) {
-            rc = asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - instance not found.", (int)value_len, value);
-            goto error;
-        }
-    }
-
-    /* HACK remove previously stored original value */
-    lydict_remove(ctx, storage->canonical);
-
     /* store resolved schema path */
     storage->target = path;
     path = NULL;
@@ -1428,7 +1465,7 @@
 
     storage->realtype = type;
 
-cleanup:
+    /* cleanup */
     lyxp_expr_free(ctx, exp);
     ly_path_free(ctx, path);
 
@@ -1436,7 +1473,8 @@
         free((char *)value);
     }
 
-    if ((options & LY_TYPE_OPTS_INCOMPLETE_DATA) && ((options & LY_TYPE_OPTS_SCHEMA) || type_inst->require_instance)) {
+    if (type_inst->require_instance) {
+        /* needs to be resolved */
         return LY_EINCOMPLETE;
     } else {
         return LY_SUCCESS;
@@ -1458,6 +1496,38 @@
 }
 
 /**
+ * @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(const struct ly_ctx *UNUSED(ctx), const struct lysc_type *UNUSED(type),
+        const struct lyd_node *UNUSED(ctx_node), const struct lyd_node *tree, struct lyd_value *storage,
+        struct ly_err_item **err)
+{
+    struct lysc_type_instanceid *type_inst = (struct lysc_type_instanceid *)storage->realtype;
+    LY_ERR ret;
+    char *errmsg;
+
+    *err = NULL;
+
+    if (!type_inst->require_instance) {
+        /* redundant to resolve */
+        return LY_SUCCESS;
+    }
+
+    /* find the target in data */
+    ret = ly_path_eval(storage->target, tree, NULL);
+    if (ret) {
+        asprintf(&errmsg, "Invalid instance-identifier \"%s\" value - required instance not found.", storage->canonical);
+        *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_DATA, errmsg, NULL, NULL);
+        return ret;
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
  * @brief Comparison callback checking the instance-identifier value.
  *
  * Implementation of the ly_type_compare_clb.
@@ -1728,69 +1798,63 @@
 }
 
 /**
- * @brief Validate, canonize and store value of the YANG built-in leafref type.
+ * @brief Store and canonize value of the YANG built-in leafref type.
  *
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_leafref(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, uint32_t options,
-        LY_PREFIX_FORMAT format, void *prefix_data, const void *context_node, const struct lyd_node *tree,
+ly_type_store_leafref(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+        uint32_t options, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
         struct lyd_value *storage, struct ly_err_item **err)
 {
     LY_ERR ret = LY_SUCCESS;
-    char *errmsg = NULL;
     struct lysc_type_leafref *type_lr = (struct lysc_type_leafref *)type;
-    struct ly_path *p = NULL;
-    struct ly_set *set = NULL;
 
-    if (!type_lr->realtype) {
-        if ((options & LY_TYPE_OPTS_SCHEMA) && (options & LY_TYPE_OPTS_INCOMPLETE_DATA)) {
-            /* leafref's path was not yet resolved - in schema trees, path can be resolved when
-             * the complete schema tree is present, in such a case we need to wait with validating
-             * default values */
-            return LY_EINCOMPLETE;
-        } else {
-            LOGINT(ctx);
-            return LY_EINT;
-        }
-    }
+    assert(type_lr->realtype);
 
-    if ((options & LY_TYPE_OPTS_SCHEMA) && (options & LY_TYPE_OPTS_SECOND_CALL)) {
-        /* hide the LY_TYPE_OPTS_SECOND_CALL option from the target's store callback, the option is connected
-         * only with the leafref's path, so it is not supposed to be used here. If the previous LY_EINCOMPLETE would
-         * be caused by the target's type, the target type's callback would be used directly, not via leafref's callback */
-        options &= ~LY_TYPE_OPTS_SECOND_CALL;
-    }
-
-    /* check value according to the real type of the leafref target */
+    /* store the value as the real type of the leafref target */
     ret = type_lr->realtype->plugin->store(ctx, type_lr->realtype, value, value_len, options, format, prefix_data,
-                                           context_node, tree, storage, err);
-    if (ret != LY_SUCCESS && ret != LY_EINCOMPLETE) {
-        goto cleanup;
+            hints, ctx_node, storage, err);
+    if (ret == LY_EINCOMPLETE) {
+        /* it is irrelevant whether the target type needs some resolving */
+        ret = LY_SUCCESS;
+    }
+    LY_CHECK_RET(ret);
+
+    if (type_lr->require_instance) {
+        /* needs to be resolved */
+        return LY_EINCOMPLETE;
+    } else {
+        return LY_SUCCESS;
+    }
+}
+
+/**
+ * @brief Validate value of the YANG built-in leafref type.
+ *
+ * Implementation of the ly_type_validate_clb.
+ */
+static LY_ERR
+ly_type_validate_leafref(const struct ly_ctx *UNUSED(ctx), const struct lysc_type *type, const struct lyd_node *ctx_node,
+        const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err)
+{
+    struct lysc_type_leafref *type_lr = (struct lysc_type_leafref *)type;
+    char *errmsg = NULL;
+
+    *err = NULL;
+
+    if (!type_lr->require_instance) {
+        /* redundant to resolve */
+        return LY_SUCCESS;
     }
 
-    /* rewrite leafref plugin stored in the storage by default */
-    storage->realtype = type_lr->realtype;
-
-    if (!(options & LY_TYPE_OPTS_SCHEMA) && type_lr->require_instance) {
-        if (options & LY_TYPE_OPTS_INCOMPLETE_DATA) {
-            ret = LY_EINCOMPLETE;
-            goto cleanup;
-        }
-
-        /* check leafref target existence */
-        ret = ly_type_find_leafref(type_lr, (struct lyd_node *)context_node, storage, tree, NULL, &errmsg);
-        if (ret) {
-            *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_DATA, errmsg, NULL, NULL);
-            ret = LY_EVALID;
-            goto cleanup;
-        }
+    /* check leafref target existence */
+    if (ly_type_find_leafref(type_lr, ctx_node, storage, tree, NULL, &errmsg)) {
+        *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_DATA, errmsg, NULL, NULL);
+        return LY_EVALID;
     }
 
-cleanup:
-    ly_path_free(ctx, p);
-    ly_set_free(set, NULL);
-    return ret;
+    return LY_SUCCESS;
 }
 
 /**
@@ -1840,55 +1904,6 @@
 }
 
 /**
- * @brief Answer if the type is suitable for the parser's hit (if any) in the specified format
- */
-LY_ERR
-type_check_parser_hint(LY_PREFIX_FORMAT format, uint32_t hint, LY_DATA_TYPE type)
-{
-    if (format == LY_PREF_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 Free stored prefix data of a union.
  *
  * @param[in] format Format of the prefixes.
@@ -1900,6 +1915,10 @@
     struct ly_set *ns_list;
     uint32_t i;
 
+    if (!prefix_data) {
+        return;
+    }
+
     switch (format) {
     case LY_PREF_XML:
         ns_list = prefix_data;
@@ -2055,131 +2074,161 @@
     return NULL;
 }
 
+static LY_ERR
+ly_type_union_store_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_value_subvalue *subvalue,
+        ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree, struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    LY_ARRAY_COUNT_TYPE u;
+    char *errmsg = NULL;
+    uint32_t prev_lo;
+
+    if (!types || !LY_ARRAY_COUNT(types)) {
+        return LY_EINVAL;
+    }
+
+    /* turn logging off */
+    prev_lo = ly_log_options(0);
+
+    /* use the first usable subtype to store the value */
+    for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
+        ret = types[u]->plugin->store(ctx, types[u], subvalue->original, strlen(subvalue->original),
+                                              0, subvalue->format, subvalue->prefix_data,
+                                              subvalue->hints, subvalue->ctx_node, &subvalue->value, err);
+        if ((ret == LY_SUCCESS) || (ret == LY_EINCOMPLETE)) {
+            if (resolve && (ret == LY_EINCOMPLETE)) {
+                /* we need the value resolved */
+                ret = subvalue->value.realtype->plugin->validate(ctx, types[u], ctx_node, tree, &subvalue->value, err);
+                if (!ret) {
+                    /* store and resolve successful */
+                    break;
+                }
+
+                /* resolve failed, we need to free the stored value */
+                types[u]->plugin->free(ctx, &subvalue->value);
+            } else {
+                /* store successful */
+                break;
+            }
+        }
+        ly_err_free(*err);
+        *err = NULL;
+    }
+
+    if (u == LY_ARRAY_COUNT(types)) {
+        if (asprintf(&errmsg, "Invalid union value \"%s\" - no matching subtype found.", subvalue->original) == -1) {
+            *err = ly_err_new(LY_LLERR, LY_EMEM, 0, "Memory allocation failed.", NULL, NULL);
+            ret = LY_EMEM;
+            goto cleanup;
+        }
+        *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_DATA, errmsg, NULL, NULL);
+        ret = LY_EVALID;
+        goto cleanup;
+    }
+
+cleanup:
+    /* restore logging */
+    ly_log_options(prev_lo);
+    return ret;
+}
+
 /**
- * @brief Validate, canonize and store value of the YANG built-in union type.
+ * @brief Store and canonize 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, uint32_t options,
-        LY_PREFIX_FORMAT format, void *prefix_data, const void *context_node, const struct lyd_node *tree,
+ly_type_store_union(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value, size_t value_len,
+        uint32_t options, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
         struct lyd_value *storage, struct ly_err_item **err)
 {
     LY_ERR ret = LY_SUCCESS;
-    LY_ARRAY_COUNT_TYPE u;
     struct lysc_type_union *type_u = (struct lysc_type_union *)type;
-    struct lyd_value_subvalue *subvalue;
-    char *errmsg = NULL;
-    uint32_t prev_lo;
+    struct lyd_value_subvalue *subvalue = NULL;
 
-    if (options & LY_TYPE_OPTS_SECOND_CALL) {
-        subvalue = storage->subvalue;
+    /* prepare subvalue storage */
+    subvalue = calloc(1, sizeof *subvalue);
+    if (!subvalue) {
+        *err = ly_err_new(LY_LLERR, LY_EMEM, 0, "Memory allocation failed.", NULL, NULL);
+        return LY_EMEM;
+    }
 
-        /* call the callback second time */
-        ret = subvalue->value->realtype->plugin->store(ctx, subvalue->value->realtype, subvalue->original,
-                                                       strlen(subvalue->original), options & ~LY_TYPE_OPTS_DYNAMIC,
-                                                       subvalue->format, subvalue->prefix_data, context_node, tree,
-                                                       subvalue->value, err);
-        if (ret == LY_SUCCESS) {
-            /* storing successful */
-            return LY_SUCCESS;
-        }
+    /* remember the original value */
+    if (options & LY_TYPE_OPTS_DYNAMIC) {
+        ret = lydict_insert_zc(ctx, (char *)value, &subvalue->original);
+        LY_CHECK_GOTO(ret, cleanup);
 
-        /* second call failed, we have to try another subtype of the union.
-         * Unfortunately, since the realtype can change (e.g. in leafref), we are not able to detect
-         * which of the subtype's were tried the last time, so we have to try all of them.
-         * We also have to remove the LY_TYPE_OPTS_SECOND_CALL flag since the callbacks will be now
-         * called for the first time.
-         * In the second call we should have all the data instances, so the LY_EINCOMPLETE should not
-         * happen again.
-         */
-        options = options & ~LY_TYPE_OPTS_SECOND_CALL;
-        ly_err_free(*err);
-        *err = NULL;
+        options &= ~LY_TYPE_OPTS_DYNAMIC;
     } else {
-        /* prepare subvalue storage */
-        subvalue = calloc(1, sizeof *subvalue);
-        if (!subvalue) {
-            *err = ly_err_new(LY_LLERR, LY_EMEM, 0, "Memory allocation failed.", NULL, NULL);
-            return LY_EMEM;
-        }
-        subvalue->value = calloc(1, sizeof *subvalue->value);
-        if (!subvalue->value) {
-            free(subvalue);
-            *err = ly_err_new(LY_LLERR, LY_EMEM, 0, "Memory allocation failed.", NULL, NULL);
-            return LY_EMEM;
-        }
-
-        /* remember the original value */
         ret = lydict_insert(ctx, value_len ? value : "", value_len, &subvalue->original);
-        if (ret) {
-            free(subvalue->value);
-            free(subvalue);
-            return ret;
-        }
-
-        /* store format-specific data for later prefix resolution */
-        subvalue->format = format;
-        subvalue->prefix_data = ly_type_union_store_prefix_data(ctx, value, value_len, format, prefix_data);
-
-        /* remember the hint options */
-        subvalue->parser_hint = options & LY_TYPE_PARSER_HINTS_MASK;
+        LY_CHECK_GOTO(ret, cleanup);
     }
 
-    /* use the first usable sybtype to store the value */
-    LY_ARRAY_FOR(type_u->types, u) {
-        if (type_check_parser_hint(format, subvalue->parser_hint, type_u->types[u]->basetype)) {
-            /* not a suitable type */
-            continue;
-        }
+    /* store format-specific data for later prefix resolution */
+    subvalue->format = format;
+    subvalue->prefix_data = ly_type_union_store_prefix_data(ctx, value, value_len, format, prefix_data);
+    subvalue->hints = hints;
+    subvalue->ctx_node = ctx_node;
 
-        /* turn logging off */
-        prev_lo = ly_log_options(0);
-        ret = type_u->types[u]->plugin->store(ctx, type_u->types[u], subvalue->original, strlen(subvalue->original),
-                                              options & ~LY_TYPE_OPTS_DYNAMIC, subvalue->format, subvalue->prefix_data,
-                                              context_node, tree, subvalue->value, err);
-        /* restore logging */
-        ly_log_options(prev_lo);
-        if (ret == LY_SUCCESS || ret == LY_EINCOMPLETE) {
-            /* success (or not yet complete) */
-            break;
-        }
-        ly_err_free(*err);
-        *err = NULL;
-    }
+    /* use the first usable subtype to store the value */
+    ret = ly_type_union_store_type(ctx, type_u->types, subvalue, 0, NULL, NULL, err);
+    LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
 
-    if (u == LY_ARRAY_COUNT(type_u->types)) {
-        if (asprintf(&errmsg, "Invalid union value \"%.*s\" - no matching subtype found.", (int)value_len, value) == -1) {
-            *err = ly_err_new(LY_LLERR, LY_EMEM, 0, "Memory allocation failed.", NULL, NULL);
-            ret = LY_EMEM;
-        } else {
-            *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_DATA, errmsg, NULL, NULL);
-            ret = LY_EVALID;
-        }
-        goto error;
-    }
-
-    ret = lydict_insert(ctx, subvalue->value->canonical, strlen(subvalue->value->canonical), &storage->canonical);
-    if (ret) {
-error:
-        /* free any stored information */
-        free(subvalue->value);
+cleanup:
+    if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
         lydict_remove(ctx, subvalue->original);
         ly_type_union_free_prefix_data(subvalue->format, subvalue->prefix_data);
         free(subvalue);
-        if (options & LY_TYPE_OPTS_SECOND_CALL) {
-            storage->subvalue = NULL;
-        }
-        return ret;
+    } else {
+        /* store it as union, the specific type is in the subvalue, but canonical value is the specific type value */
+        ret = lydict_insert(ctx, subvalue->value.canonical, 0, &storage->canonical);
+        LY_CHECK_GOTO(ret, cleanup);
+        storage->subvalue = subvalue;
+        storage->realtype = type;
+    }
+    return ret;
+}
+
+/**
+ * @brief Validate value of the YANG built-in union type.
+ *
+ * Implementation of the ly_type_validate_clb.
+ */
+static LY_ERR
+ly_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type, const struct lyd_node *ctx_node,
+        const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lysc_type_union *type_u = (struct lysc_type_union *)storage->realtype;
+    struct lyd_value_subvalue *subvalue = storage->subvalue;
+
+    *err = NULL;
+
+    if (!subvalue->value.realtype->plugin->validate) {
+        /* nothing to resolve */
+        return LY_SUCCESS;
     }
 
-    /* success */
-    storage->subvalue = subvalue;
-    storage->realtype = type;
-
-    if (options & LY_TYPE_OPTS_DYNAMIC) {
-        free((char *)value);
+    /* resolve the stored value */
+    if (!subvalue->value.realtype->plugin->validate(ctx, type, ctx_node, tree, &subvalue->value, err)) {
+        /* resolve successful */
+        return LY_SUCCESS;
     }
+
+    /* Resolve failed, we have to try another subtype of the union.
+     * Unfortunately, since the realtype can change (e.g. in leafref), we are not able to detect
+     * which of the subtype's were tried the last time, so we have to try all of them again.
+     */
+    ly_err_free(*err);
+    *err = NULL;
+
+    /* store and resolve the value */
+    ret = ly_type_union_store_type(ctx, type_u->types, subvalue, 1, ctx_node, tree, err);
+    LY_CHECK_RET(ret);
+
+    /* success, update the canonical value */
+    lydict_remove(ctx, storage->canonical);
+    LY_CHECK_RET(lydict_insert(ctx, subvalue->value.canonical, 0, &storage->canonical));
     return LY_SUCCESS;
 }
 
@@ -2191,10 +2240,10 @@
 static LY_ERR
 ly_type_compare_union(const struct lyd_value *val1, const struct lyd_value *val2)
 {
-    if (val1->subvalue->value->realtype != val2->subvalue->value->realtype) {
+    if (val1->subvalue->value.realtype != val2->subvalue->value.realtype) {
         return LY_ENOT;
     }
-    return val1->subvalue->value->realtype->plugin->compare(val1->subvalue->value, val2->subvalue->value);
+    return val1->subvalue->value.realtype->plugin->compare(&val1->subvalue->value, &val2->subvalue->value);
 }
 
 /**
@@ -2205,7 +2254,7 @@
 static const char *
 ly_type_print_union(const struct lyd_value *value, LY_PREFIX_FORMAT format, void *prefix_data, ly_bool *dynamic)
 {
-    return value->subvalue->value->realtype->plugin->print(value->subvalue->value, format, prefix_data, dynamic);
+    return value->subvalue->value.realtype->plugin->print(&value->subvalue->value, format, prefix_data, dynamic);
 }
 
 /**
@@ -2220,11 +2269,10 @@
 
     dup->subvalue = calloc(1, sizeof *dup->subvalue);
     LY_CHECK_ERR_RET(!dup->subvalue, LOGMEM(ctx), LY_EMEM);
-    dup->subvalue->value = calloc(1, sizeof *dup->subvalue->value);
-    LY_CHECK_ERR_RET(!dup->subvalue->value, LOGMEM(ctx), LY_EMEM);
-    LY_CHECK_RET(original->subvalue->value->realtype->plugin->duplicate(ctx, original->subvalue->value, dup->subvalue->value));
+    LY_CHECK_RET(original->subvalue->value.realtype->plugin->duplicate(ctx, &original->subvalue->value, &dup->subvalue->value));
 
-    LY_CHECK_RET(lydict_insert(ctx, original->subvalue->original, strlen(original->subvalue->original), &dup->subvalue->original));
+    LY_CHECK_RET(lydict_insert(ctx, original->subvalue->original, strlen(original->subvalue->original),
+            &dup->subvalue->original));
     dup->subvalue->format = original->subvalue->format;
     dup->subvalue->prefix_data = ly_type_union_dup_prefix_data(ctx, original->subvalue->format,
                                                                original->subvalue->prefix_data);
@@ -2243,11 +2291,10 @@
 {
     lydict_remove(ctx, value->canonical);
     if (value->subvalue) {
-        if (value->subvalue->value) {
-            value->subvalue->value->realtype->plugin->free(ctx, value->subvalue->value);
-            free(value->subvalue->value);
-            ly_type_union_free_prefix_data(value->subvalue->format, value->subvalue->prefix_data);
+        if (value->subvalue->value.realtype) {
+            value->subvalue->value.realtype->plugin->free(ctx, &value->subvalue->value);
         }
+        ly_type_union_free_prefix_data(value->subvalue->format, value->subvalue->prefix_data);
         lydict_remove(ctx, value->subvalue->original);
         free(value->subvalue);
         value->subvalue = NULL;
@@ -2259,61 +2306,61 @@
  */
 struct lysc_type_plugin ly_builtin_type_plugins[LY_DATA_TYPE_COUNT] = {
     {0}, /* LY_TYPE_UNKNOWN */
-    {.type = LY_TYPE_BINARY, .store = ly_type_store_binary, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_BINARY, .store = ly_type_store_binary, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - binary, version 1"},
-    {.type = LY_TYPE_UINT8, .store = ly_type_store_uint, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_UINT8, .store = ly_type_store_uint, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - unsigned integer, version 1"},
-    {.type = LY_TYPE_UINT16, .store = ly_type_store_uint, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_UINT16, .store = ly_type_store_uint, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - unsigned integer, version 1"},
-    {.type = LY_TYPE_UINT32, .store = ly_type_store_uint, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_UINT32, .store = ly_type_store_uint, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - unsigned integer, version 1"},
-    {.type = LY_TYPE_UINT64, .store = ly_type_store_uint, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_UINT64, .store = ly_type_store_uint, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - unsigned integer, version 1"},
-    {.type = LY_TYPE_STRING, .store = ly_type_store_string, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_STRING, .store = ly_type_store_string, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - string, version 1"},
-    {.type = LY_TYPE_BITS, .store = ly_type_store_bits, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_BITS, .store = ly_type_store_bits, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_bits, .free = ly_type_free_bits,
         .id = "libyang 2 - bits, version 1"},
-    {.type = LY_TYPE_BOOL, .store = ly_type_store_boolean, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_BOOL, .store = ly_type_store_boolean, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - boolean, version 1"},
-    {.type = LY_TYPE_DEC64, .store = ly_type_store_decimal64, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_DEC64, .store = ly_type_store_decimal64, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - decimal64, version 1"},
-    {.type = LY_TYPE_EMPTY, .store = ly_type_store_empty, .compare = ly_type_compare_empty,
+    {.type = LY_TYPE_EMPTY, .store = ly_type_store_empty, .validate = NULL, .compare = ly_type_compare_empty,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - empty, version 1"},
-    {.type = LY_TYPE_ENUM, .store = ly_type_store_enum, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_ENUM, .store = ly_type_store_enum, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - enumeration, version 1"},
-    {.type = LY_TYPE_IDENT, .store = ly_type_store_identityref, .compare = ly_type_compare_identityref,
+    {.type = LY_TYPE_IDENT, .store = ly_type_store_identityref, .validate = NULL, .compare = ly_type_compare_identityref,
         .print = ly_type_print_identityref, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - identityref, version 1"},
-    {.type = LY_TYPE_INST, .store = ly_type_store_instanceid, .compare = ly_type_compare_instanceid,
-        .print = ly_type_print_instanceid, .duplicate = ly_type_dup_instanceid, .free = ly_type_free_instanceid,
-        .id = "libyang 2 - instance-identifier, version 1"},
-    {.type = LY_TYPE_LEAFREF, .store = ly_type_store_leafref, .compare = ly_type_compare_leafref,
-        .print = ly_type_print_leafref, .duplicate = ly_type_dup_leafref, .free = ly_type_free_leafref,
-        .id = "libyang 2 - leafref, version 1"},
-    {.type = LY_TYPE_UNION, .store = ly_type_store_union, .compare = ly_type_compare_union,
+    {.type = LY_TYPE_INST, .store = ly_type_store_instanceid, .validate = ly_type_validate_instanceid,
+        .compare = ly_type_compare_instanceid, .print = ly_type_print_instanceid, .duplicate = ly_type_dup_instanceid,
+        .free = ly_type_free_instanceid, .id = "libyang 2 - instance-identifier, version 1"},
+    {.type = LY_TYPE_LEAFREF, .store = ly_type_store_leafref, .validate = ly_type_validate_leafref,
+        .compare = ly_type_compare_leafref, .print = ly_type_print_leafref, .duplicate = ly_type_dup_leafref,
+        .free = ly_type_free_leafref, .id = "libyang 2 - leafref, version 1"},
+    {.type = LY_TYPE_UNION, .store = ly_type_store_union, .validate = ly_type_validate_union, .compare = ly_type_compare_union,
         .print = ly_type_print_union, .duplicate = ly_type_dup_union, .free = ly_type_free_union,
         .id = "libyang 2 - union,version 1"},
-    {.type = LY_TYPE_INT8, .store = ly_type_store_int, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_INT8, .store = ly_type_store_int, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - integer, version 1"},
-    {.type = LY_TYPE_INT16, .store = ly_type_store_int, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_INT16, .store = ly_type_store_int, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - integer, version 1"},
-    {.type = LY_TYPE_INT32, .store = ly_type_store_int, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_INT32, .store = ly_type_store_int, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - integer, version 1"},
-    {.type = LY_TYPE_INT64, .store = ly_type_store_int, .compare = ly_type_compare_simple,
+    {.type = LY_TYPE_INT64, .store = ly_type_store_int, .validate = NULL, .compare = ly_type_compare_simple,
         .print = ly_type_print_simple, .duplicate = ly_type_dup_simple, .free = ly_type_free_simple,
         .id = "libyang 2 - integer, version 1"},
 };
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 34dec40..3385b4f 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -134,7 +134,8 @@
 const char *ly_get_prefix(const struct lys_module *mod, LY_PREFIX_FORMAT format, void *prefix_data);
 
 /**
- * @defgroup plugintypeopts Options for type plugin callbacks. The same set of the options is passed to all the type's callbacks used together.
+ * @defgroup plugintypeopts Options for type plugin callbacks. The same set of the options is passed to
+ * all the type's callbacks used together.
  *
  * Options applicable to ly_type_store_clb().
  * @{
@@ -144,70 +145,59 @@
                                             (e.g. in case of canonization).
                                             In any case, the caller of the callback does not free the provided string value after calling
                                             the type's callbacks with this option */
-#define LY_TYPE_OPTS_SCHEMA       0x02 /**< Flag for the value used in schema instead of the data tree. With this flag also the meaning of
-                                            LY_TYPE_OPTS_INCOMPLETE_DATA changes and means that the schema tree is not complete (data tree
-                                            is not taken into account at all). */
-#define LY_TYPE_OPTS_INCOMPLETE_DATA 0x04 /**< Flag for the case the data trees (schema trees in case it is used in combination with
-                                            LY_TYPE_OPTS_SCHEMA) are not yet complete. In this case the plugin should do what it
-                                            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  0x08 /**< 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 */
 
 /**
- * @brief Callback to validate, canonize and store (optionally, according to the given @p options) the given @p value
- * according to the given @p type.
+ * @brief Callback to store and canonize the given @p value according to the given @p type.
  *
- * Even when the callback returns #LY_EINCOMPLETE, the value must be normally stored in the structure
- * (meaning it can be printed/duplicated/compared). That basically means that the #LY_TYPE_OPTS_SECOND_CALL
- * should only validate the value but not change the internal value! The only exception is union, when this could
- * happen. However, even on the first call it is stored as a potentially matching value, which means the value
- * structure is valid. That is all that is required.
+ * Value must always be correctly stored meaning all the other type callbacks (such as print or compare)
+ * must function as expected.
  *
  * Note that the \p value string is not necessarily zero-terminated. The provided \p value_len is always correct.
  *
  * @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).
+ * @param[in] type Type of the value being stored.
+ * @param[in] value Lexical representation of the value to be stored.
  *            It is never NULL, empty string is represented as "" with zero @p value_len.
  * @param[in] value_len Length (number of bytes) of the given \p value.
  * @param[in] options [Type plugin options](@ref plugintypeopts).
- * @param[in] format Input format of the data.
+ * @param[in] format Input format of the value.
  * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
- * @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 a lys_node (in case LY_TYPE_OPTS_INCOMPLETE_DATA or LY_TYPE_OPTS_SCHEMA set in @p options)
- *            or lyd_node structure.
- * @param[in] tree External data tree (e.g. when validating RPC/Notification) where the required data instance can be placed.
- * @param[in] storage Storage for the value in the type's specific encoding. All the members should be filled by the plugin.
+ * @param[in] hints Bitmap of [value hints](@ref lydvalhints) of all the allowed value types.
+ * @param[in] ctx_node The @p value schema context node.
+ * @param[out] storage Storage for the value in the type's specific encoding. All the members should be filled by the plugin.
  * @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic
- *             error message is prepared instead.
- *             The error structure can be created by ly_err_new().
- * @return LY_SUCCESS on success
- * @return LY_EINCOMPLETE in case the option included LY_TYPE_OPTS_INCOMPLETE_DATA flag and the data @p trees are needed to finish the validation.
- * @return LY_ERR value if an error occurred and the value could not be canonized following the type's rules.
+ *             error message is prepared instead. The error structure can be created by ly_err_new().
+ * @return LY_SUCCESS on success,
+ * @return LY_EINCOMPLETE in case the ::ly_type_validate_clb should be called to finish value validation in data,
+ * @return LY_ERR value on error.
  */
-typedef LY_ERR (*ly_type_store_clb)(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len,
-        uint32_t options, LY_PREFIX_FORMAT format, void *prefix_data, const void *context_node,
-        const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err);
+typedef LY_ERR (*ly_type_store_clb)(const struct ly_ctx *ctx, const struct lysc_type *type, const char *value,
+        size_t value_len, uint32_t options, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints,
+        const struct lysc_node *ctx_node, struct lyd_value *storage, struct ly_err_item **err);
+
+/**
+ * @brief Callback to validate the stored value in data.
+ *
+ * This callback is optional for types that can only be validated in a data tree. It must be called and succeed
+ * in case the ::ly_type_store_clb callback returned ::LY_EINCOMPLETE for the value to be valid. However, this
+ * callback can be called even in other cases (such as separate/repeated validation).
+ *
+ * @param[in] ctx libyang Context
+ * @param[in] type Original type of the value (not necessarily the stored one) being validated.
+ * @param[in] ctx_node The value data context node for validation.
+ * @param[in] tree External data tree (e.g. when validating RPC/Notification) with possibly referenced data.
+ * @param[in,out] storage Storage of the value successfully filled by ::ly_type_store_clb. May be modified.
+ * @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic
+ *             error message is prepared instead. The error structure can be created by ly_err_new().
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value on error.
+ */
+typedef LY_ERR (*ly_type_validate_clb)(const struct ly_ctx *ctx, const struct lysc_type *type,
+        const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err);
 
 /**
  * @brief Callback for comparing 2 values of the same type.
- * Must be able to compare values that are not fully resolved! Meaning, whose storing callback returned
- * #LY_EINCOMPLETE and the was not called again.
  *
  * Caller is responsible to provide values of the SAME type.
  *
@@ -219,9 +209,7 @@
 typedef LY_ERR (*ly_type_compare_clb)(const struct lyd_value *val1, const struct lyd_value *val2);
 
 /**
- * @brief Callback to receive printed (canonical) value of the data stored in @p value.
- * Must be able to print values that are not fully resolved! Meaning, whose storing callback returned
- * #LY_EINCOMPLETE and the was not called again.
+ * @brief Callback to getting the canonical value of the data stored in @p value.
  *
  * @param[in] value Value to print.
  * @param[in] format Format in which the data are supposed to be printed.
@@ -237,15 +225,14 @@
         ly_bool *dynamic);
 
 /**
- * @brief Callback to duplicate data in data structure. Note that callback is even responsible for duplicating lyd_value::canonized.
- * Must be able to duplicate values that are not fully resolved! Meaning, whose storing callback returned
- * #LY_EINCOMPLETE and the was not called again.
+ * @brief Callback to duplicate data in data structure. Note that callback is even responsible for
+ * duplicating lyd_value::canonized.
  *
  * @param[in] ctx libyang context of the @p dup. Note that the context of @p original and @p dup might not be the same.
  * @param[in] original Original data structure to be duplicated.
  * @param[in,out] dup Prepared data structure to be filled with the duplicated data of @p original.
  * @return LY_SUCCESS after successful duplication.
- * @return other LY_ERR values on error.
+ * @return LY_ERR value on error.
  */
 typedef LY_ERR (*ly_type_dup_clb)(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
 
@@ -268,7 +255,8 @@
  */
 struct lysc_type_plugin {
     LY_DATA_TYPE type;               /**< implemented type, use LY_TYPE_UNKNOWN for derived data types */
-    ly_type_store_clb store;         /**< function to validate, canonize and store (according to the options) the value in the type-specific way */
+    ly_type_store_clb store;         /**< store and canonize the value in the type-specific way */
+    ly_type_validate_clb validate;   /**< optional, validate the value in the type-specific way in data */
     ly_type_compare_clb compare;     /**< comparison callback to compare 2 values of the same type */
     ly_type_print_clb print;         /**< printer callback to get string representing the value */
     ly_type_dup_clb duplicate;       /**< data duplication callback */
diff --git a/src/printer_json.c b/src/printer_json.c
index 2cf8cef..3e488de 100644
--- a/src/printer_json.c
+++ b/src/printer_json.c
@@ -401,9 +401,9 @@
         PRINT_COMMA;
         json_print_member2(ctx, (struct lyd_node *)node, attr->format, &attr->prefix, attr->name, 0);
 
-        if (attr->hint & (LYD_NODE_OPAQ_ISBOOLEAN | LYD_NODE_OPAQ_ISNUMBER)) {
+        if (attr->hints & (LYD_VALHINT_BOOLEAN | LYD_VALHINT_DECNUM)) {
             ly_print_(ctx->out, "%s", attr->value[0] ? attr->value : "null");
-        } else if (attr->hint & LYD_NODE_OPAQ_ISEMPTY) {
+        } else if (attr->hints & LYD_VALHINT_EMPTY) {
             ly_print_(ctx->out, "[null]");
         } else {
             json_print_string(ctx->out, attr->value);
@@ -754,7 +754,7 @@
 {
     ly_bool first = 1, last = 1;
 
-    if (node->hint & LYD_NODE_OPAQ_ISLIST) {
+    if (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) {
         const struct lyd_node_opaq *prev = (const struct lyd_node_opaq *)node->prev;
         const struct lyd_node_opaq *next = (const struct lyd_node_opaq *)node->next;
         if (prev->next && matching_node((const struct lyd_node *)prev, (const struct lyd_node *)node)) {
@@ -768,18 +768,18 @@
     if (first) {
         LY_CHECK_RET(json_print_member2(ctx, node->parent, node->format, &node->prefix, node->name, 0));
 
-        if (node->hint & LYD_NODE_OPAQ_ISLIST) {
+        if (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) {
             LY_CHECK_RET(json_print_array_open(ctx, (struct lyd_node *)node));
             LEVEL_INC;
         }
     }
-    if (node->child || (node->hint & LYD_NODE_OPAQ_ISLIST)) {
+    if (node->child || (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST))) {
         LY_CHECK_RET(json_print_inner(ctx, (struct lyd_node *)node));
         LEVEL_PRINTED;
     } else {
-        if (node->hint & LYD_VALUE_PARSE_ISEMPTY) {
+        if (node->hints & LYD_VALHINT_EMPTY) {
             ly_print_(ctx->out, "[null]");
-        } else if (node->hint & (LYD_VALUE_PARSE_ISBOOLEAN | LYD_VALUE_PARSE_ISNUMBER)) {
+        } else if (node->hints & (LYD_VALHINT_BOOLEAN | LYD_VALHINT_DECNUM)) {
             ly_print_(ctx->out, "%s", node->value);
         } else {
             /* string */
@@ -791,7 +791,7 @@
         json_print_attributes(ctx, (const struct lyd_node *)node, 0);
 
     }
-    if (last && (node->hint & LYD_NODE_OPAQ_ISLIST)) {
+    if (last && (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST))) {
         json_print_array_close(ctx);
         LEVEL_DEC;
         LEVEL_PRINTED;
diff --git a/src/printer_xml.c b/src/printer_xml.c
index 26fa401..eea1e76 100644
--- a/src/printer_xml.c
+++ b/src/printer_xml.c
@@ -105,7 +105,6 @@
 static const char *
 xml_print_ns_opaq(struct xmlpr_ctx *ctx, LYD_FORMAT format, const struct ly_prefix *prefix, uint32_t 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);
diff --git a/src/tree_data.c b/src/tree_data.c
index 6514520..a7db1bf 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -54,102 +54,60 @@
         struct lyd_node **match);
 
 LY_ERR
-lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, ly_bool *dynamic, ly_bool second,
-        uint32_t value_hint, LY_PREFIX_FORMAT format, void *prefix_data, const struct lyd_node *tree)
+lyd_value_store(const struct ly_ctx *ctx, struct lyd_value *val, const struct lysc_type *type, const char *value,
+        size_t value_len, ly_bool *dynamic, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints,
+        const struct lysc_node *ctx_node, ly_bool *incomplete, enum LY_VLOG_ELEM log_elem_type, const void *log_elem)
 {
-    LY_ERR ret = LY_SUCCESS;
+    LY_ERR ret;
     struct ly_err_item *err = NULL;
-    struct ly_ctx *ctx;
-    struct lysc_type *type;
-    uint32_t options = value_hint | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
-            (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
-    assert(node);
+    uint32_t options = (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0);
 
-    ctx = node->schema->module->ctx;
-
-    type = ((struct lysc_node_leaf *)node->schema)->type;
-    ret = type->plugin->store(ctx, type, value, value_len, options, format, prefix_data,
-                              tree ? (void *)node : (void *)node->schema, tree, &node->value, &err);
-    if (ret && (ret != LY_EINCOMPLETE)) {
-        if (err) {
-            /* node may not be connected yet so use the schema node */
-            if (!node->parent && lysc_data_parent(node->schema)) {
-                LOGVAL(ctx, LY_VLOG_LYSC, node->schema, err->vecode, err->msg);
-            } else {
-                LOGVAL(ctx, LY_VLOG_LYD, node, err->vecode, err->msg);
-            }
-            ly_err_free(err);
-        }
-        goto error;
-    } else if (dynamic) {
-        *dynamic = 0;
+    if (incomplete) {
+        *incomplete = 0;
     }
 
-error:
-    return ret;
-}
-
-/* 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, ly_bool *dynamic,
-        LY_PREFIX_FORMAT format, void *prefix_data)
-{
-    LY_ERR ret = LY_SUCCESS;
-    struct ly_err_item *err = NULL;
-    struct ly_ctx *ctx;
-    struct lysc_type *type;
-    uint32_t options = LY_TYPE_OPTS_INCOMPLETE_DATA | (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0);
-
-    assert(val && schema && (schema->nodetype & LYD_NODE_TERM));
-
-    ctx = schema->module->ctx;
-    type = ((struct lysc_node_leaf *)schema)->type;
-    ret = type->plugin->store(ctx, type, value, value_len, options, format, prefix_data, (void *)schema, NULL,
-                              val, &err);
+    ret = type->plugin->store(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node, val, &err);
     if (ret == LY_EINCOMPLETE) {
-        /* this is fine, we do not need it resolved */
-        ret = LY_SUCCESS;
-    } else if (ret && err) {
-        ly_err_print(err);
-        LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
-        ly_err_free(err);
-    }
-    if (!ret && dynamic) {
-        *dynamic = 0;
+        if (incomplete) {
+            *incomplete = 1;
+        }
+    } else if (ret) {
+        if (err) {
+            LOGVAL(ctx, log_elem_type, log_elem, err->vecode, err->msg);
+            ly_err_free(err);
+        } else {
+            LOGVAL(ctx, log_elem_type, log_elem, LYVE_OTHER, "Storing value \"%.*s\" failed.", (int)value_len, value);
+        }
+        return ret;
     }
 
-    return ret;
+    if (dynamic) {
+        *dynamic = 0;
+    }
+    return LY_SUCCESS;
 }
 
 LY_ERR
-lyd_value_parse_meta(const struct ly_ctx *ctx, struct lyd_meta *meta, const char *value, size_t value_len, ly_bool *dynamic,
-        ly_bool second, uint32_t value_hint, LY_PREFIX_FORMAT format, void *prefix_data, const struct lysc_node *ctx_snode,
-        const struct lyd_node *tree)
+lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc_type *type, struct lyd_value *val,
+        const struct lyd_node *ctx_node, const struct lyd_node *tree, enum LY_VLOG_ELEM log_elem_type, const void *log_elem)
 {
-    LY_ERR ret = LY_SUCCESS;
+    LY_ERR ret;
     struct ly_err_item *err = NULL;
-    struct lyext_metadata *ant;
-    uint32_t options = value_hint | (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));
+    assert(type->plugin->validate);
 
-    ant = meta->annotation->data;
-    ret = ant->type->plugin->store(ctx, ant->type, value, value_len, options, format, prefix_data,
-                                  tree ? (void *)meta->parent : (void *)ctx_snode, tree, &meta->value, &err);
-    if (ret && (ret != LY_EINCOMPLETE)) {
+    ret = type->plugin->validate(ctx, type, ctx_node, tree, val, &err);
+    if (ret) {
         if (err) {
-            ly_err_print(err);
-            LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
+            LOGVAL(ctx, log_elem_type, log_elem, err->vecode, err->msg);
             ly_err_free(err);
+        } else {
+            LOGVAL(ctx, log_elem_type, log_elem, LYVE_OTHER, "Resolving value \"%s\" failed.", val->canonical);
         }
-        goto error;
-    } else if (dynamic) {
-        *dynamic = 0;
+        return ret;
     }
 
-error:
-    return ret;
+    return LY_SUCCESS;
 }
 
 LY_ERR
@@ -169,9 +127,8 @@
     }
 
     type = ((struct lysc_node_leaf *)node)->type;
-    /* just validate, no storing of enything */
-    rc = type->plugin->store(ctx ? ctx : node->module->ctx, type, value, value_len, LY_TYPE_OPTS_INCOMPLETE_DATA,
-                             format, prefix_data, node, NULL, &storage, &err);
+    rc = type->plugin->store(ctx ? ctx : node->module->ctx, type, value, value_len, 0, format, prefix_data,
+            LYD_HINT_SCHEMA, node, &storage, &err);
     if (rc == LY_EINCOMPLETE) {
         /* actually success since we do not provide the context tree and call validation with
          * LY_TYPE_OPTS_INCOMPLETE_DATA */
@@ -199,29 +156,37 @@
 
 API LY_ERR
 lyd_value_validate(const struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
-        const struct lyd_node *tree, struct lysc_type **realtype)
+        const struct lyd_node *tree, const struct lysc_type **realtype)
 {
     LY_ERR rc;
     struct ly_err_item *err = NULL;
     struct lysc_type *type;
     struct lyd_value val = {0};
-    uint32_t options = (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+    ly_bool stored = 0;
 
     LY_CHECK_ARG_RET(ctx, node, value, LY_EINVAL);
 
     type = ((struct lysc_node_leaf *)node->schema)->type;
-    rc = type->plugin->store(ctx ? ctx : node->schema->module->ctx, type, value, value_len, options, LY_PREF_JSON, NULL,
-                             tree ? (void *)node : (void *)node->schema, tree, &val, &err);
+    /* store */
+    rc = type->plugin->store(ctx ? ctx : LYD_CTX(node), type, value, value_len, 0, LY_PREF_JSON, NULL,
+            LYD_HINT_DATA, node->schema, &val, &err);
     if (rc == LY_EINCOMPLETE) {
-        return rc;
-    } else if (rc) {
+        stored = 1;
+
+        /* resolve */
+        rc = type->plugin->validate(ctx ? ctx : LYD_CTX(node), type, (struct lyd_node *)node, tree, &val, &err);
+    }
+
+    if (rc) {
         if (err) {
             if (ctx) {
-                ly_err_print(err);
-                LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
+                LOGVAL(ctx, LY_VLOG_LYD, node, err->vecode, err->msg);
             }
             ly_err_free(err);
         }
+        if (stored) {
+            type->plugin->free(ctx ? ctx : LYD_CTX(node), &val);
+        }
         return rc;
     }
 
@@ -229,47 +194,32 @@
         *realtype = val.realtype;
     }
 
-    type->plugin->free(ctx ? ctx : node->schema->module->ctx, &val);
+    type->plugin->free(ctx ? ctx : LYD_CTX(node), &val);
     return LY_SUCCESS;
 }
 
 API LY_ERR
-lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t value_len, const struct lyd_node *tree)
+lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t value_len)
 {
-    LY_ERR ret = LY_SUCCESS, rc;
-    struct ly_err_item *err = NULL;
+    LY_ERR ret = LY_SUCCESS;
     struct ly_ctx *ctx;
     struct lysc_type *type;
-    struct lyd_value data = {0};
-    uint32_t options = (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+    struct lyd_value val = {0};
 
     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;
-    rc = type->plugin->store(ctx, type, value, value_len, options, LY_PREF_JSON, NULL, (struct lyd_node *)node, tree, &data,
-                             &err);
-    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;
-    }
 
-    /* compare data */
-    if (type->plugin->compare(&node->value, &data)) {
-        /* do not assign it directly from the compare callback to keep possible LY_EINCOMPLETE from validation */
-        ret = LY_ENOT;
-    }
+    /* store the value */
+    ret = lyd_value_store(ctx, &val, type, value, value_len, NULL, LY_PREF_JSON, NULL, LYD_HINT_DATA, node->schema,
+            NULL, LY_VLOG_LYSC, node->schema);
+    LY_CHECK_RET(ret);
 
-cleanup:
-    type->plugin->free(ctx, &data);
+    /* compare values */
+    ret = type->plugin->compare(&node->value, &val);
 
+    type->plugin->free(ctx, &val);
     return ret;
 }
 
@@ -400,19 +350,7 @@
             LY_CHECK_GOTO(ret, cleanup);
 
             /* finish incompletely validated terminal values/attributes and when conditions */
-            switch (format) {
-            case LYD_XML:
-                ret = lyd_validate_unres(tree, &lydctx->when_check, &lydctx->unres_node_type, &lydctx->unres_meta_type,
-                                         LY_PREF_XML, &((struct lyxml_ctx *)lydctx->data_ctx)->ns, NULL);
-                break;
-            case LYD_JSON:
-            case LYD_LYB:
-                ret = lyd_validate_unres(tree, &lydctx->when_check, &lydctx->unres_node_type, &lydctx->unres_meta_type,
-                                         LY_PREF_JSON, NULL, NULL);
-                break;
-            case LYD_UNKNOWN:
-                LOGINT_RET(ctx);
-            }
+            ret = lyd_validate_unres(tree, &lydctx->when_check, &lydctx->unres_node_type, &lydctx->unres_meta_type, NULL);
             LY_CHECK_GOTO(ret, cleanup);
 
             /* perform final validation that assumes the data tree is final */
@@ -571,8 +509,8 @@
 }
 
 LY_ERR
-lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, ly_bool *dynamic, uint32_t value_hint,
-        LY_PREFIX_FORMAT format, void *prefix_data, struct lyd_node **node)
+lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, ly_bool *dynamic,
+        LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints, ly_bool *incomplete, struct lyd_node **node)
 {
     LY_ERR ret;
     struct lyd_node_term *term;
@@ -586,11 +524,9 @@
     term->prev = (struct lyd_node *)term;
     term->flags = LYD_NEW;
 
-    ret = lyd_value_parse(term, value, value_len, dynamic, 0, value_hint, format, prefix_data, NULL);
-    if (ret && (ret != LY_EINCOMPLETE)) {
-        free(term);
-        return ret;
-    }
+    ret = lyd_value_store(schema->module->ctx, &term->value, ((struct lysc_node_leaf *)term->schema)->type, value,
+            value_len, dynamic, format, prefix_data, hints, schema, incomplete, LY_VLOG_LYSC, schema);
+    LY_CHECK_ERR_RET(ret, free(term), ret);
     lyd_hash((struct lyd_node *)term);
 
     *node = (struct lyd_node *)term;
@@ -731,13 +667,13 @@
 
 LY_ERR
 lyd_create_opaq(const struct ly_ctx *ctx, const char *name, size_t name_len, const char *value, size_t value_len,
-        ly_bool *dynamic, uint32_t value_hint, LYD_FORMAT format, struct ly_prefix *val_prefs, const char *prefix, size_t pref_len,
+        ly_bool *dynamic, LYD_FORMAT format, uint32_t hints, 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)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyd_node_opaq *opaq;
 
-    assert(ctx && name && name_len);
+    assert(ctx && name && name_len && format);
 
     if (!value_len) {
         value = "";
@@ -764,7 +700,7 @@
     } else {
         LY_CHECK_GOTO(ret = lydict_insert(ctx, value, value_len, &opaq->value), finish);
     }
-    opaq->hint = value_hint;
+    opaq->hints = hints;
     opaq->ctx = ctx;
 
 finish:
@@ -832,9 +768,9 @@
     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, 0, LY_PREF_JSON, NULL, &key);
-        LY_CHECK_GOTO(rc && (rc != LY_EINCOMPLETE), cleanup);
-        rc = LY_SUCCESS;
+        rc = lyd_create_term(key_s, key_val, key_val ? strlen(key_val) : 0, NULL, LY_PREF_JSON, NULL, LYD_HINT_DATA,
+                NULL, &key);
+        LY_CHECK_GOTO(rc, cleanup);
         lyd_insert_node(ret, NULL, key);
     }
 
@@ -909,8 +845,9 @@
     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, 0, LY_PREF_JSON, NULL, &ret);
-    LY_CHECK_RET(rc && (rc != LY_EINCOMPLETE), rc);
+    rc = lyd_create_term(schema, val_str, val_str ? strlen(val_str) : 0, NULL, LY_PREF_JSON, NULL, LYD_HINT_DATA, NULL,
+            &ret);
+    LY_CHECK_RET(rc);
     if (parent) {
         lyd_insert_node(parent, NULL, ret);
     }
@@ -1053,8 +990,8 @@
         val_str = "";
     }
 
-    LY_CHECK_RET(lyd_create_meta(parent, &ret, module, name, name_len, val_str, strlen(val_str), NULL, 0,
-                                 LY_PREF_JSON, NULL, parent->schema));
+    LY_CHECK_RET(lyd_create_meta(parent, &ret, module, name, name_len, val_str, strlen(val_str), NULL, LY_PREF_JSON,
+            NULL, LYD_HINT_DATA, NULL));
 
     if (meta) {
         *meta = ret;
@@ -1077,8 +1014,8 @@
         value = "";
     }
 
-    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));
+    LY_CHECK_RET(lyd_create_opaq(ctx, name, strlen(name), value, strlen(value), NULL, LYD_JSON, 0, NULL, NULL, 0,
+            module_name, strlen(module_name), &ret));
     if (parent) {
         lyd_insert_node(parent, NULL, ret);
     }
@@ -1114,7 +1051,7 @@
         val_str = "";
     }
 
-    LY_CHECK_RET(lyd_create_attr(parent, &ret, ctx, name, name_len, val_str, strlen(val_str), NULL, 0, LYD_JSON, NULL,
+    LY_CHECK_RET(lyd_create_attr(parent, &ret, ctx, name, name_len, val_str, strlen(val_str), NULL, LYD_JSON, 0, NULL,
                                  prefix, pref_len, module_name, module_name ? strlen(module_name) : 0));
 
     if (attr) {
@@ -1142,7 +1079,9 @@
     type = ((struct lysc_node_leaf *)term->schema)->type;
 
     /* parse the new value */
-    LY_CHECK_GOTO(ret = lyd_value_store(&val, term->schema, val_str, strlen(val_str), NULL, LY_PREF_JSON, NULL), cleanup);
+    ret = lyd_value_store(LYD_CTX(term), &val, type, val_str, strlen(val_str), NULL, LY_PREF_JSON, NULL, LYD_HINT_DATA,
+            term->schema, NULL, LY_VLOG_LYD, term);
+    LY_CHECK_GOTO(ret, cleanup);
 
     /* compare original and new value */
     if (type->plugin->compare(&t->value, &val)) {
@@ -1217,7 +1156,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, 0, LY_PREF_JSON, NULL, NULL), cleanup);
+            strlen(val_str), NULL, LY_PREF_JSON, NULL, LYD_HINT_DATA, NULL), cleanup);
 
     /* compare original and new value */
     if (lyd_compare_meta(meta, m2)) {
@@ -1295,7 +1234,9 @@
             r = lys_value_validate(NULL, schema, value, strlen(value));
         }
         if (!r) {
-            LY_CHECK_GOTO(ret = lyd_value_store(&pred->value, schema, value, strlen(value), NULL, LY_PREF_JSON, NULL), cleanup);
+            ret = lyd_value_store(ctx, &pred->value, ((struct lysc_node_leaflist *)schema)->type, value, strlen(value),
+                    NULL, LY_PREF_JSON, NULL, LYD_HINT_DATA, schema, NULL, LY_VLOG_LYSC, schema);
+            LY_CHECK_GOTO(ret, cleanup);
         } /* else we have opaq flag and the value is not valid, leavne no predicate and then create an opaque node */
     }
 
@@ -1337,8 +1278,8 @@
             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, 0,
-                                                        LYD_JSON, NULL, NULL, 0, schema->module->name, strlen(schema->module->name), &node),
+                    LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL, LYD_JSON,
+                            LYD_NODEHINT_LIST, NULL, NULL, 0, schema->module->name, strlen(schema->module->name), &node),
                                   cleanup);
                 } else {
                     assert(p[path_idx].pred_type == LY_PATH_PREDTYPE_LIST);
@@ -1356,9 +1297,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, 0,
-                                                    LYD_JSON, NULL, NULL, 0, schema->module->name, strlen(schema->module->name), &node),
-                              cleanup);
+                LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL, LYD_JSON,
+                        LYD_NODEHINT_LEAFLIST, 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);
@@ -1375,11 +1316,12 @@
                 r = lys_value_validate(NULL, schema, value, strlen(value));
             }
             if (!r) {
-                LY_CHECK_GOTO(ret = lyd_create_term(schema, value, strlen(value), NULL, 0, LY_PREF_JSON, NULL, &node), cleanup);
+                ret = lyd_create_term(schema, value, strlen(value), NULL, LY_PREF_JSON, NULL, LYD_HINT_DATA, NULL, &node);
+                LY_CHECK_GOTO(ret, cleanup);
             } else {
                 /* creating opaque leaf without value */
-                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,
+                LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL, LYD_JSON,
+                                                    0, NULL, NULL, 0, schema->module->name,
                                                     strlen(schema->module->name), &node), cleanup);
             }
             break;
@@ -2162,10 +2104,10 @@
 
 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, ly_bool *dynamic, uint32_t value_hint, LY_PREFIX_FORMAT format,
-        void *prefix_data, const struct lysc_node *ctx_snode)
+        size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, LY_PREFIX_FORMAT format,
+        void *prefix_data, uint32_t hints, ly_bool *incomplete)
 {
-    LY_ERR ret, rc;
+    LY_ERR rc;
     struct lysc_ext_instance *ant = NULL;
     struct lyd_meta *mt, *last;
     LY_ARRAY_COUNT_TYPE u;
@@ -2191,11 +2133,9 @@
     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, value_hint, format, prefix_data, ctx_snode, NULL);
-    if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
-        free(mt);
-        return ret;
-    }
+    rc = lyd_value_store(mod->ctx, &mt->value, ((struct lyext_metadata *)ant->data)->type, value, value_len, dynamic,
+            format, prefix_data, hints, parent ? parent->schema : NULL, incomplete, LY_VLOG_NONE, NULL);
+    LY_CHECK_ERR_RET(rc, free(mt), rc);
     rc = lydict_insert(mod->ctx, name, name_len, &mt->name);
     LY_CHECK_ERR_RET(rc, free(mt), rc);
 
@@ -2210,7 +2150,7 @@
     if (meta) {
         *meta = mt;
     }
-    return ret;
+    return LY_SUCCESS;
 }
 
 void
@@ -2240,15 +2180,15 @@
 }
 
 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, ly_bool *dynamic, uint32_t value_hint, LYD_FORMAT format,
+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, ly_bool *dynamic, LYD_FORMAT format, uint32_t hints,
         struct ly_prefix *val_prefs, const char *prefix, size_t prefix_len, const char *module_key, size_t module_key_len)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyd_attr *at, *last;
 
     assert(ctx && (parent || attr) && (!parent || !parent->schema));
-    assert(name && name_len);
+    assert(name && name_len && format);
 
     if (!value_len) {
         value = "";
@@ -2256,7 +2196,7 @@
 
     at = calloc(1, sizeof *at);
     LY_CHECK_ERR_RET(!at, LOGMEM(ctx); ly_free_val_prefs(ctx, val_prefs), LY_EMEM);
-    at->hint = value_hint;
+    at->hints = hints;
     at->format = format;
     at->val_prefs = val_prefs;
 
@@ -3356,8 +3296,8 @@
         /* 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, 0, LY_PREF_JSON, NULL, &target);
-            LY_CHECK_RET(rc && (rc != LY_EINCOMPLETE), rc);
+            rc = lyd_create_term(schema, key_or_value, val_len, NULL, LY_PREF_JSON, NULL, LYD_HINT_DATA, NULL, &target);
+            LY_CHECK_RET(rc);
         } else {
             /* target used attributes: schema, hash, child (all keys) */
             LY_CHECK_RET(lyd_create_list2(schema, key_or_value, val_len, &target));
diff --git a/src/tree_data.h b/src/tree_data.h
index 02e9252..6817d64 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -159,25 +159,6 @@
 /** @} */
 
 /**
- * @brief Special lyd_value structure for union.
- *
- * Represents 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 one of the union's types.
- * The lyd_value_subvalue:prefixes provides (possible) mappings from prefixes in the original value to YANG modules.
- * These prefixes are necessary to parse original value to the union's subtypes.
- */
-struct lyd_value_subvalue {
-    struct lyd_value *value;     /**< representation of the value according to the selected union's subtype
-                                      (stored as lyd_value::realpath here, in subvalue structure */
-    const char *original;        /**< Original value in the dictionary. */
-    LY_PREFIX_FORMAT format;     /**< Prefix format of the value. However, this information is also used to decide
-                                      whether a value is valid for the specific format or not on later validations
-                                      (instance-identifier in XML looks different than in JSON). */
-    void *prefix_data;           /**< Format-specific data for prefix resolution (see ::ly_resolve_prefix) */
-    uint32_t parser_hint;        /**< Hint options from the parser */
-};
-
-/**
  * @brief YANG data representation
  */
 struct lyd_value {
@@ -203,13 +184,13 @@
     };  /**< The union is just a list of shorthands to possible values stored by a type's plugin. libyang itself uses the lyd_value::realtype
              plugin's callbacks to work with the data. */
 
-    struct lysc_type *realtype;      /**< pointer to the real type of the data stored in the value structure. This type can differ from the type
-                                          in the schema node of the data node since the type's store plugin can use other types/plugins for
-                                          storing data. Speaking about built-in types, this is the case of leafref which stores data as its
-                                          target type. In contrast, union type also use its subtype's callbacks, but inside an internal data
-                                          lyd_value::subvalue structure, so here is the pointer to the union type.
-                                          In general, this type is used to get free callback for this lyd_value structure, so it must reflect
-                                          the type used to store data directly in the same lyd_value instance. */
+    const struct lysc_type *realtype; /**< pointer to the real type of the data stored in the value structure. This type can differ from the type
+                                           in the schema node of the data node since the type's store plugin can use other types/plugins for
+                                           storing data. Speaking about built-in types, this is the case of leafref which stores data as its
+                                           target type. In contrast, union type also use its subtype's callbacks, but inside an internal data
+                                           lyd_value::subvalue structure, so here is the pointer to the union type.
+                                           In general, this type is used to get free callback for this lyd_value structure, so it must reflect
+                                           the type used to store data directly in the same lyd_value instance. */
 };
 
 /**
@@ -221,6 +202,26 @@
 #define LYD_CANON_VALUE(node) ((struct lyd_node_term *)(node))->value.canonical
 
 /**
+ * @brief Special lyd_value structure for union.
+ *
+ * Represents 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 one of the union's types.
+ * The lyd_value_subvalue:prefixes provides (possible) mappings from prefixes in the original value to YANG modules.
+ * These prefixes are necessary to parse original value to the union's subtypes.
+ */
+struct lyd_value_subvalue {
+    struct lyd_value value;      /**< representation of the value according to the selected union's subtype
+                                      (stored as lyd_value::realtype here, in subvalue structure */
+    const char *original;        /**< Original value in the dictionary. */
+    LY_PREFIX_FORMAT format;     /**< Prefix format of the value. However, this information is also used to decide
+                                      whether a value is valid for the specific format or not on later validations
+                                      (instance-identifier in XML looks different than in JSON). */
+    void *prefix_data;           /**< Format-specific data for prefix resolution (see ::ly_resolve_prefix) */
+    uint32_t hints;              /**< [Value hints](@ref lydvalhints) from the parser */
+    const struct lysc_node *ctx_node;   /**< Context schema node. */
+};
+
+/**
  * @brief Metadata structure.
  *
  * The structure provides information about metadata of a data element. Such attributes must map to
@@ -264,7 +265,7 @@
     const char *value;
 
     LYD_FORMAT format;              /**< format of the prefixes, only LYD_XML and LYD_JSON values can appear at this place */
-    uint32_t hint;                  /**< additional information about from the data source, see the [hints list](@ref lydopaqhints) */
+    uint32_t hints;                 /**< additional information about from the data source, see the [hints list](@ref lydhints) */
     struct ly_prefix prefix;        /**< name prefix, it is stored because they are a real pain to generate properly */
 
 };
@@ -375,7 +376,7 @@
     void *priv;                      /**< private user data, not used by libyang */
 #endif
 
-    struct lyd_value value;            /**< node's value representation */
+    struct lyd_value value;          /**< node's value representation */
 };
 
 /**
@@ -394,7 +395,7 @@
                                           never NULL. If there is no sibling node, pointer points to the node
                                           itself. In case of the first node, this pointer points to the last
                                           node in the list. */
-    struct lyd_meta *meta;           /**< pointer to the list of attributes of this node */
+    struct lyd_meta *meta;           /**< pointer to the list of metadata of this node */
 
 #ifdef LY_ENABLED_LYD_PRIV
     void *priv;                      /**< private user data, not used by libyang */
@@ -411,21 +412,53 @@
 };
 
 /**
- * @defgroup lydopaqhints Opaq data node hints.
+ * @defgroup lydvalhints Value format hints. Any information about value types encoded in the format
+ * is hinted this way.
  *
- * 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_VALHINT_STRING     0x0001 /**< value is allowed to be a string */
+#define LYD_VALHINT_DECNUM     0x0002 /**< value is allowed to be a decimal number */
+#define LYD_VALHINT_OCTNUM     0x0004 /**< value is allowed to be an octal number */
+#define LYD_VALHINT_HEXNUM     0x0008 /**< value is allowed to be a hexadecimal number */
+#define LYD_VALHINT_NUM64      0x0010 /**< value is allowed to be an int64 or uint64 */
+#define LYD_VALHINT_BOOLEAN    0x0020 /**< value is allowed to be a boolean */
+#define LYD_VALHINT_EMPTY      0x0040 /**< value is allowed to be empty */
+/**
+ * @} lydvalhints
+ */
 
-#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. */
+/**
+ * @defgroup lydnodehints Node type format hints. Any information about node types encoded in the format
+ * is hinted this way.
+ *
+ * @{
+ */
+#define LYD_NODEHINT_LIST       0x0080 /**< node is allowed to be a list instance */
+#define LYD_NODEHINT_LEAFLIST   0x0100 /**< node is allowed to be a leaf-list instance */
+#define LYD_NODEHINT_ENVELOPE   0x8000 /**< only found in opaque node hints; node is a special protocol-dependent
+                                            RPC/Action/Notification envelope */
+/**
+ * @} lydnodehints
+ */
 
-/** @} lydopaqhints */
+/**
+ * @defgroup lydhints Value and node type format hints. Any information about value and node types encoded in the format
+ * is hinted this way. It combines [value hints](@ref lydvalhints) and [node hints](@ref lydnodehints).
+ *
+ * @{
+ */
+#define LYD_HINT_DATA       0x01F3 /**< special node/value hint to be used for generic data node/value (for cases when
+                                        there is no encoding or it does not provide any additional information about
+                                        a node/value type); do not combine with specific [value hints](@ref lydvalhints)
+                                        or [node hints](@ref lydnodehints). */
+#define LYD_HINT_SCHEMA     0x01FF /**< special node/value hint to be used for generic schema node/value(for cases when
+                                        there is no encoding or it does not provide any additional information about
+                                        a node/value type); do not combine with specific [value hints](@ref lydvalhints)
+                                        or [node hints](@ref lydnodehints). */
+/**
+ * @} lydhints
+ */
 
 /**
  * @brief Data node structure for unparsed (opaque) nodes.
@@ -437,7 +470,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 lyd_attr *attr;
+    struct lyd_attr *attr;          /**< pointer to the list of generic attributes of this node */
 
 #ifdef LY_ENABLED_LYD_PRIV
     void *priv;                     /**< private user data, not used by libyang */
@@ -449,7 +482,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 */
-    uint32_t hint;                  /**< additional information about from the data source, see the [hints list](@ref lydopaqhints) */
+    uint32_t hints;                 /**< additional information about from the data source, see the [hints list](@ref lydhints) */
     const struct ly_ctx *ctx;       /**< libyang context */
 };
 
@@ -926,25 +959,21 @@
  * @return LY_ERR value if an error occurred.
  */
 LY_ERR lyd_value_validate(const struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
-        const struct lyd_node *tree, struct lysc_type **realtype);
+        const struct lyd_node *tree, const struct lysc_type **realtype);
 
 /**
- * @brief Compare the node's value with the given string value. The string value is first validated according to the node's type.
+ * @brief Compare the node's value with the given string value. The string value is first validated according to
+ * the (current) 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. But it is expected to be in JSON format.
  * @param[in] value_len Length of the given @p value (mandatory).
- * @param[in] tree Data tree (e.g. when validating RPC/Notification) where the required data instance (leafref target,
- *            instance-identifier) can be placed. NULL in case the data tree is not yet complete,
- *            then LY_EINCOMPLETE can be returned.
- * @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_ENOT if the values do not match.
+ * @return LY_SUCCESS on success,
+ * @return LY_ENOT if the values do not match,
  * @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, const struct lyd_node *tree);
+LY_ERR lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t value_len);
 
 /**
  * @defgroup datacompareoptions Data compare options
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index d5bf168..d1f91b5 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -95,16 +95,17 @@
  * @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] format Input format of @p value.
  * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in] hints [Value hints](@ref lydvalhints) from the parser regarding the value type.
+ * @param[out] incomplete Whether the value needs to be resolved.
  * @param[out] node Created node.
  * @return LY_SUCCESS on success.
  * @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, ly_bool *dynamic, uint32_t value_hint,
-        LY_PREFIX_FORMAT format, void *prefix_data, struct lyd_node **node);
+LY_ERR lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, ly_bool *dynamic,
+        LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints, ly_bool *incomplete, struct lyd_node **node);
 
 /**
  * @brief Create a term (leaf/leaf-list) node from a parsed value by duplicating it.
@@ -170,8 +171,8 @@
  * @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] hints [Hints](@ref lydhints) from the parser regarding the node/value type.
  * @param[in] val_prefs Possible value prefixes, array is spent (even in case the function fails).
  * @param[in] prefix Element prefix.
  * @param[in] pref_len Length of @p prefix, must be set correctly.
@@ -182,7 +183,7 @@
  * @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,
-        ly_bool *dynamic, uint32_t value_hint, LYD_FORMAT format, struct ly_prefix *val_prefs, const char *prefix,
+        ly_bool *dynamic, LYD_FORMAT format, uint32_t hints, 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);
 
 /**
@@ -240,17 +241,17 @@
  * @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.
  * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
- * @param[in] ctx_snode Context node for value resolution in schema.
+ * @param[in] hints [Value hints](@ref lydvalhints) from the parser regarding the value type.
+ * @param[out] incomplete Whether the value needs to be resolved.
  * @return LY_SUCCESS on success.
  * @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_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, ly_bool *dynamic, uint32_t value_hint,
-        LY_PREFIX_FORMAT format, void *prefix_data, const struct lysc_node *ctx_snode);
+        size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, LY_PREFIX_FORMAT format,
+        void *prefix_data, uint32_t hints, ly_bool *incomplete);
 
 /**
  * @brief Insert an attribute (last) into a parent
@@ -271,81 +272,58 @@
  * @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] hints [Hints](@ref lydhints) from the parser regarding the node/value type.
  * @param[in] val_prefs Possible value prefixes, array is spent (even in case the function fails).
  * @param[in] prefix Attribute prefix.
  * @param[in] prefix_len Attribute prefix length.
  * @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.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value on error.
  */
 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, ly_bool *dynamic, uint32_t value_hint, LYD_FORMAT format,
+        size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, LYD_FORMAT format, uint32_t hints,
         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.
- *
- * @param[in] node Data node for the @p value.
- * @param[in] value String value to be parsed, must not be NULL.
- * @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] format Input format of @p value.
- * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
- * @param[in] tree Data tree (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.
- * @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, ly_bool *dynamic, ly_bool second,
-        uint32_t value_hint, LY_PREFIX_FORMAT format, void *prefix_data, 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,
-        ly_bool *dynamic, LY_PREFIX_FORMAT format, void *prefix_data);
-
-/**
- * @brief Validate, canonize and store the given @p value into the metadata according to the annotation type's rules.
+ * @brief Store and canonize the given @p value into @p val according to the schema node type rules.
  *
  * @param[in] ctx libyang context.
- * @param[in] meta Metadata for the @p value.
+ * @param[in,out] val Storage for the value.
+ * @param[in] type Type of the value.
  * @param[in] value String value to be parsed, must not be NULL.
  * @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] format Input format of the data.
+ * @param[in] format Input format of @p value.
  * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
- * @param[in] ctx_snode Context node for value resolution in schema.
- * @param[in] tree Data tree (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.
- * @return LY_ERR value if an error occurred.
+ * @param[in] hints [Value hints](@ref lydvalhints) from the parser.
+ * @param[in] ctx_node Context schema node.
+ * @param[out] incomplete Optional, set if the value also needs to be resolved.
+ * @param[in] log_elem_type Elem type for logging.
+ * @param[in] log_elem Elem for logging.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value on error.
  */
-LY_ERR lyd_value_parse_meta(const struct ly_ctx *ctx, struct lyd_meta *meta, const char *value, size_t value_len,
-        ly_bool *dynamic, ly_bool second, uint32_t value_hint, LY_PREFIX_FORMAT format, void *prefix_data,
-        const struct lysc_node *ctx_snode, const struct lyd_node *tree);
+LY_ERR lyd_value_store(const struct ly_ctx *ctx, struct lyd_value *val, const struct lysc_type *type, const char *value,
+        size_t value_len, ly_bool *dynamic, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints,
+        const struct lysc_node *ctx_node, ly_bool *incomplete, enum LY_VLOG_ELEM log_elem_type, const void *log_elem);
+
+/**
+ * @brief Validate previously incompletely stored value.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] type Schema type of the value (not the stored one, but the original one).
+ * @param[in,out] val Stored value to resolve.
+ * @param[in] ctx_node Context node for the resolution.
+ * @param[in] tree Data tree for the resolution.
+ * @param[in] log_elem_type Elem type for logging.
+ * @param[in] log_elem Elem for logging.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value on error.
+ */
+LY_ERR lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc_type *type, struct lyd_value *val,
+        const struct lyd_node *ctx_node, const struct lyd_node *tree, enum LY_VLOG_ELEM log_elem_type, const void *log_elem);
 
 /* 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,
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 2fcea59..b7d449e 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -3947,7 +3947,7 @@
         /* ignore default values of the key */
         if (key->dflt) {
             key->dflt->realtype->plugin->free(ctx->ctx, key->dflt);
-            lysc_type_free(ctx->ctx, key->dflt->realtype);
+            lysc_type_free(ctx->ctx, (struct lysc_type *)key->dflt->realtype);
             free(key->dflt);
             key->dflt = NULL;
         }
@@ -7806,21 +7806,29 @@
     LY_ERR ret;
     struct ly_err_item *err = NULL;
 
-    ret = type->plugin->store(ctx->ctx, type, dflt, strlen(dflt), LY_TYPE_OPTS_SCHEMA,
-                              LY_PREF_SCHEMA, (void *)dflt_mod, node, NULL, storage, &err);
-    if (err) {
-        ly_err_print(err);
+    ret = type->plugin->store(ctx->ctx, type, dflt, strlen(dflt), 0, LY_PREF_SCHEMA, (void *)dflt_mod, LYD_HINT_SCHEMA,
+                              node, storage, &err);
+    if (ret == LY_EINCOMPLETE) {
+        /* we have no data so we will not be resolving it */
+        ret = LY_SUCCESS;
+    }
+
+    if (ret) {
         ctx->path[0] = '\0';
         lysc_path(node, LYSC_PATH_LOG, ctx->path, LYSC_CTX_BUFSIZE);
-        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-               "Invalid default - value does not fit the type (%s).", err->msg);
-        ly_err_free(err);
+        if (err) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                "Invalid default - value does not fit the type (%s).", err->msg);
+            ly_err_free(err);
+        } else {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                "Invalid default - value does not fit the type.");
+        }
+        return ret;
     }
-    if (!ret) {
-        ++storage->realtype->refcount;
-        return LY_SUCCESS;
-    }
-    return ret;
+
+    ++((struct lysc_type *)storage->realtype)->refcount;
+    return LY_SUCCESS;
 }
 
 /**
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index 673c4d9..a43ab9b 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -718,7 +718,7 @@
     FREE_STRING(ctx, node->units);
     if (node->dflt) {
         node->dflt->realtype->plugin->free(ctx, node->dflt);
-        lysc_type_free(ctx, node->dflt->realtype);
+        lysc_type_free(ctx, (struct lysc_type *)node->dflt->realtype);
         free(node->dflt);
     }
 }
@@ -735,7 +735,7 @@
     FREE_STRING(ctx, node->units);
     LY_ARRAY_FOR(node->dflts, u) {
         node->dflts[u]->realtype->plugin->free(ctx, node->dflts[u]);
-        lysc_type_free(ctx, node->dflts[u]->realtype);
+        lysc_type_free(ctx, (struct lysc_type *)node->dflts[u]->realtype);
         free(node->dflts[u]);
     }
     LY_ARRAY_FREE(node->dflts);
diff --git a/src/validation.c b/src/validation.c
index daa340b..4fc7158 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -25,6 +25,7 @@
 #include "hash_table.h"
 #include "log.h"
 #include "parser_data.h"
+#include "plugins_exts_metadata.h"
 #include "plugins_types.h"
 #include "set.h"
 #include "tree.h"
@@ -111,7 +112,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,
-        LY_PREFIX_FORMAT format, void *prefix_data, struct lyd_node **diff)
+        struct lyd_node **diff)
 {
     LY_ERR ret = LY_SUCCESS;
     uint32_t i;
@@ -170,10 +171,11 @@
             --i;
 
             struct lyd_node_term *node = (struct lyd_node_term *)node_types->objs[i];
+            struct lysc_type *type = ((struct lysc_node_leaf *)node->schema)->type;
 
-            /* validate and store the value of the node */
-            ret = lyd_value_parse(node, node->value.canonical, strlen(node->value.canonical), 0, 1, 0, format,
-                                  prefix_data, *tree);
+            /* resolve the value of the node */
+            ret = lyd_value_validate_incomplete(LYD_CTX(node), type, &node->value, (struct lyd_node *)node, *tree,
+                    LY_VLOG_LYD, node);
             LY_CHECK_RET(ret);
 
             /* remove this node from the set */
@@ -188,10 +190,11 @@
             --i;
 
             struct lyd_meta *meta = (struct lyd_meta *)meta_types->objs[i];
+            struct lysc_type *type = ((struct lyext_metadata *)meta->annotation->data)->type;
 
             /* validate and store the value of the metadata */
-            ret = lyd_value_parse_meta(meta->parent->schema->module->ctx, meta, meta->value.canonical,
-                                       strlen(meta->value.canonical), 0, 1, 0, format, prefix_data, NULL, *tree);
+            ret = lyd_value_validate_incomplete(LYD_CTX(meta->parent), type, &meta->value, meta->parent, *tree,
+                    LY_VLOG_NONE, NULL);
             LY_CHECK_RET(ret);
 
             /* remove this attr from the set */
@@ -1037,11 +1040,13 @@
         /* skip added default nodes */
         if ((node->flags & (LYD_DEFAULT | LYD_NEW)) != (LYD_DEFAULT | LYD_NEW)) {
             LY_LIST_FOR(node->meta, meta) {
-                /* metadata type resolution */
-                LY_CHECK_RET(ly_set_add(type_meta_check, (void *)meta, LY_SET_OPT_USEASLIST, NULL));
+                if (((struct lyext_metadata *)meta->annotation->data)->type->plugin->validate) {
+                    /* metadata type resolution */
+                    LY_CHECK_RET(ly_set_add(type_meta_check, (void *)meta, LY_SET_OPT_USEASLIST, NULL));
+                }
             }
 
-            if (node->schema->nodetype & LYD_NODE_TERM) {
+            if ((node->schema->nodetype & LYD_NODE_TERM) && ((struct lysc_node_leaf *)node->schema)->type->plugin->validate) {
                 /* node type resolution */
                 LY_CHECK_RET(ly_set_add(type_check, (void *)node, LY_SET_OPT_USEASLIST, NULL));
             } else if (node->schema->nodetype & LYD_NODE_INNER) {
@@ -1125,7 +1130,7 @@
         }
 
         /* finish incompletely validated terminal values/attributes and when conditions */
-        ret = lyd_validate_unres(tree, &when_check, &type_check, &type_meta_check, LY_PREF_JSON, NULL, diff);
+        ret = lyd_validate_unres(tree, &when_check, &type_check, &type_meta_check, diff);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* perform final validation that assumes the data tree is final */
@@ -1256,7 +1261,7 @@
 
     /* finish incompletely validated terminal values/attributes and when conditions on the full tree */
     LY_CHECK_GOTO(ret = lyd_validate_unres((struct lyd_node **)&tree, &when_check, &type_check, &type_meta_check,
-                                           LY_PREF_JSON, NULL, diff), cleanup);
+            diff), cleanup);
 
     /* perform final validation of the operation/notification */
     lyd_validate_obsolete(op_node);
diff --git a/src/validation.h b/src/validation.h
index 1a1a00a..3e2c5cd 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -41,13 +41,11 @@
  * @param[in] node_when Set with nodes with "when" conditions, can be NULL.
  * @param[in] node_types Set with nodes with unresolved types, can be NULL
  * @param[in] meta_types Set with metdata with unresolved types, can be NULL.
- * @param[in] format Format of the unresolved data.
- * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
  * @param[in,out] diff Validation diff.
  * @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,
-        LY_PREFIX_FORMAT format, void *prefix_data, struct lyd_node **diff);
+        struct lyd_node **diff);
 
 /**
  * @brief Validate new siblings. Specifically, check duplicated instances, autodelete default values and cases.
diff --git a/src/xpath.c b/src/xpath.c
index a1376b4..56cfa2a 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -1515,7 +1515,7 @@
 static LY_ERR
 set_comp_canonize(struct lyxp_set *trg, const struct lyxp_set *src, const struct lyxp_set_node *xp_node)
 {
-    struct lysc_type *type = NULL;
+    const struct lysc_type *type = NULL;
     struct lyd_value val;
     struct ly_err_item *err = NULL;
     char *str, *ptr;
@@ -1547,8 +1547,8 @@
     }
 
     /* ignore errors, the value may not satisfy schema constraints */
-    rc = type->plugin->store(src->ctx, type, str, strlen(str), LY_TYPE_OPTS_INCOMPLETE_DATA | LY_TYPE_OPTS_DYNAMIC,
-                             LY_PREF_JSON, NULL, NULL, NULL, &val, &err);
+    rc = type->plugin->store(src->ctx, type, str, strlen(str), LY_TYPE_OPTS_DYNAMIC, LY_PREF_JSON, NULL, LYD_HINT_DATA,
+            xp_node->node->schema, &val, &err);
     ly_err_free(err);
     if (rc) {
         /* invalid value */
@@ -3333,8 +3333,8 @@
 
         type = ((struct lysc_node_leaf *)scnode)->type;
         if (type->basetype != LY_TYPE_IDENT) {
-            rc = type->plugin->store(set->ctx, type, value, strlen(value), LY_TYPE_OPTS_SCHEMA, LY_PREF_SCHEMA,
-                            (void *)set->local_mod, NULL, NULL, &storage, &err);
+            rc = type->plugin->store(set->ctx, type, value, strlen(value), 0, LY_PREF_SCHEMA, (void *)set->local_mod,
+                    LYD_HINT_DATA, scnode, &storage, &err);
 
             if (err) {
                 LOGWRN(set->ctx, "Invalid value \"%s\" which does not fit the type (%s).", value, err->msg);
@@ -3807,8 +3807,7 @@
 
             /* store args[1] as ident */
             rc = val->realtype->plugin->store(set->ctx, val->realtype, args[1]->val.str, strlen(args[1]->val.str),
-                                              0, set->format, (void *)set->local_mod,
-                            (struct lyd_node *)leaf, set->tree, &data, &err);
+                    0, set->format, (void *)set->local_mod, LYD_HINT_DATA, leaf->schema, &data, &err);
         } else {
             meta = args[0]->val.meta[i].meta;
             val = &meta->value;
@@ -3818,9 +3817,8 @@
             }
 
             /* store args[1] as ident */
-            rc = val->realtype->plugin->store(set->ctx, val->realtype, args[1]->val.str, strlen(args[1]->val.str),
-                                              0, set->format, (void *)meta->annotation->module,
-                                              meta->parent, set->tree, &data, &err);
+            rc = val->realtype->plugin->store(set->ctx, val->realtype, args[1]->val.str, strlen(args[1]->val.str), 0,
+                    set->format, (void *)meta->annotation->module, LYD_HINT_DATA, meta->parent->schema, &data, &err);
         }
 
         if (err) {