schema mount UPDATE support for creating nested data

Not just parsing it directly.
diff --git a/src/parser_json.c b/src/parser_json.c
index c967063..7b780ba 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -183,80 +183,6 @@
 }
 
 /**
- * @brief Try to parse data with a parent based on an extension instance.
- *
- * @param[in] lydctx JSON data parser context.
- * @param[in,out] parent Parent node where the children are inserted.
- * @return LY_SUCCESS on success;
- * @return LY_ENOT if no extension instance parsed the data;
- * @return LY_ERR on error.
- */
-static LY_ERR
-lydjson_nested_ext(struct lyd_json_ctx *lydctx, struct lyd_node *parent)
-{
-    LY_ERR r;
-    struct ly_in in_bck, in_start, in_ext;
-    LY_ARRAY_COUNT_TYPE u;
-    struct lysc_ext_instance *nested_exts = NULL;
-    lyplg_ext_data_parse_clb ext_parse_cb;
-    struct lyd_ctx_ext_val *ext_val;
-    uint32_t quot_count;
-
-    /* backup current input */
-    in_bck = *lydctx->jsonctx->in;
-
-    /* go back in the input for extension parsing */
-    in_start = *lydctx->jsonctx->in;
-    assert(lyjson_ctx_status(lydctx->jsonctx, 0) == LYJSON_OBJECT);
-    quot_count = 0;
-    do {
-        --in_start.current;
-        if ((in_start.current[0] == '\"') && (in_start.current[-1] != '\\')) {
-            ++quot_count;
-        }
-        if (in_start.current == in_start.start) {
-            /* invalid JSON */
-            return LY_ENOT;
-        }
-    } while (quot_count < 2);
-
-    /* check if there are any nested extension instances */
-    if (parent && parent->schema) {
-        nested_exts = parent->schema->exts;
-    }
-    LY_ARRAY_FOR(nested_exts, u) {
-        /* prepare the input and try to parse this extension data */
-        in_ext = in_start;
-        ext_parse_cb = nested_exts[u].def->plugin->parse;
-        r = ext_parse_cb(&in_ext, LYD_JSON, &nested_exts[u], parent, lydctx->parse_opts | LYD_PARSE_ONLY);
-        if (!r) {
-            /* data successfully parsed, remember for validation */
-            if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
-                ext_val = malloc(sizeof *ext_val);
-                LY_CHECK_ERR_RET(!ext_val, LOGMEM(lydctx->jsonctx->ctx), LY_EMEM);
-                ext_val->ext = &nested_exts[u];
-                ext_val->sibling = lyd_child_no_keys(parent);
-                LY_CHECK_RET(ly_set_add(&lydctx->ext_val, ext_val, 1, NULL));
-            }
-
-            /* adjust the jsonctx accordingly */
-            *lydctx->jsonctx->in = in_ext;
-            LYJSON_STATUS_PUSH_RET(lydctx->jsonctx, LYJSON_OBJECT_CLOSED);
-            LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, NULL));
-            return LY_SUCCESS;
-        } else if (r != LY_ENOT) {
-            /* fatal error */
-            return r;
-        }
-        /* data was not from this module, continue */
-    }
-
-    /* no extensions or none matched, restore input */
-    *lydctx->jsonctx->in = in_bck;
-    return LY_ENOT;
-}
-
-/**
  * @brief Skip the currently open JSON object/array
  * @param[in] jsonctx JSON context with the input data to skip.
  * @return LY_ERR value.
@@ -303,27 +229,28 @@
  * @param[in] name Requested node's name.
  * @param[in] name_len Length of the @p name.
  * @param[in] parent Parent of the node being processed, can be NULL in case of top-level.
- * @param[out] snode_p Found schema node corresponding to the input parameters. If NULL, parse as an opaque node.
+ * @param[out] snode Found schema node corresponding to the input parameters. If NULL, parse as an opaque node.
+ * @param[out] ext Extension instance that provided @p snode, if any.
  * @return LY_SUCCES on success.
  * @return LY_ENOT if the whole object was parsed (skipped or as an extension).
  * @return LY_ERR on error.
  */
 static LY_ERR
 lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *prefix, size_t prefix_len, const char *name,
-        size_t name_len, struct lyd_node *parent, const struct lysc_node **snode_p)
+        size_t name_len, struct lyd_node *parent, const struct lysc_node **snode, struct lysc_ext_instance **ext)
 {
     LY_ERR ret = LY_SUCCESS, r;
     struct lys_module *mod = NULL;
     uint32_t getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
 
-    /* init return value */
-    *snode_p = NULL;
+    *snode = NULL;
+    *ext = NULL;
 
     LOG_LOCSET(NULL, parent, NULL, NULL);
 
-    /* get the element module */
+    /* get the element module, prefer parent context because of extensions */
     if (prefix_len) {
-        mod = ly_ctx_get_module_implemented2(lydctx->jsonctx->ctx, prefix, prefix_len);
+        mod = ly_ctx_get_module_implemented2(parent ? LYD_CTX(parent) : lydctx->jsonctx->ctx, prefix, prefix_len);
     } else if (parent) {
         if (parent->schema) {
             mod = parent->schema->module;
@@ -336,13 +263,9 @@
     }
     if (!mod) {
         /* check for extension data */
-        r = lydjson_nested_ext(lydctx, parent);
-        if (!r) {
-            /* successfully parsed */
-            ret = LY_ENOT;
-            goto cleanup;
-        } else if (r != LY_ENOT) {
-            /* error */
+        r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_JSON, NULL, name, name_len, snode, ext);
+        if (r != LY_ENOT) {
+            /* success or error */
             ret = r;
             goto cleanup;
         }
@@ -366,19 +289,15 @@
     /* get the schema node */
     if (mod && (!parent || parent->schema)) {
         if (!parent && lydctx->ext) {
-            *snode_p = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
+            *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
         } else {
-            *snode_p = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
+            *snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
         }
-        if (!*snode_p) {
+        if (!*snode) {
             /* check for extension data */
-            r = lydjson_nested_ext(lydctx, parent);
-            if (!r) {
-                /* successfully parsed */
-                ret = LY_ENOT;
-                goto cleanup;
-            } else if (r != LY_ENOT) {
-                /* error */
+            r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_JSON, NULL, name, name_len, snode, ext);
+            if (r != LY_ENOT) {
+                /* success or error */
                 ret = r;
                 goto cleanup;
             }
@@ -413,7 +332,7 @@
             }
         } else {
             /* check that schema node is valid and can be used */
-            ret = lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode_p);
+            ret = lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode);
         }
     }
 
@@ -634,6 +553,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyd_node *node, *attr, *next, *meta_iter;
+    struct lysc_ext_instance *ext;
     uint64_t instance = 0;
     const char *prev = NULL;
     uint32_t log_location_items = 0;
@@ -704,8 +624,7 @@
                 lydjson_parse_name(meta_container->name.name, strlen(meta_container->name.name), &name, &name_len,
                         &prefix, &prefix_len, &is_attr);
                 assert(is_attr);
-                ret = lydjson_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, lyd_parent(*first_p), &snode);
-                assert(ret == LY_SUCCESS);
+                lydjson_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, lyd_parent(*first_p), &snode, &ext);
 
                 if (snode != node->schema) {
                     continue;
@@ -742,10 +661,10 @@
                         goto cleanup;
                     }
                 }
-                /* add/correct flags */
-                lyd_parse_set_data_flags(node, &lydctx->node_when, &node->meta, lydctx->parse_opts);
 
-                /* done */
+                /* add/correct flags */
+                ret = lyd_parse_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, ext);
+                LY_CHECK_GOTO(ret, cleanup);
                 break;
             }
         }
@@ -929,7 +848,8 @@
             LY_CHECK_GOTO(ret, cleanup);
 
             /* add/correct flags */
