validation NEW duplicate instances

Also trees structure removed, some
refactoring included.
diff --git a/src/common.h b/src/common.h
index 99f5ec6..3be741d 100644
--- a/src/common.h
+++ b/src/common.h
@@ -233,6 +233,8 @@
 #define LY_VCODE_NOMIN          LYVE_DATA, "Too few \"%s\" instances."
 #define LY_VCODE_NOMAX          LYVE_DATA, "Too many \"%s\" instances."
 #define LY_VCODE_NOUNIQ         LYVE_DATA, "Unique data leaf(s) \"%s\" not satisfied in \"%s\" and \"%s\"."
+#define LY_VCODE_DUP            LYVE_DATA, "Duplicate instance of \"%s\"."
+#define LY_VCODE_DUPCASE        LYVE_DATA, "Data for both cases \"%s\" and \"%s\" exist."
 
 /******************************************************************************
  * Context
diff --git a/src/parser_xml.c b/src/parser_xml.c
index b23b5bf..be516d4 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -450,7 +450,7 @@
     }
     ly_set_erase(&attrs_data, free);
     if (ret && *node) {
-        lyd_free_withsiblings(*node);
+        lyd_free_siblings(*node);
         *node = NULL;
     }
     return ret;
@@ -460,7 +460,6 @@
 lyd_parse_xml(struct ly_ctx *ctx, const char *data, int options, struct lyd_node **result)
 {
     LY_ERR ret = LY_SUCCESS;
-    const struct lyd_node **result_trees = NULL;
     struct lyd_xml_ctx xmlctx = {0};
 
     xmlctx.options = options;
@@ -478,19 +477,14 @@
         ret = lyd_validate_defaults_top(result, NULL, 0, ctx, &xmlctx.incomplete_type_validation, &xmlctx.when_check, options);
         LY_CHECK_GOTO(ret, cleanup);
 
-        /* prepare sized array for validator */
-        if (*result) {
-            result_trees = lyd_trees_new(1, *result);
-        }
-
         /* finish incompletely validated terminal values/attributes and when conditions */
         ret = lyd_validate_unres(&xmlctx.incomplete_type_validation, &xmlctx.incomplete_type_validation_attrs,
-                                 &xmlctx.when_check, LYD_XML, lydxml_resolve_prefix, ctx, result_trees);
+                                 &xmlctx.when_check, LYD_XML, lydxml_resolve_prefix, ctx, *result);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* context node and other validation tasks that depend on other data nodes */
-        ret = lyd_validate_data(result_trees, NULL, 0, ctx, options);
-        LY_CHECK_GOTO(result, cleanup);
+        ret = lyd_validate_data(result, NULL, 0, ctx, options);
+        LY_CHECK_GOTO(ret, cleanup);
     }
 
 cleanup:
@@ -502,7 +496,6 @@
     ly_set_erase(&xmlctx.incomplete_type_validation_attrs, NULL);
     ly_set_erase(&xmlctx.when_check, NULL);
     lyxml_context_clear((struct lyxml_context *)&xmlctx);