-            lyd_parse_set_data_flags(node, &lydctx->node_when, &meta, lydctx->parse_opts);
+            ret = lyd_parse_set_data_flags(node, &meta, (struct lyd_ctx *)lydctx, NULL);
+            LY_CHECK_GOTO(ret, cleanup);
         } else {
             /* create attribute */
             const char *module_name;
@@ -982,13 +902,19 @@
  * @param[in,out] first_p Pointer to the first sibling node in case of top-level.
  * @param[in,out] node_p pointer to the new node to insert, after the insert is done, pointer is set to NULL.
  * @param[in] last If set, always insert at the end.
+ * @param[in] ext Extension instance of @p node_p, if any.
  */
 static void
-lydjson_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last)
+lydjson_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last,
+        struct lysc_ext_instance *ext)
 {
     if (*node_p) {
         /* insert, keep first pointer correct */
-        lyd_insert_node(parent, first_p, *node_p, last);
+        if (ext) {
+            lyd_insert_ext(parent, *node_p);
+        } else {
+            lyd_insert_node(parent, first_p, *node_p, last);
+        }
         if (first_p) {
             if (parent) {
                 *first_p = lyd_child(parent);
@@ -1116,7 +1042,7 @@
 
         /* continue with the next instance */
         assert(node_p);
-        lydjson_maintain_children(parent, first_p, node_p, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
+        lydjson_maintain_children(parent, first_p, node_p, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, NULL);
         LY_CHECK_RET(lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p));
     }
 
@@ -1250,6 +1176,7 @@
  * @param[in] lydctx JSON data parser context. When the function returns, the context is in the same state
  * as before calling, despite it is necessary to process input data for checking.
  * @param[in] snode Schema node corresponding to the member currently being processed in the context.
+ * @param[in] ext Extension instance of @p snode, if any.
  * @param[in,out] status JSON parser status, is updated.
  * @param[out] node Parsed data (or opaque) node.
  * @return LY_SUCCESS if a node was successfully parsed,
@@ -1257,8 +1184,8 @@
  * @return LY_ERR on other errors.
  */
 static LY_ERR
-lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, enum LYJSON_PARSER_STATUS *status,
-        struct lyd_node **node)
+lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext,
+        enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
 {
     LY_ERR r;
     uint32_t prev_parse_opts, prev_int_opts;
@@ -1294,7 +1221,7 @@
          * process data as opaq nodes */
         prev_parse_opts = lydctx->parse_opts;
         lydctx->parse_opts &= ~LYD_PARSE_STRICT;
-        lydctx->parse_opts |= LYD_PARSE_OPAQ;
+        lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
         prev_int_opts = lydctx->int_opts;
         lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
 
@@ -1382,6 +1309,7 @@
  * @param[in] parent Data parent of the subtree, must be set if @p first is not.
  * @param[in,out] first_p Pointer to the variable holding the first top-level sibling, must be set if @p parent is not.
  * @param[in] snode Schema node corresponding to the member currently being processed in the context.
+ * @param[in] ext Extension instance of @p snode, if any.
  * @param[in] name Parsed JSON node name.
  * @param[in] name_len Lenght of @p name.
  * @param[in] prefix Parsed JSON node prefix.
@@ -1394,11 +1322,12 @@
  */
 static LY_ERR
 lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p,
-        const struct lysc_node *snode, const char *name, size_t name_len, const char *prefix, size_t prefix_len,
-        enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
+        const struct lysc_node *snode, struct lysc_ext_instance *ext, const char *name, size_t name_len,
+        const char *prefix, size_t prefix_len, enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
 {
     LY_ERR ret;
     uint32_t type_hints = 0;
+    uint32_t prev_parse_opts;
 
     ret = lydjson_data_check_opaq(lydctx, snode, &type_hints);
     if (ret == LY_SUCCESS) {
@@ -1429,6 +1358,12 @@
 
             LOG_LOCSET(snode, *node, NULL, NULL);
 
+            prev_parse_opts = lydctx->parse_opts;
+            if (ext) {
+                /* only parse these extension data and validate afterwards */
+                lydctx->parse_opts |= LYD_PARSE_ONLY;
+            }
+
             /* process children */
             while ((*status != LYJSON_OBJECT_CLOSED) && (*status != LYJSON_OBJECT_EMPTY)) {
                 ret = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
@@ -1436,6 +1371,9 @@
                 *status = lyjson_ctx_status(lydctx->jsonctx, 0);
             }
 
+            /* restore options */
+            lydctx->parse_opts = prev_parse_opts;
+
             /* finish linking metadata */
             ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node));
             LY_CHECK_ERR_RET(ret, LOG_LOCBACK(1, 1, 0, 0), ret);
@@ -1460,11 +1398,11 @@
             LOG_LOCBACK(1, 1, 0, 0);
         } else {
             /* create any node */
-            LY_CHECK_RET(lydjson_parse_any(lydctx, snode, status, node));
+            LY_CHECK_RET(lydjson_parse_any(lydctx, snode, ext, status, node));
         }
 
         /* add/correct flags */
-        lyd_parse_set_data_flags(*node, &lydctx->node_when, &(*node)->meta, lydctx->parse_opts);
+        lyd_parse_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext);
     } else if (ret == LY_ENOT) {
         /* parse it again as an opaq node */
         ret = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status, status, first_p, node);
@@ -1498,6 +1436,7 @@
     size_t name_len, prefix_len = 0;
     ly_bool is_meta = 0, parse_subtree;
     const struct lysc_node *snode = NULL;
+    struct lysc_ext_instance *ext;
     struct lyd_node *node = NULL, *attr_node = NULL;
     const struct ly_ctx *ctx = lydctx->jsonctx->ctx;
     char *value = NULL;
@@ -1515,7 +1454,7 @@
 
     if (!is_meta || name_len || prefix_len) {
         /* get the schema node */
-        r = lydjson_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, parent, &snode);
+        r = lydjson_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, parent, &snode, &ext);
         if (r == LY_ENOT) {
             /* data parsed */
             goto cleanup;
@@ -1582,15 +1521,14 @@
 
             /* process all the values/objects */
             do {
-                lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
-
-                ret = lydjson_parse_instance(lydctx, parent, first_p, snode, name, name_len, prefix, prefix_len,
+                ret = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
                         &status, &node);
                 if (ret == LY_ENOT) {
                     goto representation_error;
                 } else if (ret) {
                     goto cleanup;
                 }
+                lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, ext);
 
                 /* move after the item(s) */
                 LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
@@ -1615,7 +1553,8 @@
             }
 
             /* process the value/object */
-            ret = lydjson_parse_instance(lydctx, parent, first_p, snode, name, name_len, prefix, prefix_len, &status, &node);
+            ret = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
+                    &status, &node);
             if (ret == LY_ENOT) {
                 goto representation_error;
             } else if (ret) {
@@ -1631,7 +1570,7 @@
     }
 
     /* finally connect the parsed node */