-    lyd_trees_free(result_trees, 0);
     if (ret) {
         lyd_free_all(*result);
         *result = NULL;
diff --git a/src/plugins_types.c b/src/plugins_types.c
index bf0dd16..f05b5c2 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -484,7 +484,7 @@
 static LY_ERR
 ly_type_store_int(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                   ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
-                  const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+                  const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                   struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
     LY_ERR ret;
@@ -560,7 +560,7 @@
 static LY_ERR
 ly_type_store_uint(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                    ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
-                   const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+                   const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                    struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
     LY_ERR ret;
@@ -635,7 +635,7 @@
 static LY_ERR
 ly_type_store_decimal64(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                         ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
-                        const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+                        const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                         struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
     int64_t d;
@@ -719,7 +719,7 @@
 static LY_ERR
 ly_type_store_binary(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                      ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
-                     const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+                     const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                      struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
     size_t start = 0, stop = 0, count = 0, u, termination = 0;
@@ -824,7 +824,7 @@
 static LY_ERR
 ly_type_store_string(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                      ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
-                     const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+                     const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                      struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
     struct lysc_type_str *type_str = (struct lysc_type_str *)type;
@@ -868,7 +868,7 @@
 static LY_ERR
 ly_type_store_bits(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                    ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
-                   const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+                   const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                    struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
     LY_ERR ret = LY_EVALID;
@@ -1053,7 +1053,7 @@
 static LY_ERR
 ly_type_store_enum(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                    ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
-                   const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+                   const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                    struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
     unsigned int u, v;
@@ -1129,7 +1129,7 @@
 static LY_ERR
 ly_type_store_boolean(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
                       ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
-                      const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+                      const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                       struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
     int8_t i;
@@ -1185,7 +1185,7 @@
 static LY_ERR
 ly_type_store_empty(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
                     ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
-                    const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+                    const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                     struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
     if (options & LY_TYPE_OPTS_SECOND_CALL) {
@@ -1246,7 +1246,7 @@
 static LY_ERR
 ly_type_store_identityref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                           ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT UNUSED(format),
-                          const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+                          const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                           struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
     struct lysc_type_identityref *type_ident = (struct lysc_type_identityref *)type;
@@ -1402,7 +1402,7 @@
 ly_type_store_instanceid_checknodeid(const char *orig, size_t orig_len, int options, int require_instance,
                                      const char **token, struct lyd_value_prefix *prefixes, LYD_FORMAT format,
                                      const struct lysc_node **node_s, const struct lyd_node **node_d,
-                                     const struct lyd_node **trees, char **errmsg)
+                                     const struct lyd_node *tree, char **errmsg)
 {
     const char *id, *prefix;
     size_t id_len, prefix_len;
@@ -1463,12 +1463,7 @@
             }
         } else {
             /* top-level node */
-            LY_ARRAY_FOR(trees, u) {
-                lyd_find_sibling_next(trees[u], mod, id, id_len, NULL, 0, (struct lyd_node **)node_d);
-                if (*node_d) {
-                    break;
-                }
-            }
+            lyd_find_sibling_next(tree, mod, id, id_len, NULL, 0, (struct lyd_node **)node_d);
             if (!(*node_d)) {
                 /* node not found */
                 asprintf(errmsg, "Invalid instance-identifier \"%.*s\" value - path \"%.*s\" does not exists in the data tree(s).",
@@ -1588,7 +1583,7 @@
 static LY_ERR
 ly_type_store_instanceid(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                          ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
-                         const void *UNUSED(context_node), const struct lyd_node **trees,
+                         const void *UNUSED(context_node), const struct lyd_node *tree,
                          struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
     LY_ERR ret = LY_EVALID;
@@ -1620,7 +1615,7 @@
 
     if (!(options & LY_TYPE_OPTS_SCHEMA) && (options & LY_TYPE_OPTS_SECOND_CALL) && (options & LY_TYPE_OPTS_STORE)) {
         /* the second run in data tree, the first one ended with LY_EINCOMPLETE, but we have prepared the target structure */
-        if (!lyd_target(storage->target, trees)) {
+        if (!lyd_target(storage->target, tree)) {
             /* 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. */
             int dynamic = 0;
@@ -1663,7 +1658,7 @@
 
             token++;
             if (ly_type_store_instanceid_checknodeid(value, value_len, options, type_inst->require_instance,
-                                                     &token, prefixes, format, &node_s, &node_d, trees, &errmsg)) {
+                                                     &token, prefixes, format, &node_s, &node_d, tree, &errmsg)) {
                 goto error;
             }
 
@@ -1724,7 +1719,7 @@
 
                 /* resolve the key in predicate */
                 if (ly_type_store_instanceid_checknodeid(value, value_len, options, type_inst->require_instance,
-                                                            &t, prefixes, format, &key_s, &key_d, trees, &errmsg)) {
+                                                            &t, prefixes, format, &key_s, &key_d, tree, &errmsg)) {
                     goto error;
                 }
                 if (key_d) {
@@ -1740,7 +1735,7 @@
 
                 if (node_d) {
                     while (node_d) {
-                        if (!lyd_value_compare((const struct lyd_node_term*)key_d, val, val_len, resolve_prefix, parser, format, trees)) {
+                        if (!lyd_value_compare((const struct lyd_node_term*)key_d, val, val_len, resolve_prefix, parser, format, tree)) {
                             /* match */
                             break;
                         }
@@ -1818,7 +1813,7 @@
 
                 if (key_d) {
                     while (key_d) {
-                        if (!lyd_value_compare((const struct lyd_node_term*)key_d, val, val_len, resolve_prefix, parser, format, trees)) {
+                        if (!lyd_value_compare((const struct lyd_node_term*)key_d, val, val_len, resolve_prefix, parser, format, tree)) {
                             /* match */
                             break;
                         }
@@ -2167,12 +2162,11 @@
  */
 const struct lyd_node *
 ly_type_find_leafref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len,
-                     const struct lyd_node *context_node, const struct lyd_node **trees, struct lyd_value *storage, char **errmsg)
+                     const struct lyd_node *context_node, const struct lyd_node *tree, struct lyd_value *storage, char **errmsg)
 {
     struct lysc_type_leafref *type_lr = (struct lysc_type_leafref*)type;
     const char *first_pred = NULL;
     const struct lyd_node *start_search;
-    unsigned int u = 0;
     const char *prefix, *id;
     size_t prefix_len, id_len;
     const struct lys_module *mod_node = NULL;
@@ -2219,14 +2213,9 @@
                 }
             } else {
                 /* top-level node */
-                LY_ARRAY_FOR(trees, u) {
-                    start_search = trees[u];
+                start_search = tree;
 next_instance_toplevel:
-                    lyd_find_sibling_next(start_search, mod_node, id, id_len, NULL, 0, (struct lyd_node **)&node);
-                    if (node) {
-                        break;
-                    }
-                }
+                lyd_find_sibling_next(start_search, mod_node, id, id_len, NULL, 0, (struct lyd_node **)&node);
             }
             if (!node) {
                 /* node not found */
@@ -2299,12 +2288,7 @@
 
                 if (!value) {
                     /* top-level search */
-                    LY_ARRAY_FOR(trees, u) {
-                        lyd_find_sibling_next(trees[u], mod_pred, src, src_len, NULL, 0, (struct lyd_node **)&value);
-                        if (value) {
-                            break;
-                        }
-                    }
+                    lyd_find_sibling_next(tree, mod_pred, src, src_len, NULL, 0, (struct lyd_node **)&value);
                 } else {
                     /* inner node */
                     lyd_find_sibling_next(lyd_node_children(value), mod_pred, src, src_len, NULL, 0, (struct lyd_node **)&value);
@@ -2369,7 +2353,7 @@
 static LY_ERR
 ly_type_store_leafref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                       ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
-                      const void *context_node, const struct lyd_node **trees,
+                      const void *context_node, const struct lyd_node *tree,
                       struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
     LY_ERR ret;
@@ -2388,7 +2372,7 @@
 
     /* check value according to the real type of the leafref target */
     ret = type_lr->realtype->plugin->store(ctx, type_lr->realtype, value, value_len, options,
-                                           resolve_prefix, parser, format, context_node, trees,
+                                           resolve_prefix, parser, format, context_node, tree,
                                            storage, canonized, err);
     if (ret != LY_SUCCESS && ret != LY_EINCOMPLETE) {
         return ret;
@@ -2400,7 +2384,7 @@
         }
 
         /* find corresponding data instance */
-        if (!ly_type_find_leafref(ctx, type, value, value_len, (const struct lyd_node *)context_node, trees, storage, &errmsg)) {
+        if (!ly_type_find_leafref(ctx, type, value, value_len, (const struct lyd_node *)context_node, tree, storage, &errmsg)) {
             goto error;
         }
     }
@@ -2474,7 +2458,7 @@
 static LY_ERR
 ly_type_store_union(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                     ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
-                    const void *context_node, const struct lyd_node **trees,
+                    const void *context_node, const struct lyd_node *tree,
                     struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
 {
     LY_ERR ret;
@@ -2490,7 +2474,7 @@
         ret = subvalue->value->realtype->plugin->store(ctx, subvalue->value->realtype, value, value_len,
                                                        options & ~(LY_TYPE_OPTS_CANONIZE | LY_TYPE_OPTS_DYNAMIC),
                                                        ly_type_stored_prefixes_clb, subvalue->prefixes, format,
-                                                       context_node, trees, subvalue->value, canonized, err);
+                                                       context_node, tree, subvalue->value, canonized, err);
         if (ret) {
             /* 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
@@ -2519,7 +2503,7 @@
             subvalue->value->realtype = type_u->types[u];
             ret = type_u->types[u]->plugin->store(ctx, type_u->types[u], value, value_len, options & ~LY_TYPE_OPTS_DYNAMIC,
                                                   ly_type_stored_prefixes_clb, subvalue->prefixes, format,
-                                                  context_node, trees, subvalue->value, canonized, err);
+                                                  context_node, tree, subvalue->value, canonized, err);
             if (ret == LY_SUCCESS || ret == LY_EINCOMPLETE) {
                 /* success (or not yet complete) */
                 break;
diff --git a/src/plugins_types.h b/src/plugins_types.h
index bdff1f7..2987382 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -145,8 +145,7 @@
  * @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] trees ([Sized array](@ref sizedarrays)) of external data trees (e.g. when validating RPC/Notification)
- *            where the required data instance can be placed.
+ * @param[in] tree External data tree (e.g. when validating RPC/Notification) where the required data instance can be placed.
  * @param[in,out] storage If LY_TYPE_OPTS_STORE option set, the parsed data are stored into this structure in the type's specific way.
  *             If the @p canonized differs from the storage's canonized member, the canonized value is also stored here despite the
  *             LY_TYPE_OPTS_CANONIZE option.
@@ -161,7 +160,7 @@
  */
 typedef LY_ERR (*ly_type_store_clb)(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                                     ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
-                                    const void *context_node, const struct lyd_node **trees,
+                                    const void *context_node, const struct lyd_node *tree,
                                     struct lyd_value *storage, const char **canonized, struct ly_err_item **err);
 
 /**
@@ -335,7 +334,7 @@
  *            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] context_node The @p value's node for the case that the require-instance restriction is supposed to be resolved.
- * @param[in] trees ([Sized array](@ref sizedarrays)) of external data trees (e.g. when validating RPC/Notification) where the required data
+ * @param[in] tree External data tree (e.g. when validating RPC/Notification) where the required data
  *            instance can be placed.
  *
  * @param[in] storage Parsed @p value.
@@ -343,7 +342,7 @@
  * @return Leafref target node or NULL on error when @p errmsg is always set.
  */
 const struct lyd_node *ly_type_find_leafref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len,
-                                            const struct lyd_node *context_node, const struct lyd_node **trees,
+                                            const struct lyd_node *context_node, const struct lyd_node *tree,
                                             struct lyd_value *storage, char **errmsg);
 
 /**
diff --git a/src/tree_data.c b/src/tree_data.c
index 78529f5..bf3402c 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -42,89 +42,16 @@
     size_t key_count;
 };
 
-API void
-lyd_trees_free(const struct lyd_node **trees, int free_data)
-{
-    if (!trees) {
-        return;
-    }
-
-    if (free_data) {
-        unsigned int u;
-        LY_ARRAY_FOR(trees, u) {
-            lyd_free_all((struct lyd_node *)trees[u]);
-        }
-    }
-    LY_ARRAY_FREE(trees);
-}
-
-static const struct lyd_node *
-lyd_trees_getstart(const struct lyd_node *tree)
-{
-    if (!tree) {
-        return NULL;
-    }
-    while (tree->prev->next) {
-        tree = tree->prev;
-    }
-    return tree;
-}
-
-API const struct lyd_node **
-lyd_trees_new(size_t count, const struct lyd_node *tree, ...)
-{
-    LY_ERR ret;
-    const struct lyd_node **trees = NULL;
-    va_list ap;
-
-    LY_CHECK_ARG_RET(NULL, tree, count > 0, NULL);
-
-    va_start(ap, tree);
-
-    LY_ARRAY_CREATE_GOTO(tree->schema->module->ctx, trees, count, ret, error);
-    /* first, mandatory, tree to insert */
-    trees[0] = lyd_trees_getstart(tree);
-    LY_ARRAY_INCREMENT(trees);
-
-    /* variable arguments */
-    for (unsigned int u = 1; u < count; ++u) {
-        trees[u] = lyd_trees_getstart(va_arg(ap, const struct lyd_node *));
-        LY_ARRAY_INCREMENT(trees);
-    }
-
-    va_end(ap);
-    return trees;
-
-error:
-    (void)ret; /* unused */
-    lyd_trees_free(trees, 1);
-    va_end(ap);
-    return NULL;
-}
-
-API const struct lyd_node **
-lyd_trees_add(const struct lyd_node **trees, const struct lyd_node *tree)
-{
-    const struct lyd_node **t = NULL;
-
-    LY_CHECK_ARG_RET(NULL, tree, trees, trees);
-
-    LY_ARRAY_NEW_RET(tree->schema->module->ctx, trees, t, NULL);
-    *t = lyd_trees_getstart(tree);
-
-    return trees;
-}
-
 LY_ERR
 lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int *dynamic, int second,
-                ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node **trees)
+                ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node *tree)
 {
     LY_ERR ret = LY_SUCCESS;
     struct ly_err_item *err = NULL;
     struct ly_ctx *ctx;
     struct lysc_type *type;
     int options = LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
-            (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+            (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
     assert(node);
 
     ctx = node->schema->module->ctx;
@@ -134,7 +61,7 @@
         node->value.realtype = type;
     }
     ret = type->plugin->store(ctx, type, value, value_len, options, get_prefix, parser, format,
-                             trees ? (void*)node : (void*)node->schema, trees,
+                             tree ? (void *)node : (void *)node->schema, tree,
                              &node->value, NULL, &err);
     if (ret && (ret != LY_EINCOMPLETE)) {
         if (err) {
@@ -187,15 +114,15 @@
 LY_ERR
 lyd_value_parse_attr(struct ly_ctx *ctx, struct lyd_attr *attr, const char *value, size_t value_len, int *dynamic,
                      int second, ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,
-                     const struct lysc_node *ctx_snode, const struct lyd_node **trees)
+                     const struct lysc_node *ctx_snode, const struct lyd_node *tree)
 {
     LY_ERR ret = LY_SUCCESS;
     struct ly_err_item *err = NULL;
     struct lyext_metadata *ant;
     int options = LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
-            (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+            (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
 
-    assert(ctx && attr && ((trees && attr->parent) || ctx_snode));
+    assert(ctx && attr && ((tree && attr->parent) || ctx_snode));
 
     ant = attr->annotation->data;
 
@@ -203,7 +130,7 @@
         attr->value.realtype = ant->type;
     }
     ret = ant->type->plugin->store(ctx, ant->type, value, value_len, options, get_prefix, parser, format,
-                                  trees ? (void *)attr->parent : (void *)ctx_snode, trees, &attr->value, NULL, &err);
+                                  tree ? (void *)attr->parent : (void *)ctx_snode, tree, &attr->value, NULL, &err);
     if (ret && (ret != LY_EINCOMPLETE)) {
         if (err) {
             ly_err_print(err);
@@ -256,18 +183,18 @@
 
 API LY_ERR
 lyd_value_validate(struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
-                   ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node **trees)
+                   ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node *tree)
 {
     LY_ERR rc;
     struct ly_err_item *err = NULL;
     struct lysc_type *type;
-    int options = (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+    int options = (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
 
     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,
-                             get_prefix, get_prefix_data, format, trees ? (void*)node : (void*)node->schema, trees,
+                             get_prefix, get_prefix_data, format, tree ? (void*)node : (void*)node->schema, tree,
                              NULL, NULL, &err);
     if (rc == LY_EINCOMPLETE) {
         return rc;
@@ -287,21 +214,21 @@
 
 API LY_ERR
 lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t value_len,
-                  ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node **trees)
+                  ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node *tree)
 {
     LY_ERR ret = LY_SUCCESS, rc;
     struct ly_err_item *err = NULL;
     struct ly_ctx *ctx;
     struct lysc_type *type;
     struct lyd_value data = {0};
-    int options = LY_TYPE_OPTS_STORE | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+    int options = LY_TYPE_OPTS_STORE | (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
 
     LY_CHECK_ARG_RET(node ? node->schema->module->ctx : NULL, node, value, LY_EINVAL);
 
     ctx = node->schema->module->ctx;
     type = ((struct lysc_node_leaf*)node->schema)->type;
     rc = type->plugin->store(ctx, type, value, value_len, options, get_prefix, get_prefix_data, format, (struct lyd_node*)node,
-                             trees, &data, NULL, &err);
+                             tree, &data, NULL, &err);
     if (rc == LY_EINCOMPLETE) {
         ret = rc;
         /* continue with comparing, just remember what to return if storing is ok */
@@ -605,7 +532,12 @@
 
     memset(keys, 0, sizeof *keys);
 
-    keys->str = strndup(keys_str, keys_len ? keys_len : strlen(keys_str));
+    if (!keys_str) {
+        /* nothing to parse */
+        return LY_SUCCESS;
+    }
+
+    keys->str = strndup(keys_str, keys_len);
     LY_CHECK_ERR_GOTO(!keys->str, LOGMEM(list->module->ctx); ret = LY_EMEM, cleanup);
 
     next_key = keys->str;
@@ -686,8 +618,8 @@
 
     /* create and insert all the keys */
     for (i = 0; i < keys.key_count; ++i) {
-        LY_CHECK_GOTO(ret = lyd_create_term((struct lysc_node *)keys.keys[i].schema, keys.keys[i].value, 0, 0,
-                                            lydjson_resolve_prefix, NULL, LYD_JSON, &key), cleanup);
+        LY_CHECK_GOTO(ret = lyd_create_term((struct lysc_node *)keys.keys[i].schema, keys.keys[i].value,
+                                            strlen(keys.keys[i].value), NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &key), cleanup);
         lyd_insert_node(list, NULL, key);
     }
 
@@ -735,6 +667,10 @@
 
     LY_CHECK_ARG_RET(ctx, parent || module, name, NULL);
 
+    if (!module) {
+        module = parent->schema->module;
+    }
+
     schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYS_CONTAINER | LYS_NOTIF | LYS_ACTION, 0);
     LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Inner node \"%s\" not found.", name), NULL);
 
@@ -756,6 +692,10 @@
 
     LY_CHECK_ARG_RET(ctx, parent || module, name, NULL);
 
+    if (!module) {
+        module = parent->schema->module;
+    }
+
     schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYS_LIST, 0);
     LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "List node \"%s\" not found.", name), NULL);
 
@@ -768,7 +708,8 @@
     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 *);
 
-        LY_CHECK_GOTO(rc = lyd_create_term(key_s, key_val, 0, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &key), cleanup);
+        rc = lyd_create_term(key_s, key_val, key_val ? strlen(key_val) : 0, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &key);
+        LY_CHECK_GOTO(rc, cleanup);
         lyd_insert_node(ret, NULL, key);
     }
 
@@ -797,10 +738,14 @@
 
     LY_CHECK_ARG_RET(ctx, parent || module, name, NULL);
 
+    if (!module) {
+        module = parent->schema->module;
+    }
+
     schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYS_LIST, 0);
     LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "List node \"%s\" not found.", name), NULL);
 
-    if (!lyd_create_list(schema, keys, 0, &ret) && parent) {
+    if (!lyd_create_list(schema, keys, keys ? strlen(keys) : 0, &ret) && parent) {
         lyd_insert_node(parent, NULL, ret);
     }
     return ret;
@@ -815,10 +760,15 @@
 
     LY_CHECK_ARG_RET(ctx, parent || module, name, NULL);
 
+    if (!module) {
+        module = parent->schema->module;
+    }
+
     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), NULL);
 
-    if (!lyd_create_term(schema, val_str, 0, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &ret) && parent) {
+    if (!lyd_create_term(schema, val_str, val_str ? strlen(val_str) : 0, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &ret)
+            && parent) {
         lyd_insert_node(parent, NULL, ret);
     }
     return ret;
@@ -834,6 +784,10 @@
 
     LY_CHECK_ARG_RET(ctx, parent || module, name, NULL);
 
+    if (!module) {
+        module = parent->schema->module;
+    }
+
     schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYD_NODE_ANY, 0);
     LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Any node \"%s\" not found.", name), NULL);
 
@@ -867,7 +821,7 @@
  * @param[in] node Node to insert.
  */
 static void
-lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node)
+lyd_insert_after_node(struct lyd_node *sibling, struct lyd_node *node)
 {
     assert(!node->next && (node->prev == node));
 
@@ -896,7 +850,7 @@
  * @param[in] node Node to insert.
  */
 static void
-lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node)
+lyd_insert_before_node(struct lyd_node *sibling, struct lyd_node *node)
 {
     assert(!node->next && (node->prev == node));
 
@@ -921,7 +875,7 @@
  * @param[in] node Node to insert.
  */
 static void
-lyd_insert_last(struct lyd_node *parent, struct lyd_node *node)
+lyd_insert_last_node(struct lyd_node *parent, struct lyd_node *node)
 {
     struct lyd_node_inner *par;
 
@@ -956,15 +910,15 @@
             /* it is key and we need to insert it at the correct place */
             anchor = lyd_get_prev_key_anchor(lyd_node_children(parent), node->schema);
             if (anchor) {
-                lyd_insert_after(anchor, node);
+                lyd_insert_after_node(anchor, node);
             } else if (lyd_node_children(parent)) {
-                lyd_insert_before((struct lyd_node *)lyd_node_children(parent), node);
+                lyd_insert_before_node((struct lyd_node *)lyd_node_children(parent), node);
             } else {
-                lyd_insert_last(parent, node);
+                lyd_insert_last_node(parent, node);
             }
         } else {
             /* last child */
-            lyd_insert_last(parent, node);
+            lyd_insert_last_node(parent, node);
         }
     } else if (*first_sibling) {
         /* top-level siblings, find the last one from this module, or simply the last */
@@ -974,7 +928,7 @@
         }
 
         /* insert */
-        lyd_insert_after(anchor, node);
+        lyd_insert_after_node(anchor, node);
     } else {
         /* the only sibling */
         *first_sibling = node;
@@ -992,6 +946,187 @@
     lyd_insert_hash(node);
 }
 
+static LY_ERR
+lyd_insert_check_schema(const struct lysc_node *parent, const struct lysc_node *schema)
+{
+    const struct lysc_node *par2;
+
+    assert(schema);
+
+    /* adjust parent first */
+    while (parent && (parent->nodetype & (LYS_CASE | LYS_CHOICE))) {
+        parent = parent->parent;
+    }
+
+    /* find schema parent */
+    for (par2 = schema->parent; par2 && (par2->nodetype & (LYS_CASE | LYS_CHOICE)); par2 = par2->parent);
+
+    if (parent) {
+        /* inner node */
+        if (par2 != parent) {
+            LOGERR(parent->module->ctx, LY_EINVAL, "Cannot insert, parent of \"%s\" is not \"%s\".", schema->name, parent->name);
+            return LY_EINVAL;
+        }
+    } else {
+        /* top-level node */
+        if (par2) {
+            LOGERR(parent->module->ctx, LY_EINVAL, "Cannot insert, node \"%s\" is not top-level.", schema->name);
+            return LY_EINVAL;
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+API LY_ERR
+lyd_insert(struct lyd_node *parent, struct lyd_node *node)
+{
+    struct lyd_node *iter;
+
+    LY_CHECK_ARG_RET(NULL, parent, node, !(parent->schema->nodetype & LYD_NODE_INNER), LY_EINVAL);
+
+    LY_CHECK_RET(lyd_insert_check_schema(parent->schema, node->schema));
+
+    if (node->schema->flags & LYS_KEY) {
+        LOGERR(parent->schema->module->ctx, LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name);
+        return LY_EINVAL;
+    }
+
+    if (node->parent || node->prev->next) {
+        lyd_unlink_tree(node);
+    }
+
+    while (node) {
+        iter = node->next;
+        lyd_unlink_tree(node);
+        lyd_insert_node(parent, NULL, node);
+        node = iter;
+    }
+    return LY_SUCCESS;
+}
+
+API LY_ERR
+lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node)
+{
+    struct lyd_node *iter;
+
+    LY_CHECK_ARG_RET(NULL, sibling, node, LY_EINVAL);
+
+    LY_CHECK_RET(lyd_insert_check_schema(sibling->schema->parent, node->schema));
+
+    if (node->schema->flags & LYS_KEY) {
+        LOGERR(sibling->schema->module->ctx, LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name);
+        return LY_EINVAL;
+    } else if (sibling->schema->flags & LYS_KEY) {
+        LOGERR(sibling->schema->module->ctx, LY_EINVAL, "Cannot insert into keys.");
+        return LY_EINVAL;
+    }
+
+    if (node->parent || node->prev->next) {
+        lyd_unlink_tree(node);
+    }
+
+    /* insert in reverse order to get the original order */
+    node = node->prev;
+    while (node) {
+        iter = node->prev;
+        lyd_unlink_tree(node);
+
+        lyd_insert_before_node(sibling, node);
+        /* move the anchor accordingly */
+        sibling = node;
+
+        node = (iter == node) ? NULL : iter;
+    }
+    return LY_SUCCESS;
+}
+
+API LY_ERR
+lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node)
+{
+    struct lyd_node *iter;
+
+    LY_CHECK_ARG_RET(NULL, sibling, node, LY_EINVAL);
+
+    LY_CHECK_RET(lyd_insert_check_schema(sibling->schema->parent, node->schema));
+
+    if (node->schema->flags & LYS_KEY) {
+        LOGERR(sibling->schema->module->ctx, LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name);
+        return LY_EINVAL;
+    } else if (sibling->next && (sibling->next->schema->flags & LYS_KEY)) {
+        LOGERR(sibling->schema->module->ctx, LY_EINVAL, "Cannot insert into keys.");
+        return LY_EINVAL;
+    }
+
+    if (node->parent || node->prev->next) {
+        lyd_unlink_tree(node);
+    }
+
+    while (node) {
+        iter = node->next;
+        lyd_unlink_tree(node);
+
+        lyd_insert_after_node(sibling, node);
+        /* move the anchor accordingly */
+        sibling = node;
+
+        node = iter;
+    }
+    return LY_SUCCESS;
+}
+
+API void
+lyd_unlink_tree(struct lyd_node *node)
+{
+    struct lyd_node *iter;
+
+    if (!node) {
+        return;
+    }
+
+    /* unlink from siblings */
+    if (node->prev->next) {
+        node->prev->next = node->next;
+    }
+    if (node->next) {
+        node->next->prev = node->prev;
+    } else {
+        /* unlinking the last node */
+        if (node->parent) {
+            iter = node->parent->child;
+        } else {
+            iter = node->prev;
+            while (iter->prev != node) {
+                iter = iter->prev;
+            }
+        }
+        /* update the "last" pointer from the first node */
+        iter->prev = node->prev;
+    }
+
+    /* unlink from parent */
+    if (node->parent) {
+        if (node->parent->child == node) {
+            /* the node is the first child */
+            node->parent->child = node->next;
+        }
+
+        lyd_unlink_hash(node);
+
+        /* check for keyless list and update its hash */
+        for (iter = (struct lyd_node *)node->parent; iter; iter = (struct lyd_node *)iter->parent) {
+            if (iter->schema->flags & LYS_KEYLESS) {
+                lyd_hash(iter);
+            }
+        }
+
+        node->parent = NULL;
+    }
+
+    node->next = NULL;
+    node->prev = node;
+}
+
 LY_ERR
 lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const struct lys_module *mod, const char *name,
                 size_t name_len, const char *value, size_t value_len, int *dynamic, ly_clb_resolve_prefix get_prefix,
@@ -1056,14 +1191,14 @@
 }
 
 API const struct lyd_node_term *
-lyd_target(struct lyd_value_path *path, const struct lyd_node **trees)
+lyd_target(struct lyd_value_path *path, const struct lyd_node *tree)
 {
-    unsigned int u, v, x;
+    unsigned int u, x;
     const struct lyd_node *parent = NULL, *start_search;
     struct lyd_node *node = NULL;
     uint64_t pos = 1;
 
-    LY_CHECK_ARG_RET(NULL, path, trees, NULL);
+    LY_CHECK_ARG_RET(NULL, path, tree, NULL);
 
     LY_ARRAY_FOR(path, u) {
         if (parent) {
@@ -1071,15 +1206,10 @@
 search_inner:
             lyd_find_sibling_next(start_search, path[u].node->module, path[u].node->name, 0, NULL, 0, &node);
         } else {
-            LY_ARRAY_FOR(trees, v) {
-                start_search = trees[v];
+            start_search = tree;
 search_toplevel:
-                /* WARNING! to use search_toplevel label correctly, variable v must be preserved and not changed! */
-                lyd_find_sibling_next(start_search, path[u].node->module, path[u].node->name, 0, NULL, 0, &node);
-                if (node) {
-                    break;
-                }
-            }
+            /* WARNING! to use search_toplevel label correctly, variable v must be preserved and not changed! */
+            lyd_find_sibling_next(start_search, path[u].node->module, path[u].node->name, 0, NULL, 0, &node);
         }
         if (!node) {
             return NULL;
@@ -1498,7 +1628,7 @@
     if (top) {
         lyd_free_tree(top);
     } else {
-        lyd_free_withsiblings(first);
+        lyd_free_siblings(first);
     }
     return NULL;
 }
@@ -1864,7 +1994,7 @@
     struct lyd_node **match_p;
     struct lyd_node_inner *parent;
 
-    LY_CHECK_ARG_RET(NULL, target, match, LY_EINVAL);
+    LY_CHECK_ARG_RET(NULL, target, LY_EINVAL);
 
     if (!siblings) {
         /* no data */
@@ -2101,6 +2231,10 @@
         return LY_ENOTFOUND;
     }
 
+    if (key_or_value && !val_len) {
+        val_len = strlen(key_or_value);
+    }
+
     /* create data node if needed and find it */
     switch (schema->nodetype) {
     case LYS_CONTAINER:
diff --git a/src/tree_data.h b/src/tree_data.h
index 0bb4e8b..4943b49 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -309,7 +309,7 @@
 };
 
 /**
- * @brief Data node structure for the terminal data tree nodes - leafs and leaf-lists.
+ * @brief Data node structure for the terminal data tree nodes - leaves and leaf-lists.
  */
 struct lyd_node_term {
     uint32_t hash;                   /**< hash of this particular node (module name + schema name + key string values if list or
@@ -517,10 +517,21 @@
 struct lyd_node *lyd_parse_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options);
 
 /**
+ * @brief Fully validate a data tree.
+ *
+ * @param[in] ctx libyang context. Can be NULL if @p node is set.
+ * @param[in,out] node Root data tree node to recursively validate. May be changed by validation.
+ * @param[in] val_opts Validation options (@ref datavalidationoptions).
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LY_ERR lyd_validate(const struct ly_ctx *ctx, struct lyd_node **node, int val_opts);
+
+/**
  * @brief Create a new inner node in a data tree.
  *
  * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
- * @param[in] module Module with the node being created.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
  * @param[in] name Schema node name of the new data node. The node can be #LYS_CONTAINER, #LYS_NOTIF, #LYS_RPC, or #LYS_ACTION.
  * @return New created node.
  * @return NULL on error.
@@ -531,7 +542,7 @@
  * @brief Create a new list node in a data tree.
  *
  * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
- * @param[in] module Module with the node being created.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
  * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
  * @param[in] ... Ordered key values of the new list instance, all must be set. In case of an instance-identifier
  * or identityref value, the JSON format is expected (module names instead of prefixes).
@@ -544,7 +555,7 @@
  * @brief Create a new list node in a data tree.
  *
  * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
- * @param[in] module Module with the node being created.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
  * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
  * @param[in] keys All key values predicate in the form of "[key1='val1'][key2='val2']...", they do not have to be ordered.
  * In case of an instance-identifier or identityref value, the JSON format is expected (module names instead of prefixes).
@@ -557,7 +568,7 @@
  * @brief Create a new term node in a data tree.
  *
  * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
- * @param[in] module Module with the node being created.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
  * @param[in] name Schema node name of the new data node. The node can be #LYS_LEAF or #LYS_LEAFLIST.
  * @param[in] val_str String form of the value of the node being created. In case of an instance-identifier or identityref
  * value, the JSON format is expected (module names instead of prefixes).
@@ -570,7 +581,7 @@
  * @brief Create a new any node in a data tree.
  *
  * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
- * @param[in] module Module with the node being created.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
  * @param[in] name Schema node name of the new data node. The node can be #LYS_ANYDATA or #LYS_ANYXML.
  * @param[in] value Value to be directly assigned to the node. Expected type is determined by @p value_type.
  * @param[in] value_type Type of the provided value in @p value.
@@ -581,6 +592,52 @@
                              const void *value, LYD_ANYDATA_VALUETYPE value_type);
 
 /**
+ * @brief Insert a child into a parent. It is inserted as the last child.
+ *
+ * - if the node is part of some other tree, it is automatically unlinked.
+ * - if the node is the first node of a node list (with no parent), all the subsequent nodes are also inserted.
+ *
+ * @param[in] parent Parent node to insert into.
+ * @param[in] node Node to insert.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LY_ERR lyd_insert(struct lyd_node *parent, struct lyd_node *node);
+
+/**
+ * @brief Insert a node before another node that is its schema sibling.
+ *
+ * - if the node is part of some other tree, it is automatically unlinked.
+ * - if the node is the first node of a node list (with no parent), all the subsequent nodes are also inserted.
+ *
+ * @param[in] sibling Sibling node to insert before.
+ * @param[in] node Node to insert.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LY_ERR lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node);
+
+/**
+ * @brief Insert a node after another node that is its schema sibling.
+ *
+ * - if the node is part of some other tree, it is automatically unlinked.
+ * - if the node is the first node of a node list (with no parent), all the subsequent nodes are also inserted.
+ *
+ * @param[in] sibling Sibling node to insert after.
+ * @param[in] node Node to insert.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LY_ERR lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node);
+
+/**
+ * @brief Unlink the specified data subtree.
+ *
+ * @param[in] node Data tree node to be unlinked (together with all the children).
+ */
+void lyd_unlink_tree(struct lyd_node *node);
+
+/**
  * @brief Free all the nodes (even parents of the node) in the data tree.
  *
  * @param[in] node Any of the nodes inside the tree.
@@ -592,33 +649,16 @@
  *
  * @param[in] node Any of the sibling nodes to free.
  */
-void lyd_free_withsiblings(struct lyd_node *node);
+void lyd_free_siblings(struct lyd_node *node);
 
 /**
  * @brief Free (and unlink) the specified data (sub)tree.
  *
- * __PARTIAL CHANGE__ - validate after the final change on the data tree (see @ref howtodatamanipulators).
- *
  * @param[in] node Root of the (sub)tree to be freed.
  */
 void lyd_free_tree(struct lyd_node *node);
 
 /**
- * @brief Unlink the specified data subtree. All referenced namespaces are copied.
- *
- * Note, that the node's connection with the schema tree is kept. Therefore, in case of
- * reconnecting the node to a data tree using lyd_paste() it is necessary to paste it
- * to the appropriate place in the data tree following the schema.
- *
- * __PARTIAL CHANGE__ - validate after the final change on the data tree (see @ref howtodatamanipulators).
- *
- * @param[in] node Data tree node to be unlinked (together with all children).
- * @return LY_SUCCESS for success
- * @return LY_E* values in case of error
- */
-LY_ERR lyd_unlink_tree(struct lyd_node *node);
-
-/**
  * @brief Destroy data attribute.
  *
  * @param[in] ctx Context where the attribute was created.
@@ -629,33 +669,6 @@
 void lyd_free_attr(struct ly_ctx *ctx, struct lyd_attr *attr, int recursive);
 
 /**
- * @brief Prepare ([sized array](@ref sizedarrays)) of data trees required by various (mostly validation) functions.
- *
- * @param[in] count Number of trees to include (including the mandatory @p tree).
- * @param[in] tree First (and mandatory) tree to be included into the resulting ([sized array](@ref sizedarrays)).
- * @return NULL in case of memory allocation failure or invalid argument, prepared ([sized array](@ref sizedarrays)) otherwise.
- */
-const struct lyd_node **lyd_trees_new(size_t count, const struct lyd_node *tree, ...);
-
-/**
- * @brief Add tree into the ([sized array](@ref sizedarrays)) of data trees created by lyd_trees_new(),
- *
- * @param[in] trees Existing [sized array](@ref sizedarrays)) of data trees to be extended.
- * @param[in] tree Data tree to be included into the provided @p trees ([sized array](@ref sizedarrays)).
- * @return NULL in case of memory allocation failure or invalid argument, extended @p trees ([sized array](@ref sizedarrays)) otherwise.
- */
-const struct lyd_node **lyd_trees_add(const struct lyd_node **trees, const struct lyd_node *tree);
-
-/**
- * @brief Free the trees ([sized array](@ref sizedarrays)).
- *
- * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees.
- * @param[in] free_data Flag to free also the particular trees in the @p trees ([sized array](@ref sizedarrays)).
- * If set to zero, only the trees envelope is freed and data are untouched.
- */
-void lyd_trees_free(const struct lyd_node **trees, int free_data);
-
-/**
  * @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value.
  *
  * The given node is not modified in any way - it is just checked if the @p value can be set to the node.
@@ -670,15 +683,15 @@
  * @param[in] get_prefix Callback function to resolve prefixes used in the @p value string.
  * @param[in] get_prefix_data Private data for the @p get_prefix callback.
  * @param[in] format Input format of the data.
- * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
- *            data instance (leafref target, instance-identifier) can be placed. NULL in case the data tree are not yet complete,
- *            then LY_EINCOMPLETE can be returned. To simply prepare this structure, use lyd_trees_new().
+ * @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 the @p trees is not provided and it was needed to finish the validation (e.g. due to require-instance).
  * @return LY_ERR value if an error occurred.
  */
 LY_ERR lyd_value_validate(struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
-                          ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node **trees);
+                          ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node *tree);
 
 /**
  * @brief Compare the node's value with the given string value. The string value is first validated according to the node's type.
@@ -690,17 +703,16 @@
  * @param[in] get_prefix Callback function to resolve prefixes used in the @p value string.
  * @param[in] get_prefix_data Private data for the @p get_prefix callback.
  * @param[in] format Input format of the data.
- * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
- *            data instance (leafref target, instance-identifier) can be placed. NULL in case the data tree are not yet complete,
- *            then LY_EINCOMPLETE can be returned in case the validation was not completed, but values matches. To simply prepare
- *            this structure, use lyd_trees_new(). To simply prepare this structure, use lyd_trees_new().
+ * @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_ERR value if an error occurred.
  */
 LY_ERR lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t value_len,
-                         ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node **trees);
+                         ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node *tree);
 
 /**
  * @defgroup datacompareoptions Data compare options
@@ -773,11 +785,10 @@
  * @brief Resolve instance-identifier defined by lyd_value_path structure.
  *
  * @param[in] path Path structure specifying the instance-identifier target.
- * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees to be searched.
- *            To simply prepare this structure, use lyd_trees_new().
- * @return Target node of the instance-identifier present in the given data @p trees.
+ * @param[in] tree Data tree to be searched.
+ * @return Target node of the instance-identifier present in the given data @p tree.
  */
-const struct lyd_node_term *lyd_target(struct lyd_value_path *path, const struct lyd_node **trees);
+const struct lyd_node_term *lyd_target(struct lyd_value_path *path, const struct lyd_node *tree);
 
 /**
  * @brief Get string value of a term data \p node.
@@ -824,7 +835,7 @@
  * @param[in] first Starting sibling node for search, only succeeding ones are searched.
  * @param[in] module Module of the node to find.
  * @param[in] name Name of the node to find.
- * @param[in] name_len Optional length of the @p name argument in case it is not NULL-terminated string.
+ * @param[in] name_len Optional length of @p name in case it is not 0-terminated string.
  * @param[in] key_or_value Expected value depends on the type of @p name node:
  *              LYS_CONTAINER:
  *              LYS_ANYXML:
@@ -843,7 +854,7 @@
  *              Note that any explicit values (leaf, leaf-list or list key values) will be canonized first
  *              before comparison. But values that do not have a canonical value are expected to be in the
  *              JSON format!
- * @param[in] val_len Optional length of the @p key_or_value argument in case it is not NULL-terminated string.
+ * @param[in] val_len Optional length of @p key_or_value in case it is not 0-terminated string.
  * @param[out] match Found data node.
  * @return LY_SUCCESS on success, @p match set.
  * @return LY_ENOTFOUND if not found, @p match set to NULL.
@@ -903,6 +914,7 @@
  *              Note that any explicit values (leaf-list or list key values) will be canonized first
  *              before comparison. But values that do not have a canonical value are expected to be in the
  *              JSON format!
+ * @param[in] val_len Optional length of @p key_or_value in case it is not 0-terminated.
  * @param[out] match Can be NULL, otherwise the found data node.
  * @return LY_SUCCESS on success, @p match set.
  * @return LY_ENOTFOUND if not found, @p match set to NULL.
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
index 9ff54c4..aea9f7b 100644
--- a/src/tree_data_free.c
+++ b/src/tree_data_free.c
@@ -42,54 +42,6 @@
     LY_ARRAY_FREE(path);
 }
 
-API LY_ERR
-lyd_unlink_tree(struct lyd_node *node)
-{
-    struct lyd_node *iter;
-    struct lyd_node **first_sibling;
-
-    LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
-
-    if (node->parent) {
-        first_sibling = lyd_node_children_p((struct lyd_node*)node->parent);
-    }
-
-    /* unlink from siblings */
-    if (node->prev->next) {
-        node->prev->next = node->next;
-    }
-    if (node->next) {
-        node->next->prev = node->prev;
-    } else {
-        /* unlinking the last node */
-        if (node->parent) {
-            iter = *first_sibling;
-        } else {
-            iter = node->prev;
-            while (iter->prev != node) {
-                iter = iter->prev;
-            }
-        }
-        /* update the "last" pointer from the first node */
-        iter->prev = node->prev;
-    }
-
-    /* unlink from parent */
-    if (node->parent) {
-        if (*first_sibling == node) {
-            /* the node is the first child */
-            *first_sibling = node->next;
-        }
-        lyd_unlink_hash(node);
-        node->parent = NULL;
-    }
-
-    node->next = NULL;
-    node->prev = node;
-
-    return EXIT_SUCCESS;
-}
-
 API void
 lyd_free_attr(struct ly_ctx *ctx, struct lyd_attr *attr, int recursive)
 {
@@ -222,7 +174,7 @@
 }
 
 API void
-lyd_free_withsiblings(struct lyd_node *node)
+lyd_free_siblings(struct lyd_node *node)
 {
     lyd_free_(node, 0);
 }
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index 7cd5d34..4da2d49 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -34,7 +34,7 @@
  *
  * @param[in] schema Schema node of the new data node.
  * @param[in] value String value to be parsed.
- * @param[in] value_len Length of @p value.
+ * @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] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
  * @param[in] prefix_data User data for @p get_prefix.
@@ -82,7 +82,7 @@
  * @param[in] schema Schema node of the new data node.
  * @param[in] keys_str List instance key values in the form of "[key1='val1'][key2='val2']...".
  *            The keys do not have to be ordered but all of them must be set.
- * @param[in] keys_len Length of @p keys_str.
+ * @param[in] keys_len Length of @p keys_str, must be set correctly.
  * @param[out] node Created node.
  * @return LY_SUCCESS on success.
  * @return LY_ERR value if an error occurred.
@@ -132,9 +132,9 @@
  * @param[in,out] attr Attribute list to add at its end if @p parent is NULL, returned created attribute.
  * @param[in] mod Attribute module (with the annotation definition).
  * @param[in] name Attribute name.
- * @param[in] name_len Length of @p name.
+ * @param[in] name_len Length of @p name, must be set correctly.
  * @param[in] value String value to be parsed.
- * @param[in] value_len Length of @p value.
+ * @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] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
  * @param[in] prefix_data User data for @p get_prefix.
@@ -153,13 +153,13 @@
  *
  * @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 (mandatory).
+ * @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] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
  * @param[in] parser Parser's data for @p get_prefix
  * @param[in] format Input format of @p value.
- * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
+ * @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
@@ -167,7 +167,7 @@
  * @return LY_ERR value if an error occurred.
  */
 LY_ERR lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int *dynamic, int second,
-                       ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node **trees);
+                       ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node *tree);
 
 /**
  * @brief Validate, canonize and store the given @p value into the attribute according to the metadata annotation type's rules.
@@ -175,14 +175,14 @@
  * @param[in] ctx libyang context.
  * @param[in] attr Data attribute 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 (mandatory).
+ * @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] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
  * @param[in] parser Parser's data for @p get_prefix
  * @param[in] format Input format of the data.
  * @param[in] ctx_snode Context node for value resolution in schema.
- * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
+ * @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
@@ -191,7 +191,7 @@
  */
 LY_ERR lyd_value_parse_attr(struct ly_ctx *ctx, struct lyd_attr *attr, const char *value, size_t value_len, int *dynamic,
                             int second, ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,
-                            const struct lysc_node *ctx_snode, const struct lyd_node **trees);
+                            const struct lysc_node *ctx_snode, const struct lyd_node *tree);
 
 /**
  * @brief Parse XML string as YANG data tree.
diff --git a/src/validation.c b/src/validation.c
index 97f02d6..96246cd 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -20,16 +20,67 @@
 #include "tree_data_internal.h"
 #include "tree_schema_internal.h"
 
+static struct lyd_node *
+lys_getnext_data(const struct lyd_node *last, const struct lyd_node *sibling, const struct lysc_node **slast,
+                 const struct lysc_node *parent, const struct lysc_module *module)
+{
+    const struct lysc_node *siter = NULL;
+    struct lyd_node *match = NULL;
+
+    assert(parent || module);
+    assert(!last || (slast && *slast));
+
+    if (slast) {
+        siter = *slast;
+    }
+
+    if (last && last->next) {
+        /* find next data instance */
+        lyd_find_sibling_next2(last->next, siter, NULL, 0, &match);
+        if (match) {
+            return match;
+        }
+    }
+
+    /* find next schema node data instance */
+    while ((siter = lys_getnext(siter, parent, module, 0))) {
+        switch (siter->nodetype) {
+        case LYS_CONTAINER:
+        case LYS_ANYXML:
+        case LYS_ANYDATA:
+        case LYS_LEAF:
+            lyd_find_sibling_val(sibling, siter, NULL, 0, &match);
+            break;
+        case LYS_LIST:
+        case LYS_LEAFLIST:
+            lyd_find_sibling_next2(sibling, siter, NULL, 0, &match);
+            break;
+        default:
+            assert(0);
+            LOGINT(NULL);
+        }
+
+        if (match) {
+            break;
+        }
+    }
+
+    if (slast) {
+        *slast = siter;
+    }
+    return match;
+}
+
 /**
  * @brief Evaluate a single "when" condition.
  *
  * @param[in] when When to evaluate.
  * @param[in] node Node whose existence depends on this when.
- * @param[in] trees Array of all data trees.
+ * @param[in] tree Data tree.
  * @return LY_ERR value (LY_EINCOMPLETE if a referenced node does not have its when evaluated)
  */
 static LY_ERR