-    lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
+    lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, ext);
 
     /* rememeber a successfully parsed node */
     if (parsed) {
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 74bdf6e..fc7f3d8 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -1,9 +1,10 @@
 /**
  * @file parser_xml.c
  * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
  * @brief XML data parser for libyang
  *
- * Copyright (c) 2019 CESNET, z.s.p.o.
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
  *
  * This source code is licensed under BSD 3-Clause License (the "License").
  * You may not use this file except in compliance with the License.
@@ -410,184 +411,77 @@
 }
 
 /**
- * @brief Try to parse data with a parent based on an extension instance.
+ * @brief Get schema node for the current element.
  *
  * @param[in] lydctx XML data parser context.
- * @param[in,out] parent Parent node where the children are inserted.
+ * @param[in] parent Parsed parent data node, if any.
+ * @param[in] prefix Element prefix, if any.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in] name Element name.
+ * @param[in] name_len Length of @p name.
+ * @param[out] snode Found schema node, NULL if no suitable was found.
+ * @param[out] ext Extension instance that provided @p snode, if any.
  * @return LY_SUCCESS on success;
- * @return LY_ENOT if no extension instance parsed the data;
  * @return LY_ERR on error.
  */
 static LY_ERR
-lydxml_nested_ext(struct lyd_xml_ctx *lydctx, struct lyd_node *parent)
+lydxml_subtree_snode(struct lyd_xml_ctx *lydctx, const struct lyd_node *parent, const char *prefix, size_t prefix_len,
+        const char *name, size_t name_len, const struct lysc_node **snode, struct lysc_ext_instance **ext)
 {
     LY_ERR r;
-    struct ly_in in_bck, in_start, in_ext;
-    LY_ARRAY_COUNT_TYPE u;
-    struct lysc_ext_instance *nested_exts = NULL;
-    lyplg_ext_data_parse_clb ext_parse_cb;
-    struct lyd_ctx_ext_val *ext_val;
-
-    /* backup current input */
-    in_bck = *lydctx->xmlctx->in;
-
-    /* go back in the input for extension parsing */
-    in_start = *lydctx->xmlctx->in;
-    if (lydctx->xmlctx->status != LYXML_ELEM_CONTENT) {
-        assert((lydctx->xmlctx->status == LYXML_ELEMENT) || (lydctx->xmlctx->status == LYXML_ATTRIBUTE));
-        in_start.current = lydctx->xmlctx->prefix ? lydctx->xmlctx->prefix : lydctx->xmlctx->name;
-    }
-    do {
-        --in_start.current;
-    } while (in_start.current[0] != '<');
-
-    /* check if there are any nested extension instances */
-    if (parent && parent->schema) {
-        nested_exts = parent->schema->exts;
-    }
-    LY_ARRAY_FOR(nested_exts, u) {
-        if (!nested_exts[u].def->plugin->parse) {
-            /* not an extension with parsed data */
-            continue;
-        }
-
-        /* prepare the input and try to parse this extension data */
-        in_ext = in_start;
-        ext_parse_cb = nested_exts[u].def->plugin->parse;
-        r = ext_parse_cb(&in_ext, LYD_XML, &nested_exts[u], parent, lydctx->parse_opts | LYD_PARSE_ONLY);
-        if (!r) {
-            /* data successfully parsed, remember for validation */
-            if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
-                ext_val = malloc(sizeof *ext_val);
-                LY_CHECK_ERR_RET(!ext_val, LOGMEM(lydctx->xmlctx->ctx), LY_EMEM);
-                ext_val->ext = &nested_exts[u];
-                ext_val->sibling = lyd_child_no_keys(parent);
-                LY_CHECK_RET(ly_set_add(&lydctx->ext_val, ext_val, 1, NULL));
-            }
-
-            /* adjust the xmlctx accordingly */
-            *lydctx->xmlctx->in = in_ext;
-            lydctx->xmlctx->status = LYXML_ELEM_CONTENT;
-            lydctx->xmlctx->dynamic = 0;
-            ly_set_rm_index(&lydctx->xmlctx->elements, lydctx->xmlctx->elements.count - 1, free);
-            lyxml_ns_rm(lydctx->xmlctx);
-            LY_CHECK_RET(lyxml_ctx_next(lydctx->xmlctx));
-            return LY_SUCCESS;
-        } else if (r != LY_ENOT) {
-            /* fatal error */
-            return r;
-        }
-        /* data was not from this module, continue */
-    }
-
-    /* no extensions or none matched, restore input */
-    *lydctx->xmlctx->in = in_bck;
-    return LY_ENOT;
-}
-
-/**
- * @brief Parse XML subtree.
- *
- * @param[in] lydctx XML YANG data parser context.
- * @param[in,out] parent Parent node where the children are inserted. NULL in case of parsing top-level elements.
- * @param[in,out] first_p Pointer to the first (@p parent or top-level) child. In case there were already some siblings,
- * this may point to a previously existing node.
- * @param[in,out] parsed Optional set to add all the parsed siblings into.
- * @return LY_ERR value.
- */
-static LY_ERR
-lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
-{
-    LY_ERR ret = LY_SUCCESS, r;
-    const char *prefix, *name;
-    size_t prefix_len, name_len;
     struct lyxml_ctx *xmlctx;
     const struct ly_ctx *ctx;
     const struct lyxml_ns *ns;
-    struct lyd_meta *meta = NULL;
-    struct lyd_attr *attr = NULL;
-    const struct lysc_node *snode;
     struct lys_module *mod;
-    uint32_t prev_parse_opts, prev_int_opts;
-    struct lyd_node *node = NULL, *anchor;
-    void *val_prefix_data = NULL;
-    LY_VALUE_FORMAT format;
     uint32_t getnext_opts;
-    ly_bool parse_subtree;
-    char *val;
-
-    assert(parent || first_p);
 
     xmlctx = lydctx->xmlctx;
     ctx = xmlctx->ctx;
     getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
 
-    parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0;
-    /* all descendants should be parsed */
-    lydctx->parse_opts &= ~LYD_PARSE_SUBTREE;
+    *snode = NULL;
+    *ext = NULL;
 
-    assert(xmlctx->status == LYXML_ELEMENT);
-
-    /* remember element prefix and name */
-    prefix = xmlctx->prefix;
-    prefix_len = xmlctx->prefix_len;
-    name = xmlctx->name;
-    name_len = xmlctx->name_len;
-
-    /* get the element module */
+    /* get current namespace */
     ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
     if (!ns) {
         LOGVAL(ctx, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", (int)prefix_len, prefix);
-        ret = LY_EVALID;
-        goto error;
+        return LY_EVALID;
     }
-    mod = ly_ctx_get_module_implemented_ns(ctx, ns->uri);
+
+    /* get the element module, use parent context if possible because of extensions */
+    mod = ly_ctx_get_module_implemented_ns(parent ? LYD_CTX(parent) : ctx, ns->uri);
     if (!mod) {
         /* check for extension data */
-        r = lydxml_nested_ext(lydctx, parent);
-        if (!r) {
-            /* successfully parsed */
+        r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_XML, &lydctx->xmlctx->ns, name, name_len,
+                snode, ext);
+        if (r != LY_ENOT) {
+            /* success or error */
             return r;
-        } else if (r != LY_ENOT) {
-            /* error */
-            ret = r;
-            goto error;
         }
 
         /* unknown module */
         if (lydctx->parse_opts & LYD_PARSE_STRICT) {
             LOGVAL(ctx, LYVE_REFERENCE, "No module with namespace \"%s\" in the context.", ns->uri);
-            ret = LY_EVALID;
-            goto error;
+            return LY_EVALID;
         }
-        if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
-            /* skip element with children */
-            LY_CHECK_GOTO(ret = lydxml_data_skip(xmlctx), error);
-            return LY_SUCCESS;
-        }
+        return LY_SUCCESS;
     }
 
-    /* parser next */
-    LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
-
     /* get the schema node */
-    snode = NULL;
     if (mod) {
         if (!parent && lydctx->ext) {
-            snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
+            *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
         } else {
-            snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
+            *snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
         }
-        if (!snode) {
+        if (!*snode) {
             /* check for extension data */
-            r = lydxml_nested_ext(lydctx, parent);
-            if (!r) {
-                /* successfully parsed */
+            r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_XML, &lydctx->xmlctx->ns, name,
+                    name_len, snode, ext);
+            if (r != LY_ENOT) {
+                /* success or error */
                 return r;
-            } else if (r != LY_ENOT) {
-                /* error */
-                ret = r;
-                goto error;
             }
 
             /* unknown data node */
@@ -607,22 +501,81 @@
                     LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
                             (int)name_len, name, mod->name);
                 }
-                ret = LY_EVALID;
-                goto error;
-            } else if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
-                LOGVRB("Skipping parsing of unknown node \"%.*s\".", name_len, name);
-
-                /* skip element with children */
-                LY_CHECK_GOTO(ret = lydxml_data_skip(xmlctx), error);
-                return LY_SUCCESS;
+                return LY_EVALID;
             }
+            return LY_SUCCESS;
         } else {
             /* check that schema node is valid and can be used */
-            LY_CHECK_GOTO(ret = lyd_parser_check_schema((struct lyd_ctx *)lydctx, snode), error);
-            LY_CHECK_GOTO(ret = lydxml_data_check_opaq(lydctx, &snode), error);
+            LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode));
+            LY_CHECK_RET(lydxml_data_check_opaq(lydctx, snode));
         }
     }
 
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse XML subtree.
+ *
+ * @param[in] lydctx XML YANG data parser context.
+ * @param[in,out] parent Parent node where the children are inserted. NULL in case of parsing top-level elements.
+ * @param[in,out] first_p Pointer to the first (@p parent or top-level) child. In case there were already some siblings,
+ * this may point to a previously existing node.
+ * @param[in,out] parsed Optional set to add all the parsed siblings into.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
+{
+    LY_ERR ret = LY_SUCCESS;
+    const char *prefix, *name;
+    size_t prefix_len, name_len;
+    struct lyxml_ctx *xmlctx;
+    const struct ly_ctx *ctx;
+    const struct lyxml_ns *ns;
+    struct lyd_meta *meta = NULL;
+    struct lyd_attr *attr = NULL;
+    const struct lysc_node *snode;
+    struct lysc_ext_instance *ext;
+    uint32_t prev_parse_opts, orig_parse_opts, prev_int_opts;
+    struct lyd_node *node = NULL, *anchor;
+    void *val_prefix_data = NULL;
+    LY_VALUE_FORMAT format;
+    ly_bool parse_subtree;
+    char *val;
+
+    assert(parent || first_p);
+
+    xmlctx = lydctx->xmlctx;
+    ctx = xmlctx->ctx;
+
+    parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0;
+    /* all descendants should be parsed */
+    lydctx->parse_opts &= ~LYD_PARSE_SUBTREE;
+    orig_parse_opts = lydctx->parse_opts;
+
+    assert(xmlctx->status == LYXML_ELEMENT);
+
+    /* remember element prefix and name */
+    prefix = xmlctx->prefix;
+    prefix_len = xmlctx->prefix_len;
+    name = xmlctx->name;
+    name_len = xmlctx->name_len;
+
+    /* parser next */
+    LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+
+    /* get the schema node */
+    LY_CHECK_GOTO(ret = lydxml_subtree_snode(lydctx, parent, prefix, prefix_len, name, name_len, &snode, &ext), error);
+
+    if (!snode && !(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
+        LOGVRB("Skipping parsing of unknown node \"%.*s\".", name_len, name);
+
+        /* skip element with children */
+        LY_CHECK_GOTO(ret = lydxml_data_skip(xmlctx), error);
+        return LY_SUCCESS;
+    }
+
     /* create metadata/attributes */
     if (xmlctx->status == LYXML_ATTRIBUTE) {
         if (snode) {
@@ -720,12 +673,21 @@
         /* parser next */
         LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
 
+        prev_parse_opts = lydctx->parse_opts;
+        if (ext) {
+            /* only parse these extension data and validate afterwards */
+            lydctx->parse_opts |= LYD_PARSE_ONLY;
+        }
+
         /* process children */
         while (xmlctx->status == LYXML_ELEMENT) {
             ret = lydxml_subtree_r(lydctx, node, lyd_node_child_p(node), NULL);
             LY_CHECK_GOTO(ret, error);
         }
 
+        /* restore options */
+        lydctx->parse_opts = prev_parse_opts;
+
         if (snode->nodetype == LYS_LIST) {
             /* check all keys exist */
             LY_CHECK_GOTO(ret = lyd_parse_check_keys(node), error);
@@ -773,7 +735,7 @@
             /* update options so that generic data can be parsed */
             prev_parse_opts = lydctx->parse_opts;
             lydctx->parse_opts &= ~LYD_PARSE_STRICT;
-            lydctx->parse_opts |= LYD_PARSE_OPAQ;
+            lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
             prev_int_opts = lydctx->int_opts;
             lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
 
@@ -801,7 +763,7 @@
 
     /* add/correct flags */
     if (snode) {
-        lyd_parse_set_data_flags(node, &lydctx->node_when, &meta, lydctx->parse_opts);
+        LY_CHECK_GOTO(ret = lyd_parse_set_data_flags(node, &meta, (struct lyd_ctx *)lydctx, ext), error);
     }
 
     /* parser next */
@@ -818,7 +780,11 @@
     }
 
     /* insert, keep first pointer correct */
-    lyd_insert_node(parent, first_p, node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
+    if (ext) {
+        LY_CHECK_GOTO(ret = lyd_insert_ext(parent, node), error);
+    } else {
+        lyd_insert_node(parent, first_p, node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
+    }
     while (!parent && (*first_p)->prev->next) {
         *first_p = (*first_p)->prev;
     }
@@ -828,10 +794,12 @@
         ly_set_add(parsed, node, 1, NULL);
     }
 
+    lydctx->parse_opts = orig_parse_opts;
     LOG_LOCBACK(node ? 1 : 0, node ? 1 : 0, 0, 0);
     return LY_SUCCESS;
 
 error:
+    lydctx->parse_opts = orig_parse_opts;
     LOG_LOCBACK(node ? 1 : 0, node ? 1 : 0, 0, 0);
     lyd_free_meta_siblings(meta);
     lyd_free_attr_siblings(ctx, attr);
diff --git a/src/path.c b/src/path.c
index 932a039..f53768b 100644
--- a/src/path.c
+++ b/src/path.c
@@ -374,33 +374,42 @@
 }
 
 /**
- * @brief Parse prefix from a NameTest, if any, and node name, and return expected module of the node.
+ * @brief Parse NameTest and get the corresponding schema node.
  *
  * @param[in] ctx libyang context.
  * @param[in] cur_node Optional current (original context) node.
  * @param[in] cur_mod Current module of the path (where the path is "instantiated"). Needed for ::LY_VALUE_SCHEMA
  * and ::LY_VALUE_SCHEMA_RESOLVED.
- * @param[in] prev_ctx_node Previous context node. Needed for ::LY_VALUE_JSON.
+ * @param[in] prev_ctx_node Previous context node.
  * @param[in] expr Parsed path.
  * @param[in] tok_idx Index in @p expr.
  * @param[in] format Format of the path.
  * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
- * @param[out] mod Resolved module.
- * @param[out] name Parsed node name.
- * @param[out] name_len Length of @p name.
+ * @param[in] top_ext Optional top-level extension to use for searching the schema node.
+ * @param[in] getnext_opts Options to be used for ::lys_getnext() calls.
+ * @param[out] snode Resolved schema node.
+ * @param[out] ext Optional extension instance of @p snode, if any.
  * @return LY_ERR value.
  */
 static LY_ERR
-ly_path_compile_prefix(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod,
+ly_path_compile_snode(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod,
         const struct lysc_node *prev_ctx_node, const struct lyxp_expr *expr, uint16_t tok_idx, LY_VALUE_FORMAT format,
-        void *prefix_data, const struct lys_module **mod, const char **name, size_t *name_len)
+        void *prefix_data, const struct lysc_ext_instance *top_ext, uint32_t getnext_opts, const struct lysc_node **snode,
+        struct lysc_ext_instance **ext)
 {
     LY_ERR ret;
-    const char *pref;
-    size_t len;
+    const struct lys_module *mod = NULL;
+    struct lysc_ext_instance *e = NULL;
+    const char *pref, *name;
+    size_t len, name_len;
 
     assert(expr->tokens[tok_idx] == LYXP_TOKEN_NAMETEST);
 
+    *snode = NULL;
+    if (ext) {
+        *ext = NULL;
+    }
+
     /* get prefix */
     if ((pref = strnstr(expr->expr + expr->tok_pos[tok_idx], ":", expr->tok_len[tok_idx]))) {
         len = pref - (expr->expr + expr->tok_pos[tok_idx]);
@@ -409,18 +418,37 @@
         len = 0;
     }
 
-    /* find next node module */
+    /* set name */
+    if (pref) {
+        name = pref + len + 1;
+        name_len = expr->tok_len[tok_idx] - len - 1;
+    } else {
+        name = expr->expr + expr->tok_pos[tok_idx];
+        name_len = expr->tok_len[tok_idx];
+    }
+
+    /* find node module */
     if (pref) {
         LOG_LOCSET(cur_node, NULL, NULL, NULL);
 
-        *mod = ly_resolve_prefix(ctx, pref, len, format, prefix_data);
-        if (!*mod) {
+        mod = ly_resolve_prefix(ctx, pref, len, format, prefix_data);
+        if ((!mod || !mod->implemented) && prev_ctx_node) {
+            /* check for nested ext data */
+            ret = ly_nested_ext_schema(NULL, prev_ctx_node, pref, len, format, prefix_data, name, name_len, snode, &e);
+            if (!ret) {
+                goto success;
+            } else if (ret != LY_ENOT) {
+                goto error;
+            }
+        }
+
+        if (!mod) {
             LOGVAL(ctx, LYVE_XPATH, "No module connected with the prefix \"%.*s\" found (prefix format %s).",
                     (int)len, pref, ly_format2str(format));
             ret = LY_EVALID;
             goto error;
-        } else if (!(*mod)->implemented) {
-            LOGVAL(ctx, LYVE_XPATH, "Not implemented module \"%s\" in path.", (*mod)->name);
+        } else if (!mod->implemented) {
+            LOGVAL(ctx, LYVE_XPATH, "Not implemented module \"%s\" in path.", mod->name);
             ret = LY_EVALID;
             goto error;
         }
@@ -434,7 +462,7 @@
                 LOGINT_RET(ctx);
             }
             /* use current module */
-            *mod = cur_mod;
+            mod = cur_mod;
             break;
         case LY_VALUE_JSON:
         case LY_VALUE_LYB:
@@ -442,7 +470,7 @@
                 LOGINT_RET(ctx);
             }
             /* inherit module of the previous node */
-            *mod = prev_ctx_node->module;
+            mod = prev_ctx_node->module;
             break;
         case LY_VALUE_CANON:
         case LY_VALUE_XML:
@@ -452,15 +480,25 @@
         }
     }
 
-    /* set name */
-    if (pref) {
-        *name = pref + len + 1;
-        *name_len = expr->tok_len[tok_idx] - len - 1;
+    /* find schema node */
+    if (!prev_ctx_node && top_ext) {
+        *snode = lysc_ext_find_node(top_ext, mod, name, name_len, 0, getnext_opts);
     } else {
-        *name = expr->expr + expr->tok_pos[tok_idx];
-        *name_len = expr->tok_len[tok_idx];
+        *snode = lys_find_child(prev_ctx_node, mod, name, name_len, 0, getnext_opts);
+        if (!(*snode) && prev_ctx_node) {
+            ret = ly_nested_ext_schema(NULL, prev_ctx_node, pref, len, format, prefix_data, name, name_len, snode, &e);
+            LY_CHECK_RET(ret && (ret != LY_ENOT), ret);
+        }
+    }
+    if (!(*snode)) {
+        LOGVAL(ctx, LYVE_XPATH, "Not found node \"%.*s\" in path.", (int)name_len, name);
+        return LY_ENOTFOUND;
     }
 
+success:
+    if (ext) {
+        *ext = e;
+    }
     return LY_SUCCESS;
 
 error:
@@ -476,9 +514,8 @@
     LY_ERR ret = LY_SUCCESS;
     struct ly_path_predicate *p;
     const struct lysc_node *key;
-    const struct lys_module *mod = NULL;
-    const char *name, *val;
-    size_t name_len, val_len, key_count;
+    const char *val;
+    size_t val_len, key_count;
 
     assert(ctx && ctx_node);
 
@@ -506,14 +543,9 @@
 
         do {
             /* NameTest, find the key */
-            LY_CHECK_RET(ly_path_compile_prefix(ctx, cur_node, cur_mod, ctx_node, expr, *tok_idx, format, prefix_data,
-                    &mod, &name, &name_len));
-            key = lys_find_child(ctx_node, mod, name, name_len, 0, 0);
-            if (!key) {
-                LOGVAL(ctx, LYVE_XPATH, "Not found node \"%.*s\" in path.", (int)name_len, name);
-                ret = LY_ENOTFOUND;
-                goto cleanup;
-            } else if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) {
+            LY_CHECK_RET(ly_path_compile_snode(ctx, cur_node, cur_mod, ctx_node, expr, *tok_idx, format, prefix_data,
+                    NULL, 0, &key, NULL));
+            if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) {
                 LOGVAL(ctx, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.", lys_nodetype2str(key->nodetype),
                         key->name);
                 ret = LY_EVALID;
@@ -638,7 +670,7 @@
         LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup);
 
         /* syntax was already checked */
-        p->position = strtoull(expr->expr + expr->tok_pos[*tok_idx], (char **)&name, LY_BASE_DEC);
+        p->position = strtoull(expr->expr + expr->tok_pos[*tok_idx], (char **)&val, LY_BASE_DEC);
         ++(*tok_idx);
 
         /* ']' */
@@ -668,9 +700,6 @@
 {
     LY_ERR ret = LY_SUCCESS;
     const struct lysc_node *key, *node, *node2;
-    const struct lys_module *mod;
-    const char *name;
-    size_t name_len;
     struct ly_ctx *ctx = cur_node->module->ctx;
 
     LOG_LOCSET(cur_node, NULL, NULL, NULL);
@@ -694,15 +723,10 @@
 
     do {
         /* NameTest, find the key */
-        ret = ly_path_compile_prefix(ctx, cur_node, cur_node->module, ctx_node, expr, *tok_idx, format, prefix_data,
-                &mod, &name, &name_len);
+        ret = ly_path_compile_snode(ctx, cur_node, cur_node->module, ctx_node, expr, *tok_idx, format, prefix_data,
+                NULL, 0, &key, NULL);
         LY_CHECK_GOTO(ret, cleanup);
-        key = lys_find_child(ctx_node, mod, name, name_len, 0, 0);
-        if (!key) {
-            LOGVAL(ctx, LYVE_XPATH, "Not found node \"%.*s\" in path.", (int)name_len, name);
-            ret = LY_EVALID;
-            goto cleanup;
-        } else if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) {
+        if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) {
             LOGVAL(ctx, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.",
                     lys_nodetype2str(key->nodetype), key->name);
             ret = LY_EVALID;
@@ -757,14 +781,8 @@
 
             /* NameTest */
             assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST);
-            LY_CHECK_RET(ly_path_compile_prefix(ctx, cur_node, cur_node->module, node, expr, *tok_idx, format,
-                    prefix_data, &mod, &name, &name_len));
-            node2 = lys_find_child(node, mod, name, name_len, 0, 0);
-            if (!node2) {
-                LOGVAL(ctx, LYVE_XPATH, "Not found node \"%.*s\" in path.", (int)name_len, name);
-                ret = LY_EVALID;
-                goto cleanup;
-            }
+            LY_CHECK_RET(ly_path_compile_snode(ctx, cur_node, cur_node->module, node, expr, *tok_idx, format,
+                    prefix_data, NULL, 0, &node2, NULL));
             node = node2;
             ++(*tok_idx);
         } while ((*tok_idx + 1 < expr->used) && (expr->tokens[*tok_idx + 1] == LYXP_TOKEN_NAMETEST));
@@ -789,7 +807,7 @@
 
 cleanup:
     LOG_LOCBACK(1, 0, 0, 0);
-    return ret;
+    return (ret == LY_ENOTFOUND) ? LY_EVALID : ret;
 }
 
 /**
@@ -799,7 +817,7 @@
  * @param[in] cur_mod Current module of the path (where it was "instantiated"), ignored of @p lref. Used for nodes
  * without a prefix for ::LY_VALUE_SCHEMA and ::LY_VALUE_SCHEMA_RESOLVED format.
  * @param[in] ctx_node Optional context node, mandatory of @p lref.
- * @param[in] ext Extension instance containing the definition of the data being created. It is used to find the top-level
+ * @param[in] top_ext Extension instance containing the definition of the data being created. It is used to find the top-level
  * node inside the extension instance instead of a module. Note that this is the case not only if the @p ctx_node is NULL,
  * but also if the relative path starting in @p ctx_node reaches the document root via double dots.
  * @param[in] expr Parsed path.
@@ -816,17 +834,15 @@
  */
 static LY_ERR
 _ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
-        const struct lysc_ext_instance *ext, const struct lyxp_expr *expr, ly_bool lref, uint8_t oper, uint8_t target,
+        const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, ly_bool lref, uint8_t oper, uint8_t target,
         ly_bool limit_access_tree, LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path)
 {
     LY_ERR ret = LY_SUCCESS;
     uint16_t tok_idx = 0;
-    const struct lys_module *mod = NULL;
     const struct lysc_node *node2, *cur_node, *op;
     struct ly_path *p = NULL;
+    struct lysc_ext_instance *ext = NULL;
     uint32_t getnext_opts;
-    const char *name;
-    size_t name_len;
 
     assert(ctx);
     assert(!lref || ctx_node);
@@ -895,19 +911,12 @@
         /* NameTest */
         LY_CHECK_ERR_GOTO(lyxp_check_token(ctx, expr, tok_idx, LYXP_TOKEN_NAMETEST), ret = LY_EVALID, cleanup);
 
-        /* get module and node name */
-        LY_CHECK_GOTO(ret = ly_path_compile_prefix(ctx, cur_node, cur_mod, ctx_node, expr, tok_idx, format, prefix_data,
-                &mod, &name, &name_len), cleanup);
+        /* get schema node */
+        LY_CHECK_GOTO(ret = ly_path_compile_snode(ctx, cur_node, cur_mod, ctx_node, expr, tok_idx, format, prefix_data,
+                top_ext, getnext_opts, &node2, &ext), cleanup);
         ++tok_idx;
-
-        /* find the next node */
-        if (!ctx_node && ext) {
-            node2 = lysc_ext_find_node(ext, mod, name, name_len, 0, getnext_opts);
-        } else {
-            node2 = lys_find_child(ctx_node, mod, name, name_len, 0, getnext_opts);
-        }
-        if (!node2 || (op && (node2->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && (node2 != op))) {
-            LOGVAL(ctx, LYVE_XPATH, "Not found node \"%.*s\" in path.", (int)name_len, name);
+        if ((op && (node2->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && (node2 != op))) {
+            LOGVAL(ctx, LYVE_XPATH, "Not found node \"%s\" in path.", node2->name);
             ret = LY_EVALID;
             goto cleanup;
         }
@@ -916,6 +925,7 @@
         /* new path segment */
         LY_ARRAY_NEW_GOTO(ctx, *path, p, ret, cleanup);
         p->node = ctx_node;
+        p->ext = ext;
 
         /* compile any predicates */
         if (lref) {
@@ -948,24 +958,24 @@
         *path = NULL;
     }
     LOG_LOCBACK(1, 0, 0, 0);
-    return ret;
+    return (ret == LY_ENOTFOUND) ? LY_EVALID : ret;
 }
 
 LY_ERR
 ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
-        const struct lysc_ext_instance *ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
+        const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
         ly_bool limit_access_tree, LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path)
 {
-    return _ly_path_compile(ctx, cur_mod, ctx_node, ext, expr, 0, oper, target, limit_access_tree, format, prefix_data,
-            path);
+    return _ly_path_compile(ctx, cur_mod, ctx_node, top_ext, expr, 0, oper, target, limit_access_tree, format,
+            prefix_data, path);
 }
 
 LY_ERR
-ly_path_compile_leafref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const struct lysc_ext_instance *ext,
+ly_path_compile_leafref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const struct lysc_ext_instance *top_ext,
         const struct lyxp_expr *expr, uint8_t oper, uint8_t target, LY_VALUE_FORMAT format, void *prefix_data,
         struct ly_path **path)
 {
-    return _ly_path_compile(ctx, ctx_node->module, ctx_node, ext, expr, 1, oper, target, 1, format, prefix_data, path);
+    return _ly_path_compile(ctx, ctx_node->module, ctx_node, top_ext, expr, 1, oper, target, 1, format, prefix_data, path);
 }
 
 LY_ERR
diff --git a/src/path.h b/src/path.h
index d3dca9e..c079274 100644
--- a/src/path.h
+++ b/src/path.h
@@ -58,8 +58,9 @@
     const struct lysc_node *node; /**< Schema node representing the path segment, first node has special meaning:
                                        - is a top-level node - path is absolute,
                                        - is inner node - path is relative */
-    struct ly_path_predicate *predicates;  /**< [Sized array](@ref sizedarrays) of the path segment's predicates */
-    enum ly_path_pred_type pred_type;   /**< Predicate type (see YANG ABNF) */
+    const struct lysc_ext_instance *ext;    /**< Extension instance of @p node, if any */
+    struct ly_path_predicate *predicates;   /**< [Sized array](@ref sizedarrays) of the path segment's predicates */
+    enum ly_path_pred_type pred_type;       /**< Predicate type (see YANG ABNF) */
 };
 
 /**
@@ -147,7 +148,7 @@
  * @param[in] cur_mod Current module of the path (where it was "instantiated"). Used for nodes in schema-nodeid
  * without a prefix for ::LY_VALUE_SCHEMA and ::LY_VALUE_SCHEMA_RESOLVED format.
  * @param[in] ctx_node Optional context node.
- * @param[in] ext Extension instance containing the definition of the data being created. It is used to find the top-level
+ * @param[in] top_ext Extension instance containing the definition of the data being created. It is used to find the top-level
  * node inside the extension instance instead of a module. Note that this is the case not only if the @p ctx_node is NULL,
  * but also if the relative path starting in @p ctx_node reaches the document root via double dots.
  * @param[in] expr Parsed path.
@@ -160,7 +161,7 @@
  * @return LY_ERR value.
  */
 LY_ERR ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
-        const struct lysc_ext_instance *ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
+        const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
         ly_bool limit_access_tree, LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path);
 
 /**
@@ -168,7 +169,7 @@
  *
  * @param[in] ctx libyang context.
  * @param[in] ctx_node Context node.
- * @param[in] ext Extension instance containing the definition of the data being created. It is used to find the top-level
+ * @param[in] top_ext Extension instance containing the definition of the data being created. It is used to find the top-level
  * node inside the extension instance instead of a module. Note that this is the case not only if the @p ctx_node is NULL,
  * but also if the relative path starting in @p ctx_node reaches the document root via double dots.
  * @param[in] expr Parsed path.
@@ -180,7 +181,7 @@
  * @return LY_ERR value.
  */
 LY_ERR ly_path_compile_leafref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
-        const struct lysc_ext_instance *ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
+        const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
         LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path);
 
 /**
diff --git a/src/plugins_exts.h b/src/plugins_exts.h
index f521d7d..d8016eb 100644
--- a/src/plugins_exts.h
+++ b/src/plugins_exts.h
@@ -100,7 +100,7 @@
 /**
  * @brief Extensions API version
  */