-lyd_val_when(struct lysc_when *when, struct lyd_node *node, const struct lyd_node **trees)
+lyd_val_when(struct lysc_when *when, struct lyd_node *node, const struct lyd_node *tree)
 {
     LY_ERR ret;
     const struct lyd_node *ctx_node;
@@ -46,7 +97,7 @@
 
     /* evaluate when */
     ret = lyxp_eval(when->cond, LYD_UNKNOWN, when->module, ctx_node, ctx_node ? LYXP_NODE_ELEM : LYXP_NODE_ROOT_CONFIG,
-                    trees, &xp_set, LYXP_SCHEMA);
+                    tree, &xp_set, LYXP_SCHEMA);
     lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
 
     /* return error or LY_EINCOMPLETE for dependant unresolved when */
@@ -72,7 +123,7 @@
 
 LY_ERR
 lyd_validate_unres(struct ly_set *node_types, struct ly_set *attr_types, struct ly_set *node_when, LYD_FORMAT format,
-                   ly_clb_resolve_prefix get_prefix_clb, void *parser_data, const struct lyd_node **trees)
+                   ly_clb_resolve_prefix get_prefix_clb, void *parser_data, const struct lyd_node *tree)
 {
     LY_ERR ret = LY_SUCCESS;
     uint32_t u;
@@ -83,7 +134,7 @@
 
         /* validate and store the value of the node */
         ret = lyd_value_parse(node, node->value.original, strlen(node->value.original), 0, 1, get_prefix_clb,
-                            parser_data, format, trees);
+                            parser_data, format, tree);
         LY_CHECK_RET(ret);
     }
 