-#define LYPLG_EXT_API_VERSION 2
+#define LYPLG_EXT_API_VERSION 3
 
 /**
  * @brief Macro to define plugin information in external plugins
@@ -159,22 +159,26 @@
 typedef void (*lyplg_ext_free_clb)(struct ly_ctx *ctx, struct lysc_ext_instance *ext);
 
 /**
- * @brief Callback for parsing YANG instance data described by an extension instance.
+ * @brief Callback for getting a schema node for a new YANG instance data described by an extension instance.
+ * Needed only if the extension instance supports some nested standard YANG data.
  *
- * This callback is used only for nested data definition (with a standard YANG schema parent).
- * Note that the siblings parsed by this function and directly connected to @p parent must have the flag ::LYD_EXT set.
- *
- * @param[in] in Input handler with the data to parse.
- * @param[in] format Format if the data in @p in.
  * @param[in] ext Compiled extension instance.
- * @param[in,out] parent Data parent to append to.
- * @param[in] parse_opts Parse options, see @ref dataparseroptions. They will always include ::LYD_PARSE_ONLY.
+ * @param[in] parent Parsed parent data node. Set if @p sparent is NULL.
+ * @param[in] sparent Schema parent node. Set if @p parent is NULL.
+ * @param[in] prefix Element prefix, if any.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in] format Format of @p prefix.
+ * @param[in] prefix_data Format-specific prefix data.
+ * @param[in] name Element name.
+ * @param[in] name_len Length of @p name.
+ * @param[out] snode Schema node to use for parsing the node.
  * @return LY_SUCCESS on success.
  * @return LY_ENOT if the data are not described by @p ext.
  * @return LY_ERR on error.
  */
-typedef LY_ERR (*lyplg_ext_data_parse_clb)(struct ly_in *in, LYD_FORMAT format, struct lysc_ext_instance *ext,
-        struct lyd_node *parent, uint32_t parse_opts);
+typedef LY_ERR (*lyplg_ext_data_snode_clb)(struct lysc_ext_instance *ext, const struct lyd_node *parent,
+        const struct lysc_node *sparent, const char *prefix, size_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data,
+        const char *name, size_t name_len, const struct lysc_node **snode);
 
 /**
  * @brief Callback for validating parsed YANG instance data described by an extension instance.
@@ -200,7 +204,7 @@
                                                  instance */
     lyplg_ext_free_clb free;                /**< free the extension-specific data created by its compilation */
 
-    lyplg_ext_data_parse_clb parse;         /**< callback to parse data instance according to the extension definition */
+    lyplg_ext_data_snode_clb snode;         /**< callback to get schema node for nested YANG data */
     lyplg_ext_data_validate_clb validate;   /**< callback to validate parsed data instances according to the extension
                                                  definition */
 };
diff --git a/src/plugins_exts/metadata.c b/src/plugins_exts/metadata.c
index bc70694..eebb434 100644
--- a/src/plugins_exts/metadata.c
+++ b/src/plugins_exts/metadata.c
@@ -163,7 +163,7 @@
         .plugin.compile = &annotation_compile,
         .plugin.sprinter = &annotation_schema_printer,
         .plugin.free = annotation_free,
-        .plugin.parse = NULL,
+        .plugin.snode = NULL,
         .plugin.validate = NULL
     },
     {0}     /* terminating zeroed record */
diff --git a/src/plugins_exts/nacm.c b/src/plugins_exts/nacm.c
index d8e1ca8..aa0d0e8 100644
--- a/src/plugins_exts/nacm.c
+++ b/src/plugins_exts/nacm.c
@@ -160,7 +160,7 @@
         .plugin.compile = &nacm_compile,
         .plugin.sprinter = NULL,
         .plugin.free = NULL,
-        .plugin.parse = NULL,
+        .plugin.snode = NULL,
         .plugin.validate = NULL
     }, {
         .module = "ietf-netconf-acm",
@@ -171,7 +171,7 @@
         .plugin.compile = &nacm_compile,
         .plugin.sprinter = NULL,
         .plugin.free = NULL,
-        .plugin.parse = NULL,
+        .plugin.snode = NULL,
         .plugin.validate = NULL
     }, {
         .module = "ietf-netconf-acm",
@@ -182,7 +182,7 @@
         .plugin.compile = &nacm_compile,
         .plugin.sprinter = NULL,
         .plugin.free = NULL,
-        .plugin.parse = NULL,
+        .plugin.snode = NULL,
         .plugin.validate = NULL
     }, {
         .module = "ietf-netconf-acm",
@@ -193,7 +193,7 @@
         .plugin.compile = &nacm_compile,
         .plugin.sprinter = NULL,
         .plugin.free = NULL,
-        .plugin.parse = NULL,
+        .plugin.snode = NULL,
         .plugin.validate = NULL
     },
     {0} /* terminating zeroed item */
diff --git a/src/plugins_exts/schema_mount.c b/src/plugins_exts/schema_mount.c
index a3a582f..8c4dfa9 100644
--- a/src/plugins_exts/schema_mount.c
+++ b/src/plugins_exts/schema_mount.c
@@ -24,6 +24,7 @@
 #include "libyang.h"
 #include "log.h"
 #include "plugins_exts.h"