@@ -93,7 +144,7 @@
 
         /* validate and store the value of the node */
         ret = lyd_value_parse_attr(attr->parent->schema->module->ctx, attr, attr->value.original,
-                                   strlen(attr->value.original), 0, 1, get_prefix_clb, parser_data, format, NULL, trees);
+                                   strlen(attr->value.original), 0, 1, get_prefix_clb, parser_data, format, NULL, tree);
         LY_CHECK_RET(ret);
     }
 
@@ -116,7 +167,7 @@
             do {
                 uint32_t i;
                 LY_ARRAY_FOR(schema->when, i) {
-                    ret = lyd_val_when(schema->when[i], node, trees);
+                    ret = lyd_val_when(schema->when[i], node, tree);
                     if (ret) {
                         break;
                     }
@@ -160,47 +211,12 @@
     return ly_ctx_get_module_iter(ctx, i);
 }
 
-static int
-lyd_val_has_choice_data(const struct lysc_node *snode, struct lyd_node *sibling)
-{
-    const struct lysc_node *iter = NULL;
-
-    assert(snode->nodetype == LYS_CHOICE);
-
-    while ((iter = lys_getnext(iter, snode, NULL, 0))) {
-        switch (iter->nodetype) {
-        case LYS_CONTAINER:
-        case LYS_ANYXML:
-        case LYS_ANYDATA:
-        case LYS_LEAF:
-            if (!lyd_find_sibling_val(sibling, iter, NULL, 0, NULL)) {
-                /* one case child data instance found */
-                return 1;
-            }
-            break;
-        case LYS_LIST:
-        case LYS_LEAFLIST:
-            if (!lyd_find_sibling_next2(sibling, iter, NULL, 0, NULL)) {
-                /* one case child data instance found */
-                return 1;
-            }
-            break;
-        default:
-            assert(0);
-            LOGINT(snode->module->ctx);
-            return 0;
-        }
-    }
-
-    return 0;
-}
-
 static LY_ERR
 lyd_validate_mandatory(const struct lysc_node *snode, struct lyd_node *sibling)
 {
     if (snode->nodetype == LYS_CHOICE) {
         /* some data of a choice case exist */
-        if (lyd_val_has_choice_data(snode, sibling)) {
+        if (lys_getnext_data(NULL, sibling, NULL, snode, NULL)) {
             return LY_SUCCESS;
         }
     } else {
@@ -501,15 +517,70 @@
 }
 
 static LY_ERR
-lyd_validate_cases(const struct lysc_node_case *cases, struct lyd_node *sibling)
+lyd_validate_cases(const struct lysc_node_choice *choic, struct lyd_node **sibling)
 {
-    /* TODO check there are nodes only from a single case,
-     * what if not? validation error or autodelete */
+    const struct lysc_node *scase, *iter, *old_case = NULL, *new_case = NULL;
+    struct lyd_node *match, *to_del;
+    int found;
+
+    LY_LIST_FOR((struct lysc_node *)choic->cases, scase) {
+        found = 0;
+        iter = NULL;
+        match = NULL;
+        while ((match = lys_getnext_data(match, *sibling, &iter, scase, NULL))) {
+            if (match->flags & LYD_NEW) {
+                /* a new case data found, nothing more to look for */
+                found = 2;
+                break;
+            } else {
+                /* and old case data found */
+                if (found == 0) {
+                    found = 1;
+                }
+            }
+        }
+
+        if (found == 1) {
+            /* there cannot be 2 old cases */
+            assert(!old_case);
+
+            /* remember an old existing case */
+            old_case = scase;
+        } else if (found == 2) {
+            if (new_case) {
+                /* new data from 2 cases */
+                LOGVAL(choic->module->ctx, LY_VLOG_LYSC, choic, LY_VCODE_DUPCASE, new_case->name, scase->name);
+                return LY_EVALID;
+            }
+
+            /* remember a new existing case */
+            new_case = scase;
+        }
+    }
+
+    if (old_case && new_case) {
+        /* auto-delete old case */
+        iter = NULL;
+        match = NULL;
+        to_del = NULL;
+        while ((match = lys_getnext_data(match, *sibling, &iter, old_case, NULL))) {
+            if ((*sibling == to_del) && !(*sibling)->parent) {
+                *sibling = (*sibling)->next;
+            }
+            lyd_free_tree(to_del);
+            to_del = match;
+        }
+        if ((*sibling == to_del) && !(*sibling)->parent) {
+            *sibling = (*sibling)->next;
+        }
+        lyd_free_tree(to_del);
+    }
+
     return LY_SUCCESS;
 }
 
 static LY_ERR
-lyd_validate_siblings_schema_r(struct lyd_node *sibling, const struct lysc_node *sparent, const struct lysc_module *mod,
+lyd_validate_siblings_schema_r(struct lyd_node **sibling, const struct lysc_node *sparent, const struct lysc_module *mod,
                                int options)
 {
     const struct lysc_node *snode = NULL;
@@ -521,25 +592,25 @@
         if (snode->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
             slist = (struct lysc_node_list *)snode;
             if (slist->min || slist->max) {
-                LY_CHECK_RET(lyd_validate_minmax(snode, slist->min, slist->max, sibling));
+                LY_CHECK_RET(lyd_validate_minmax(snode, slist->min, slist->max, *sibling));
             }
 
         /* check generic mandatory existence */
         } else if (snode->flags & LYS_MAND_TRUE) {
-            LY_CHECK_RET(lyd_validate_mandatory(snode, sibling));
+            LY_CHECK_RET(lyd_validate_mandatory(snode, *sibling));
         }
 
         /* check unique */
         if (snode->nodetype == LYS_LIST) {
             slist = (struct lysc_node_list *)snode;
             if (slist->uniques) {
-                LY_CHECK_RET(lyd_validate_unique(snode, slist->uniques, sibling));
+                LY_CHECK_RET(lyd_validate_unique(snode, slist->uniques, *sibling));
             }
         }
 
         /* check case duplicites */
         if (snode->nodetype == LYS_CHOICE) {
-            LY_CHECK_RET(lyd_validate_cases(((struct lysc_node_choice *)snode)->cases, sibling));
+            LY_CHECK_RET(lyd_validate_cases((struct lysc_node_choice *)snode, sibling));
         }
 
         if (snode->nodetype & (LYS_CHOICE | LYS_CASE)) {
@@ -552,50 +623,115 @@
 }
 
 static LY_ERR
-lyd_validate_siblings_r(struct lyd_node *sibling, const struct lysc_node *sparent, const struct lysc_module *mod, int options)
+lyd_validate_dup_nodes(struct lyd_node *sibling, struct lyd_node *node)
 {
-    struct lyd_node *node;
+    struct lyd_node **match_p;
+    int fail = 0;
+
+    if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (node->schema->flags & LYS_CONFIG_R)) {
+        /* duplicate instances allowed */
+        return LY_SUCCESS;
+    }
+
+    /* find exactly the same next instance using hashes if possible */
+    if (node->parent && node->parent->children_ht) {
+        if (!lyht_find_next(node->parent->children_ht, &node, node->hash, (void **)&match_p)) {
+            fail = 1;
+        }
+    } else {
+        for (; sibling; sibling = sibling->next) {
+            if (sibling == node) {
+                continue;
+            }
+
+            if (node->schema->nodetype & (LYD_NODE_ANY | LYS_LEAF)) {
+                if (sibling->schema == node->schema) {
+                    fail = 1;
+                    break;
+                }
+            } else if (!lyd_compare(sibling, node, 0)) {
+                fail = 1;
+                break;
+            }
+        }
+    }
+
+    if (fail) {
+        LOGVAL(node->schema->module->ctx, LY_VLOG_LYD, node, LY_VCODE_DUP, node->schema->name);
+        return LY_EVALID;
+    }
+    return LY_SUCCESS;
+}
+
+static LY_ERR
+lyd_validate_siblings_r(struct lyd_node **sibling, const struct lysc_node *sparent, const struct lysc_module *mod, int options)
+{
+    struct lyd_node *next, *node, *match;
+
+    assert(sibling);
 
     /* validate all restrictions of nodes themselves */
-    LY_LIST_FOR(sibling, node) {
-        /* TODO node instance duplicities */
+    LY_LIST_FOR_SAFE(*sibling, next, node) {
+        if (node->flags & LYD_NEW) {
+            /* new node instance duplicities */
+            LY_CHECK_RET(lyd_validate_dup_nodes(*sibling, node));
+        } else if (node->flags & LYD_DEFAULT) {
+            /* remove default if there is an explicit instance */
+            assert(node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_CONTAINER));
+            if (node->schema->nodetype == LYS_LEAFLIST) {
+                lyd_find_sibling_next2(*sibling, node->schema, NULL, 0, &match);
+            } else {
+                lyd_find_sibling_val(*sibling, node->schema, NULL, 0, &match);
+            }
+            while (match && (match->flags & LYD_DEFAULT) && !(match->flags & LYD_NEW)) {
+                lyd_find_sibling_next2(match->next, node->schema, NULL, 0, &match);
+            }
+
+            if (match) {
+                /* non-default (or at least new for container) instance found, remove the default one */
+                if ((*sibling == node) && !(*sibling)->parent) {
+                    *sibling = (*sibling)->next;
+                }
+                lyd_free_tree(node);
+                continue;
+            }
+        }
+
         /* TODO node's must */
         /* TODO node status */
         /* TODO node's if-features */
-        /* TODO node list keys */
+        /* TODO list all keys existence */
         /* node value including if-feature is checked by plugins */
     }
 
     /* validate schema-based restrictions */
     LY_CHECK_RET(lyd_validate_siblings_schema_r(sibling, sparent, mod, options));
 
-    LY_LIST_FOR(sibling, node) {
+    LY_LIST_FOR(*sibling, node) {
         /* this sibling is valid */
         node->flags &= ~LYD_NEW;
 
         /* validate all children recursively */
-        LY_CHECK_RET(lyd_validate_siblings_r((struct lyd_node *)lyd_node_children(node), node->schema, mod, options));
+        if (node->schema->nodetype & LYD_NODE_INNER) {
+            LY_CHECK_RET(lyd_validate_siblings_r(lyd_node_children_p(node), node->schema, mod, options));
+        }
     }
 
     return LY_SUCCESS;
 }
 
 LY_ERR
-lyd_validate_data(const struct lyd_node **trees, const struct lys_module **modules, int mod_count, struct ly_ctx *ctx,
+lyd_validate_data(struct lyd_node **tree, const struct lys_module **modules, int mod_count, struct ly_ctx *ctx,
                   int val_opts)
 {
-    uint32_t i = 0, j;
+    uint32_t i = 0;
     const struct lys_module *mod;
-    struct lyd_node *tree;
 
     if (val_opts & LYD_VALOPT_DATA_ONLY) {
-        if (trees) {
-            for (j = 0; j < LY_ARRAY_SIZE(trees); ++j) {
-                tree = (struct lyd_node *)trees[j];
-
-                /* validate all top-level nodes and then inner nodes recursively */
-                LY_CHECK_RET(lyd_validate_siblings_r(tree, NULL, tree->schema->module->compiled, val_opts));
-            }
+        if (*tree) {
+            /* TODO all modules, not just first */
+            /* validate all top-level nodes and then inner nodes recursively */
+            LY_CHECK_RET(lyd_validate_siblings_r(tree, NULL, (*tree)->schema->module->compiled, val_opts));
         }
     } else {
         while ((mod = lyd_val_next_module(modules, mod_count, ctx, &i))) {
@@ -603,17 +739,6 @@
                 continue;
             }
 
-            /* find data of this module, if any */
-            tree = NULL;
-            if (trees) {
-                for (j = 0; j < LY_ARRAY_SIZE(trees); ++j) {
-                    if (trees[j]->schema->module == mod) {
-                        tree = (struct lyd_node *)trees[j];
-                        break;
-                    }
-                }
-            }
-
             /* validate all top-level nodes and then inner nodes recursively */
             LY_CHECK_RET(lyd_validate_siblings_r(tree, NULL, mod->compiled, val_opts));
         }
@@ -636,7 +761,7 @@
     while ((iter = lys_getnext(iter, schema, mod, LYS_GETNEXT_WITHCHOICE))) {
         switch (iter->nodetype) {
         case LYS_CHOICE:
-            if (((struct lysc_node_choice *)iter)->dflt && !lyd_val_has_choice_data(iter, *first)) {
+            if (((struct lysc_node_choice *)iter)->dflt && !lys_getnext_data(NULL, *first, NULL, iter, NULL)) {
                 /* create default case data */
                 LY_CHECK_RET(lyd_validate_defaults_r(parent, first, (struct lysc_node *)((struct lysc_node_choice *)iter)->dflt,
                                                      NULL, node_types, node_when));
@@ -726,3 +851,58 @@
 
     return LY_SUCCESS;
 }
+
+API LY_ERR
+lyd_validate(const struct ly_ctx *ctx, struct lyd_node **tree, int val_opts)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_node *root, *next, *elem;
+    const struct lyd_attr *attr;
+    struct ly_set type_check = {0}, type_attr_check = {0}, when_check = {0};
+
+    LY_CHECK_ARG_RET(ctx, tree, ctx || *tree, LY_EINVAL);
+
+    if (!ctx) {
+        ctx = (*tree)->schema->module->ctx;
+    }
+
+    /* add nested defaults, collect all types and nodes with when condition */
+    LY_LIST_FOR(*tree, root) {
+        LYD_TREE_DFS_BEGIN(root, next, elem) {
+            LY_LIST_FOR(elem->attr, attr) {
+                ly_set_add(&type_attr_check, (void *)attr, LY_SET_OPT_USEASLIST);
+            }
+            if (elem->schema->nodetype & LYD_NODE_TERM) {
+                ly_set_add(&type_check, (void *)elem, LY_SET_OPT_USEASLIST);
+            } else if (elem->schema->nodetype & LYD_NODE_INNER) {
+                ret = lyd_validate_defaults_r((struct lyd_node_inner *)elem, lyd_node_children_p((struct lyd_node *)elem),
+                                              elem->schema, NULL, &type_check, &when_check);
+                LY_CHECK_GOTO(ret, cleanup);
+            }
+            if (!(elem->schema->nodetype & (LYS_ACTION | LYS_NOTIF)) && elem->schema->when) {
+                ly_set_add(&when_check, (void *)elem, LY_SET_OPT_USEASLIST);
+            }
+
+            LYD_TREE_DFS_END(root, next, elem);
+        }
+    }
+
+    /* add top-level default nodes */
+    ret = lyd_validate_defaults_top(tree, NULL, 0, (struct ly_ctx *)ctx, &type_check, &when_check, val_opts);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* finish incompletely validated terminal values/attributes and when conditions */
+    ret = lyd_validate_unres(&type_check, &type_attr_check, &when_check, LYD_JSON, lydjson_resolve_prefix, NULL,
+                             (const struct lyd_node *)tree);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* context node and other validation tasks that depend on other data nodes */
+    ret = lyd_validate_data(tree, NULL, 0, (struct ly_ctx *)ctx, val_opts);
+    LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+    ly_set_erase(&type_check, NULL);
+    ly_set_erase(&type_attr_check, NULL);
+    ly_set_erase(&when_check, NULL);
+    return ret;
+}
diff --git a/src/validation.h b/src/validation.h
index dfc81d0..d8b62a4 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -27,23 +27,23 @@
  * @param[in] format Format of the unresolved data.
  * @param[in] get_prefix_clb Format-specific getter to resolve prefixes.
  * @param[in] parser_data Parser's data for @p get_prefix_clb.
- * @param[in] trees Array of all data trees.
+ * @param[in] tree Data tree.
  * @return LY_ERR value.
  */
 LY_ERR lyd_validate_unres(struct ly_set *node_types, struct ly_set *attr_types, struct ly_set *node_when, LYD_FORMAT format,
-                          ly_clb_resolve_prefix get_prefix_clb, void *parser_data, const struct lyd_node **trees);
+                          ly_clb_resolve_prefix get_prefix_clb, void *parser_data, const struct lyd_node *tree);
 
 /**
  * @brief Perform all validation tasks, the data tree must be complete when calling this function.
  *
- * @param[in] trees Array of all data trees.
+ * @param[in,out] tree Data tree.
  * @param[in] modules Array of modules that should be validated, NULL for all modules.
  * @param[in] mod_count Modules count.
  * @param[in] ctx Context if all modules should be validated, NULL for only selected modules.
- * @param[in] val_opts Validation options.
+ * @param[in] val_opts Validation options (@ref datavalidationoptions).
  * @return LY_ERR value.
  */
-LY_ERR lyd_validate_data(const struct lyd_node **trees, const struct lys_module **modules, int mod_count,
+LY_ERR lyd_validate_data(struct lyd_node **tree, const struct lys_module **modules, int mod_count,
                          struct ly_ctx *ctx, int val_opts);
 
 /**
diff --git a/src/xpath.c b/src/xpath.c
index c5e3259..9cbc30a 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -751,7 +751,7 @@
         new->ctx_node = set->ctx_node;
         new->root_type = set->root_type;
         new->local_mod = set->local_mod;
-        new->trees = set->trees;
+        new->tree = set->tree;
         new->format = set->format;
     }
 }
@@ -3560,7 +3560,7 @@
                 /* find leafref target */
                 val = lyd_value2str(leaf, &dynamic);
                 node = ly_type_find_leafref(set->ctx, sleaf->type, val, strlen(val), (struct lyd_node *)leaf,
-                                            set->trees, &leaf->value, &errmsg);
+                                            set->tree, &leaf->value, &errmsg);
                 if (dynamic) {
                     free((char *)val);
                 }
@@ -3574,7 +3574,7 @@
                 set_insert_node(set, node, 0, LYXP_NODE_ELEM, 0);
             } else {
                 assert(sleaf->type->basetype == LY_TYPE_INST);
-                node = (struct lyd_node *)lyd_target(leaf->value.target, set->trees);
+                node = (struct lyd_node *)lyd_target(leaf->value.target, set->tree);
                 if (!node) {
                     val = lyd_value2str(leaf, &dynamic);
                     LOGERR(set->ctx, LY_EVALID, "Invalid instance-identifier \"%s\" value - required instance not found.", val);
@@ -3649,7 +3649,7 @@
                 /* store args[1] into ident */
                 rc = sleaf->type->plugin->store(set->ctx, sleaf->type, args[1]->val.str, strlen(args[1]->val.str),
                                                 LY_TYPE_OPTS_STORE, lys_resolve_prefix, (void *)sleaf->dflt_mod, set->format,
-                                                (struct lyd_node *)leaf, set->trees, &data, NULL, &err);
+                                                (struct lyd_node *)leaf, set->tree, &data, NULL, &err);
                 if (err) {
                     ly_err_print(err);
                     ly_err_free(err);
@@ -3732,7 +3732,7 @@
                 /* store args[1] into ident */
                 rc = sleaf->type->plugin->store(set->ctx, sleaf->type, args[1]->val.str, strlen(args[1]->val.str),
                                                 LY_TYPE_OPTS_STORE, lys_resolve_prefix, (void *)sleaf->dflt_mod, set->format,
-                                                (struct lyd_node *)leaf, set->trees, &data, NULL, &err);
+                                                (struct lyd_node *)leaf, set->tree, &data, NULL, &err);
                 if (err) {
                     ly_err_print(err);
                     ly_err_free(err);
@@ -5326,7 +5326,7 @@
 static LY_ERR
 moveto_node(struct lyxp_set *set, const char *qname, uint16_t qname_len)
 {
-    uint32_t i, j;
+    uint32_t i;
     int replaced;
     const char *name_dict = NULL; /* optimization - so we can do (==) instead (!strncmp(...)) in moveto_node_check() */
     const struct lys_module *moveto_mod;
@@ -5354,22 +5354,20 @@
         if ((set->val.nodes[i].type == LYXP_NODE_ROOT_CONFIG) || (set->val.nodes[i].type == LYXP_NODE_ROOT)) {
             assert(!set->val.nodes[i].node);
             /* search in all the trees */
-            LY_ARRAY_FOR(set->trees, j) {
-                for (sub = set->trees[j]; sub; sub = sub->next) {
-                    rc = moveto_node_check(sub, set->root_type, name_dict, moveto_mod);
-                    if (rc == LY_SUCCESS) {
-                        /* pos filled later */
-                        if (!replaced) {
-                            set_replace_node(set, sub, 0, LYXP_NODE_ELEM, i);
-                            replaced = 1;
-                        } else {
-                            set_insert_node(set, sub, 0, LYXP_NODE_ELEM, i);
-                        }
-                        ++i;
-                    } else if (rc == LY_EINCOMPLETE) {
-                        lydict_remove(set->ctx, name_dict);
-                        return rc;
+            for (sub = set->tree; sub; sub = sub->next) {
+                rc = moveto_node_check(sub, set->root_type, name_dict, moveto_mod);
+                if (rc == LY_SUCCESS) {
+                    /* pos filled later */
+                    if (!replaced) {
+                        set_replace_node(set, sub, 0, LYXP_NODE_ELEM, i);
+                        replaced = 1;
+                    } else {
+                        set_insert_node(set, sub, 0, LYXP_NODE_ELEM, i);
                     }
+                    ++i;
+                } else if (rc == LY_EINCOMPLETE) {
+                    lydict_remove(set->ctx, name_dict);
+                    return rc;
                 }
             }
 
@@ -8025,7 +8023,7 @@
 
 LY_ERR
 lyxp_eval(struct lyxp_expr *exp, LYD_FORMAT format, const struct lys_module *local_mod, const struct lyd_node *ctx_node,
-          enum lyxp_node_type ctx_node_type, const struct lyd_node **trees, struct lyxp_set *set, int options)
+          enum lyxp_node_type ctx_node_type, const struct lyd_node *tree, struct lyxp_set *set, int options)
 {
     uint16_t exp_idx = 0;
     LY_ERR rc;
@@ -8041,7 +8039,7 @@
     set->ctx_node = ctx_node;
     set->root_type = lyxp_get_root_type(ctx_node, NULL, options);
     set->local_mod = local_mod;
-    set->trees = trees;
+    set->tree = tree;
     set->format = format;
 
     /* evaluate */
diff --git a/src/xpath.h b/src/xpath.h
index b2f2eb5..d645920 100644
--- a/src/xpath.h
+++ b/src/xpath.h
@@ -251,7 +251,7 @@
     };
     enum lyxp_node_type root_type;
     const struct lys_module *local_mod;
-    const struct lyd_node **trees;
+    const struct lyd_node *tree;
     LYD_FORMAT format;
 };
 
@@ -268,7 +268,7 @@
  * this case just pass NULL for @p ctx_node and use an enum value for this kind of root (#LYXP_NODE_ROOT_CONFIG if
  * @p ctx_node has config true, otherwise #LYXP_NODE_ROOT). #LYXP_NODE_TEXT and #LYXP_NODE_ATTR can also be used,
  * but there are no use-cases in YANG.
- * @param[in] trees Data trees on which to perform the evaluation, they must include all the available tree (including
+ * @param[in] tree Data tree on which to perform the evaluation, it must include all the available data (including
  * the tree of @p ctx_node).
  * @param[out] set Result set. Must be valid and in the same libyang context as @p ctx_node.
  * To be safe, always either zero or cast the @p set to empty. After done using, either cast
@@ -279,7 +279,7 @@
  * LY_EINCOMPLETE for unresolved when).
  */
 LY_ERR lyxp_eval(struct lyxp_expr *exp, LYD_FORMAT format, const struct lys_module *local_mod, const struct lyd_node *ctx_node,
-                 enum lyxp_node_type ctx_node_type, const struct lyd_node **trees, struct lyxp_set *set, int options);
+                 enum lyxp_node_type ctx_node_type, const struct lyd_node *tree, struct lyxp_set *set, int options);
 
 #define LYXP_SCHEMA 0x01 /**< Apply data node access restrictions defined for 'when' and 'must' evaluation. */