+#include "plugins_types.h"
 #include "tree_data.h"
 #include "tree_schema.h"
 
@@ -510,62 +511,32 @@
 }
 
 /**
- * @brief Parse callback for schema mount.
- * Check if data if valid for schema mount and inserts it to the parent.
+ * @brief Snode callback for schema mount.
+ * Check if data are valid for schema mount and returns their schema node.
  */
 static LY_ERR
-schema_mount_parse(struct ly_in *in, LYD_FORMAT format, struct lysc_ext_instance *ext, struct lyd_node *parent,
-        uint32_t parse_opts)
+schema_mount_snode(struct lysc_ext_instance *ext, const struct lyd_node *parent, const struct lysc_node *sparent,
+        const char *prefix, size_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data, const char *name, size_t name_len,
+        const struct lysc_node **snode)
 {
-    LY_ERR ret = LY_SUCCESS, r;
+    LY_ERR r;
+    const struct lys_module *mod;
     const struct ly_ctx *ext_ctx;
-    struct lyd_node *subtree, *first = NULL;
-    struct ly_err_item *err;
-    uint32_t old_log_opts;
 
     /* get context based on ietf-yang-library data */
     if ((r = schema_mount_get_ctx(ext, &ext_ctx))) {
         return r;
     }
 
-    /* prepare opts */
-    old_log_opts = ly_log_options(LY_LOSTORE_LAST);
-    assert(parse_opts & LYD_PARSE_ONLY);
-    parse_opts |= LYD_PARSE_SUBTREE;
-
-    do {
-        /* parse by nested subtrees */
-        r = lyd_parse_data(ext_ctx, NULL, in, format, parse_opts, 0, &subtree);
-        if (r && (r != LY_ENOT)) {
-            /* error, maybe valid, maybe not, print as verbose */
-            err = ly_err_first(ext_ctx);
-            if (!err) {
-                lyplg_ext_log(ext, LY_LLVRB, 0, NULL, "Unknown parsing error (err code %d).", r);
-            } else {
-                lyplg_ext_log(ext, LY_LLVRB, 0, NULL, "%s (err code %d).", err->msg, err->no);
-            }
-            ret = LY_ENOT;
-            goto cleanup;
-        }
-
-        /* set the special flag and insert into siblings */
-        subtree->flags |= LYD_EXT;
-        lyd_insert_sibling(first, subtree, &first);
-    } while (r == LY_ENOT);
-
-    /* append to parent */
-    if (first && (r = lyd_insert_ext(parent, first))) {
-        lyplg_ext_log(ext, LY_LLERR, r, NULL, "Failed to append parsed data.");
-        ret = r;
-        goto cleanup;
+    /* get the module */
+    mod = lyplg_type_identity_module(ext_ctx, parent ? parent->schema : sparent, prefix, prefix_len, format, prefix_data);
+    if (!mod) {
+        return LY_ENOT;
     }
 
-cleanup:
-    ly_log_options(old_log_opts);
-    if (ret) {
-        lyd_free_siblings(first);
-    }
-    return ret;
+    /* get the top-level schema node */
+    *snode = lys_find_child(NULL, mod, name, name_len, 0, 0);
+    return *snode ? LY_SUCCESS : LY_ENOT;
 }
 
 /**
@@ -818,7 +789,7 @@
         .plugin.compile = &schema_mount_compile,
         .plugin.sprinter = NULL,
         .plugin.free = &schema_mount_free,
-        .plugin.parse = &schema_mount_parse,
+        .plugin.snode = &schema_mount_snode,
         .plugin.validate = &schema_mount_validate
     },
     {0} /* terminating zeroed item */
diff --git a/src/plugins_exts/yangdata.c b/src/plugins_exts/yangdata.c
index 55a03e3..b2e6f62 100644
--- a/src/plugins_exts/yangdata.c
+++ b/src/plugins_exts/yangdata.c
@@ -174,7 +174,7 @@
         .plugin.compile = &yangdata_compile,
         .plugin.sprinter = &yangdata_schema_printer,
         .plugin.free = yangdata_free,
-        .plugin.parse = NULL,
+        .plugin.snode = NULL,
         .plugin.validate = NULL
     },
     {0}     /* terminating zeroed record */
diff --git a/src/tree_data.c b/src/tree_data.c
index b12580a..0bf1e2d 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -885,8 +885,10 @@
 lyd_new_inner(struct lyd_node *parent, const struct lys_module *module, const char *name, ly_bool output,
         struct lyd_node **node)
 {
+    LY_ERR r;
     struct lyd_node *ret = NULL;
     const struct lysc_node *schema;
+    struct lysc_ext_instance *ext = NULL;
     const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
 
     LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
@@ -898,9 +900,18 @@
 
     schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0,
             LYS_CONTAINER | LYS_NOTIF | LYS_RPC | LYS_ACTION, output ? LYS_GETNEXT_OUTPUT : 0);
-    LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Inner node (not a list) \"%s\" not found.", name), LY_ENOTFOUND);
+    if (!schema && parent) {
+        r = ly_nested_ext_schema(parent, NULL, module->name, strlen(module->name), LY_VALUE_JSON, NULL, name,
+                strlen(name), &schema, &ext);
+        LY_CHECK_RET(r && (r != LY_ENOT), r);
+    }
+    LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Inner node (container, notif, RPC, or action) \"%s\" not found.",
+            name), LY_ENOTFOUND);
 
     LY_CHECK_RET(lyd_create_inner(schema, &ret));
+    if (ext) {
+        ret->flags |= LYD_EXT;
+    }
     if (parent) {
         lyd_insert_node(parent, NULL, ret, 0);
     }
@@ -958,10 +969,11 @@
 {
     struct lyd_node *ret = NULL, *key;
     const struct lysc_node *schema, *key_s;
+    struct lysc_ext_instance *ext = NULL;
     const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
     const void *key_val;
     uint32_t key_len;
-    LY_ERR rc = LY_SUCCESS;
+    LY_ERR r, rc = LY_SUCCESS;
 
     LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
     LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
@@ -971,6 +983,11 @@
     }
 
     schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYS_LIST, output ? LYS_GETNEXT_OUTPUT : 0);
+    if (!schema && parent) {
+        r = ly_nested_ext_schema(parent, NULL, module->name, strlen(module->name), LY_VALUE_JSON, NULL, name,
+                strlen(name), &schema, &ext);
+        LY_CHECK_RET(r && (r != LY_ENOT), r);
+    }
     LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "List node \"%s\" not found.", name), LY_ENOTFOUND);
 
     /* create list inner node */
@@ -991,6 +1008,9 @@
         lyd_insert_node(ret, NULL, key, 1);
     }
 
+    if (ext) {
+        ret->flags |= LYD_EXT;
+    }
     if (parent) {
         lyd_insert_node(parent, NULL, ret, 0);
     }
@@ -1101,8 +1121,10 @@
 lyd_new_list2(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *keys,
         ly_bool output, struct lyd_node **node)
 {
+    LY_ERR r;
     struct lyd_node *ret = NULL;
     const struct lysc_node *schema;
+    struct lysc_ext_instance *ext = NULL;
     const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
 
     LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
@@ -1117,6 +1139,11 @@
 
     /* find schema node */
     schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYS_LIST, output ? LYS_GETNEXT_OUTPUT : 0);
+    if (!schema && parent) {
+        r = ly_nested_ext_schema(parent, NULL, module->name, strlen(module->name), LY_VALUE_JSON, NULL, name, strlen(name),
+                &schema, &ext);
+        LY_CHECK_RET(r && (r != LY_ENOT), r);
+    }
     LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "List node \"%s\" not found.", name), LY_ENOTFOUND);
 
     if ((schema->flags & LYS_KEYLESS) && !keys[0]) {
@@ -1126,6 +1153,9 @@
         /* create the list node */
         LY_CHECK_RET(lyd_create_list2(schema, keys, strlen(keys), &ret));
     }
+    if (ext) {
+        ret->flags |= LYD_EXT;
+    }
     if (parent) {
         lyd_insert_node(parent, NULL, ret, 0);
     }
@@ -1154,9 +1184,10 @@
 _lyd_new_term(struct lyd_node *parent, const struct lys_module *module, const char *name, const void *value,
         size_t value_len, LY_VALUE_FORMAT format, ly_bool output, struct lyd_node **node)
 {
-    LY_ERR rc;
+    LY_ERR r;
     struct lyd_node *ret = NULL;
     const struct lysc_node *schema;
+    struct lysc_ext_instance *ext = NULL;
     const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
 
     LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
@@ -1167,10 +1198,17 @@
     }
 
     schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYD_NODE_TERM, output ? LYS_GETNEXT_OUTPUT : 0);
+    if (!schema && parent) {
+        r = ly_nested_ext_schema(parent, NULL, module->name, strlen(module->name), LY_VALUE_JSON, NULL, name,
+                strlen(name), &schema, &ext);
+        LY_CHECK_RET(r && (r != LY_ENOT), r);
+    }
     LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Term node \"%s\" not found.", name), LY_ENOTFOUND);
 
-    rc = lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA, NULL, &ret);
-    LY_CHECK_RET(rc);
+    LY_CHECK_RET(lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA, NULL, &ret));
+    if (ext) {
+        ret->flags |= LYD_EXT;
+    }
     if (parent) {
         lyd_insert_node(parent, NULL, ret, 0);
     }
@@ -1234,8 +1272,10 @@
 lyd_new_any(struct lyd_node *parent, const struct lys_module *module, const char *name, const void *value,
         ly_bool use_value, LYD_ANYDATA_VALUETYPE value_type, ly_bool output, struct lyd_node **node)
 {
+    LY_ERR r;
     struct lyd_node *ret = NULL;
     const struct lysc_node *schema;
+    struct lysc_ext_instance *ext = NULL;
     const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
 
     LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
@@ -1246,9 +1286,17 @@
     }
 
     schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYD_NODE_ANY, output ? LYS_GETNEXT_OUTPUT : 0);
+    if (!schema && parent) {
+        r = ly_nested_ext_schema(parent, NULL, module->name, strlen(module->name), LY_VALUE_JSON, NULL, name,
+                strlen(name), &schema, &ext);
+        LY_CHECK_RET(r && (r != LY_ENOT), r);
+    }
     LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Any node \"%s\" not found.", name), LY_ENOTFOUND);
 
     LY_CHECK_RET(lyd_create_any(schema, value, value_type, use_value, &ret));
+    if (ext) {
+        ret->flags |= LYD_EXT;
+    }
     if (parent) {
         lyd_insert_node(parent, NULL, ret, 0);
     }
@@ -2030,6 +2078,9 @@
             goto cleanup;
         }
 
+        if (p[path_idx].ext) {
+            node->flags |= LYD_EXT;
+        }
         if (cur_parent) {
             /* connect to the parent */
             lyd_insert_node(cur_parent, NULL, node, 0);
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index c5a0e2a..fd773ab 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -3,7 +3,7 @@
  * @author Radek Krejci <rkrejci@cesnet.cz>
  * @brief Parsing and validation helper functions for data trees
  *
- * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
  *
  * This source code is licensed under BSD 3-Clause License (the "License").
  * You may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
 #include "log.h"
 #include "lyb.h"
 #include "parser_data.h"
+#include "plugins_exts.h"
 #include "printer_data.h"
 #include "set.h"
 #include "tree.h"
@@ -400,15 +401,17 @@
     return LY_SUCCESS;
 }
 
-void
-lyd_parse_set_data_flags(struct lyd_node *node, struct ly_set *node_when, struct lyd_meta **meta, uint32_t parse_opts)
+LY_ERR
+lyd_parse_set_data_flags(struct lyd_node *node, struct lyd_meta **meta, struct lyd_ctx *lydctx,
+        struct lysc_ext_instance *ext)
 {
     struct lyd_meta *meta2, *prev_meta = NULL;
+    struct lyd_ctx_ext_val *ext_val;
 
     if (lysc_has_when(node->schema)) {
-        if (!(parse_opts & LYD_PARSE_ONLY)) {
+        if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
             /* remember we need to evaluate this node's when */
-            LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL), );
+            LY_CHECK_RET(ly_set_add(&lydctx->node_when, node, 1, NULL));
         }
     }
 
@@ -430,6 +433,22 @@
 
         prev_meta = meta2;
     }
+
+    if (ext) {
+        /* parsed for an extension */
+        node->flags |= LYD_EXT;
+
+        if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
+            /* rememeber for validation */
+            ext_val = malloc(sizeof *ext_val);
+            LY_CHECK_ERR_RET(!ext_val, LOGMEM(LYD_CTX(node)), LY_EMEM);
+            ext_val->ext = ext;
+            ext_val->sibling = node;
+            LY_CHECK_RET(ly_set_add(&lydctx->ext_val, ext_val, 1, NULL));
+        }
+    }
+
+    return LY_SUCCESS;
 }
 
 /**
@@ -771,6 +790,46 @@
     }
 }
 
+LY_ERR
+ly_nested_ext_schema(const struct lyd_node *parent, const struct lysc_node *sparent, const char *prefix,
+        size_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data, const char *name, size_t name_len,
+        const struct lysc_node **snode, struct lysc_ext_instance **ext)
+{
+    LY_ERR r;
+    LY_ARRAY_COUNT_TYPE u;
+    struct lysc_ext_instance *nested_exts = NULL;
+    lyplg_ext_data_snode_clb ext_snode_cb;
+
+    /* check if there are any nested extension instances */
+    if (parent && parent->schema) {
+        nested_exts = parent->schema->exts;
+    } else if (sparent) {
+        nested_exts = sparent->exts;
+    }
+    LY_ARRAY_FOR(nested_exts, u) {
+        ext_snode_cb = nested_exts[u].def->plugin->snode;
+        if (!ext_snode_cb) {
+            /* not an extension with nested data */
+            continue;
+        }
+
+        /* try to get the schema node */
+        r = ext_snode_cb(&nested_exts[u], parent, sparent, prefix, prefix_len, format, prefix_data, name, name_len, snode);
+        if (!r) {
+            /* data successfully created, remember the ext instance */
+            *ext = &nested_exts[u];
+            return LY_SUCCESS;
+        } else if (r != LY_ENOT) {
+            /* fatal error */
+            return r;
+        }
+        /* data was not from this module, continue */
+    }
+
+    /* no extensions or none matched */
+    return LY_ENOT;
+}
+
 void
 ly_free_prefix_data(LY_VALUE_FORMAT format, void *prefix_data)
 {
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index 3cf45c6..f95c2d8 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -22,6 +22,7 @@
 #include <stddef.h>
 
 struct ly_path_predicate;
+struct lyd_ctx;
 struct lysc_module;
 
 #define LY_XML_SUFFIX ".xml"
@@ -128,11 +129,13 @@
  * @brief Set data flags for a newly parsed node.
  *
  * @param[in] node Node to use.
- * @param[in,out] node_when Set of nodes with unresolved when.
  * @param[in,out] meta Node metadata, may be removed from.
- * @param[in] parse_opts Parse options.
+ * @param[in] lydctx Data parsing context.
+ * @param[in] ext Extension instance if @p node was parsed for one.
+ * @return LY_ERR value.
  */
-void lyd_parse_set_data_flags(struct lyd_node *node, struct ly_set *node_when, struct lyd_meta **meta, uint32_t parse_opts);
+LY_ERR lyd_parse_set_data_flags(struct lyd_node *node, struct lyd_meta **meta, struct lyd_ctx *lydctx,
+        struct lysc_ext_instance *ext);
 
 /**
  * @brief Get schema node of a data node. Useful especially for opaque nodes.
@@ -153,6 +156,27 @@
 void lyd_del_move_root(struct lyd_node **root, const struct lyd_node *to_del, const struct lys_module *mod);
 
 /**
+ * @brief Try to get schema node for data with a parent based on an extension instance.
+ *
+ * @param[in] parent Parsed parent data node. Set if @p sparent is NULL.
+ * @param[in] sparent Schema parent node. Set if @p sparent is NULL.
+ * @param[in] prefix Element prefix, if any.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in] format Format of @p prefix.
+ * @param[in] prefix_data Format-specific data.
+ * @param[in] name Element name.
+ * @param[in] name_len Length of @p name.
+ * @param[out] snode Found schema node, NULL if no suitable was found.
+ * @param[out] ext Extension instance that provided @p snode.
+ * @return LY_SUCCESS on success;
+ * @return LY_ENOT if no extension instance parsed the data;
+ * @return LY_ERR on error.
+ */
+LY_ERR ly_nested_ext_schema(const struct lyd_node *parent, const struct lysc_node *sparent, const char *prefix,
+        size_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data, const char *name, size_t name_len,
+        const struct lysc_node **snode, struct lysc_ext_instance **ext);
+
+/**
  * @brief Free stored prefix data.
  *
  * @param[in] format Format of the prefixes.
diff --git a/src/validation.c b/src/validation.c
index 8004b44..c2c33d4 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -279,7 +279,7 @@
             LY_CHECK_RET(ret);
 
             /* remove this item from the set */
-            ly_set_rm_index(node_types, i, free);
+            ly_set_rm_index(ext_val, i, free);
         } while (i);
     }