libyang CHANGE api functions xpath format unified

For data, JSON path is always used and for schema,
augment target path is always used.
diff --git a/src/common.c b/src/common.c
index d6e4d7c..370c3bb 100644
--- a/src/common.c
+++ b/src/common.c
@@ -833,6 +833,157 @@
     return NULL;
 }
 
+static int
+transform_json2xpath_subexpr(const struct lys_module *cur_module, const struct lys_module *prev_mod, struct lyxp_expr *exp,
+                             uint32_t *i, enum lyxp_token end_token, char **out, size_t *out_used, size_t *out_size)
+{
+    const char *cur_expr, *end, *ptr;
+    size_t name_len;
+    char *name;
+    const struct lys_module *mod;
+
+    while (*i < exp->used) {
+        if (exp->tokens[*i] == end_token) {
+            return 0;
+        }
+
+        cur_expr = &exp->expr[exp->expr_pos[*i]];
+
+        /* copy WS */
+        if (*i && ((end = exp->expr + exp->expr_pos[*i - 1] + exp->tok_len[*i - 1]) != cur_expr)) {
+            strncpy(*out + *out_used, end, cur_expr - end);
+            *out_used += cur_expr - end;
+        }
+
+        if (exp->tokens[*i] == LYXP_TOKEN_BRACK1) {
+            /* copy "[" */
+            strncpy(*out + *out_used, &exp->expr[exp->expr_pos[*i]], exp->tok_len[*i]);
+            *out_used += exp->tok_len[*i];
+            ++(*i);
+
+            /* call recursively because we need to remember current prev_mod for after the predicate */
+            if (transform_json2xpath_subexpr(cur_module, prev_mod, exp, i, LYXP_TOKEN_BRACK2, out, out_used, out_size)) {
+                return -1;
+            }
+
+            /* copy "]" */
+            strncpy(*out + *out_used, &exp->expr[exp->expr_pos[*i]], exp->tok_len[*i]);
+            *out_used += exp->tok_len[*i];
+        } else if (exp->tokens[*i] == LYXP_TOKEN_NAMETEST) {
+            if ((end = strnchr(cur_expr, ':', exp->tok_len[*i]))) {
+                /* there is a prefix, get the module */
+                name_len = end - cur_expr;
+                name = strndup(cur_expr, name_len);
+                prev_mod = ly_ctx_get_module(cur_module->ctx, name, NULL);
+                if (!prev_mod) {
+                    LOGVAL(LYE_INMOD_LEN, LY_VLOG_NONE, NULL, name_len ? name_len : exp->tok_len[*i], cur_expr);
+                    return -1;
+                }
+                free(name);
+                /* skip ":" */
+                ++end;
+                ++name_len;
+            } else {
+                end = cur_expr;
+                name_len = 0;
+            }
+
+            /* do we print the module name? */
+            if (prev_mod != cur_module) {
+                /* adjust out size (it can even decrease in some strange cases) */
+                *out_size += (strlen(prev_mod->name) - name_len) + 1;
+                *out = ly_realloc(*out, *out_size);
+                LY_CHECK_ERR_RETURN(!*out, LOGMEM, -1);
+
+                /* copy the model name */
+                strcpy(*out + *out_used, prev_mod->name);
+                *out_used += strlen(prev_mod->name);
+
+                /* print ":" */
+                (*out)[*out_used] = ':';
+                ++(*out_used);
+            }
+
+            /* copy the rest */
+            strncpy(*out + *out_used, end, exp->tok_len[*i] - name_len);
+            *out_used += exp->tok_len[*i] - name_len;
+        } else if ((exp->tokens[*i] == LYXP_TOKEN_LITERAL) && (end = strnchr(cur_expr, ':', exp->tok_len[*i]))) {
+            ptr = end;
+            while (isalnum(ptr[-1]) || (ptr[-1] == '_') || (ptr[-1] == '-') || (ptr[-1] == '.')) {
+                --ptr;
+            }
+
+            /* get the module, but it may actually not be a module name */
+            name_len = end - ptr;
+            name = strndup(ptr, name_len);
+            mod = ly_ctx_get_module(cur_module->ctx, name, NULL);
+            free(name);
+
+            if (mod && (mod != cur_module)) {
+                /* adjust out size (it can even decrease in some strange cases) */
+                *out_size += strlen(mod->name) - name_len;
+                *out = ly_realloc(*out, *out_size);
+                LY_CHECK_ERR_RETURN(!*out, LOGMEM, -1);
+
+                /* copy any beginning */
+                strncpy(*out + *out_used, cur_expr, ptr - cur_expr);
+                *out_used += ptr - cur_expr;
+
+                /* copy the model name */
+                strcpy(*out + *out_used, mod->name);
+                *out_used += strlen(mod->name);
+
+                /* copy the rest */
+                strncpy(*out + *out_used, end, (exp->tok_len[*i] - name_len) - (ptr - cur_expr));
+                *out_used += (exp->tok_len[*i] - name_len) - (ptr - cur_expr);
+            } else {
+                strncpy(*out + *out_used, &exp->expr[exp->expr_pos[*i]], exp->tok_len[*i]);
+                *out_used += exp->tok_len[*i];
+            }
+        } else {
+            strncpy(*out + *out_used, &exp->expr[exp->expr_pos[*i]], exp->tok_len[*i]);
+            *out_used += exp->tok_len[*i];
+        }
+
+        ++(*i);
+    }
+
+    return 0;
+}
+
+char *
+transform_json2xpath(const struct lys_module *cur_module, const char *expr)
+{
+    char *out;
+    size_t out_size, out_used;
+    uint32_t i;
+    struct lyxp_expr *exp;
+
+    assert(cur_module && expr);
+
+    out_size = strlen(expr) + 1;
+    out = malloc(out_size);
+    LY_CHECK_ERR_RETURN(!out, LOGMEM, NULL);
+    out_used = 0;
+
+    exp = lyxp_parse_expr(expr);
+    LY_CHECK_ERR_RETURN(!exp, free(out), NULL);
+
+    i = 0;
+    if (transform_json2xpath_subexpr(cur_module, cur_module, exp, &i, LYXP_TOKEN_NONE, &out, &out_used, &out_size)) {
+        goto error;
+    }
+    out[out_used] = '\0';
+
+    lyxp_expr_free(exp);
+    return out;
+
+error:
+    free(out);
+    lyxp_expr_free(exp);
+    return NULL;
+}
+
 int
 ly_new_node_validity(const struct lys_node *schema)
 {
diff --git a/src/common.h b/src/common.h
index 31795b8..7eb4358 100644
--- a/src/common.h
+++ b/src/common.h
@@ -265,7 +265,7 @@
 #define LOGPATH(elem_type, elem)                                    \
     ly_vlog(LYE_PATH, elem_type, elem);
 
-void ly_vlog_build_path_reverse(enum LY_VLOG_ELEM elem_type, const void *elem, char *path, uint16_t *index, int prefix_all);
+void ly_vlog_build_path_reverse(enum LY_VLOG_ELEM elem_type, const void *elem, char *path, uint16_t *index);
 
 /*
  * - if \p module specified, it searches for submodules, they can be loaded only from a file or via module callback,
@@ -374,6 +374,12 @@
 const char *transform_iffeat_schema2json(const struct lys_module *module, const char *expr);
 
 /**
+ * @brief Transform an XPath expression in JSON node naming conventions into
+ *        standard YANG XPath.
+ */
+char *transform_json2xpath(const struct lys_module *cur_module, const char *expr);
+
+/**
  * @brief Get a new node (non-validated) validity value.
  *
  * @param[in] schema Schema node of the new data node.
diff --git a/src/context.c b/src/context.c
index ec3ac20..eac8661 100644
--- a/src/context.c
+++ b/src/context.c
@@ -1510,19 +1510,3 @@
 
     return root;
 }
-
-API const struct lys_node *
-ly_ctx_get_node(struct ly_ctx *ctx, const struct lys_node *start, const char *nodeid)
-{
-    const struct lys_node *node;
-
-    if (!ctx || !nodeid || ((nodeid[0] != '/') && !start)) {
-        ly_errno = LY_EINVAL;
-        return NULL;
-    }
-
-    /* sets error and everything */
-    node = resolve_json_nodeid(nodeid, ctx, start);
-
-    return node;
-}
diff --git a/src/libyang.h.in b/src/libyang.h.in
index 2c7c650..e3f9850 100644
--- a/src/libyang.h.in
+++ b/src/libyang.h.in
@@ -856,10 +856,27 @@
 /**
  * @page howtoxpath XPath Addressing
  *
- * Internally, XPath evaluation is performed on \b when and \b must conditions in the schema. For that almost
- * a full XPath 1.0 evaluator was implemented except that only node sets are returned. This XPath implementation
- * is available on data trees by calling lyd_find_xpath() and on schema trees by calling lys_find_xpath().
- * This XPath conforms to the YANG specification (RFC 6020 section 6.4). Some useful examples:
+ * Internally, XPath evaluation is performed on __when__ and __must__ conditions in the schema. For that almost
+ * a full XPath 1.0 evaluator was implemented. In YANG models you can also find paths identifying __augment__
+ * targets, __leafref__ targets, and trivial paths in __choice default__ and __unique__ statements argument.
+ * The exact format of all those paths can be found in the relevant RFCs. Further will only be discussed
+ * paths that are used directly in libyang API functions.
+ *
+ * Schema
+ * ======
+ *
+ * Regarding identifying schema nodes, we use a slightly modified version of YANG __augment__ target path:
+ *   - strictly speaking, most XPath expressions are not accepted, only simple paths (no predicates,
+ *     numbers, literals, operators, ...),
+ *   - whenever a prefix is used for a node, it is not the import prefix, but the __module name__ itself,
+ *   - __current module__ is specified separately for _absolute_ paths and is the module of the start
+ *     (current) node for _relative_ paths,
+ *   - unprefixed nodes all use the prefix of the __current module__ so all nodes from other modules than
+ *     the __current module__ _MUST_ have prefixes,
+ *   - nodes from the __current module__ _MAY_ have prefixes,
+ *
+ * Examples
+ * --------
  *
  * - get all top-level nodes of the __module-name__
  *
@@ -869,67 +886,53 @@
  *
  *       /module-name:container//\asterisk
  *
- * - get __list__ instance with __key1__ of value __1__ and __key2__ of value __2__ (this can return more __list__ instances if there are more keys than __key1__ and __key2__)
- *
- *       /module-name:container/module-name:list[module-name:key1='1'][module-name:key2='2']
- *
- * - get __leaf-list__ instance with the value __val__
- *
- *       /module-name:container/module-name:leaf-list[.='val']
- *
  * - get __aug-leaf__, which was added to __module-name__ from an augment module __augment-module__
  *
- *       /module-name:container/module-name:container2/augment-module:aug-cont/augment-module:aug-leaf
- *
- * It is important to know what prefix is used for node names without one. It is the prefix of the context node, not
- * of the parent or any other. Example:
- *
- * - get __child__ of __container__ having __container__ as the context node (either of the two)
- *
- *       /container/child
- *       /child
- *
- * - get __aug-leaf__, which was added to __container__ from an augment module __augment-module__ having __container__
- *   as the context node
- *
- *       /container/augment-module:aug-leaf
- *
- * A very small subset of this full XPath is recognized by lyd_new_path(). Basically, only a relative or absolute
- * path can be specified to identify a new data node. However, lists must be identified by either all their keys and created
- * with all of them or using their relative position on their level starting from 1, so for those cases predicates are
- * allowed. List positions can be learned using lyd_list_pos(). Key predicates must be ordered the way the keys are
- * ordered and all the keys must be specified. Every predicate includes a single key with its value. If an instance
- * with such particular set of keys or with such relative position does not exist or no predicate is specified, list
- * instance is created. Optionally, leaves and leaf-lists can have predicates specifying their value in the path itself.
- * All these paths are valid XPath expressions. Example (ietf-yang-library:modules-state is the context node):
- *
- *     /modules-state/module[name='ietf-yang-library'][revision='']/conformance[.='implement']
- *     /modules-state/module[1]/conformance[.='implement']
- *
- * Almost the same XPath is accepted by ly_ctx_get_node(). The difference is that it is not used on data, but schema,
- * which means there are no key values and only one node matches one path. In effect, lists do not have to have any
- * predicates. If they do, they do not need to have all the keys specified and if values are included, they are ignored.
- * Nevertheless, any such expression is still a valid XPath, but can return more nodes if executed on a data tree.
- * Examples (all returning the same node, ietf-yang-library:modules-state is the context node):
- *
- *     /modules-state/module/submodules
- *     /modules-state/module[name]/submodules
- *     /modules-state/module[name][revision]/submodules
- *     /modules-state/module[name='ietf-yang-library'][revision]/submodules
- *
- * Also, `choice`, `case`, `input`, and `output` nodes need to be specified and cannot be skipped in schema XPaths. Use
- * lys_find_xpath() if you want to search based on a data XPath.
- *
- * Also note, that in all cases the node's prefix is specified as the name of the appropriate YANG schema. Any node
- * can be prefixed by the module name. However, if the prefix is omitted, the module name is inherited from the previous
- * (parent) node. It means, that the first node in the path is always supposed to have a prefix.
+ *       /module-name:container/container2/augment-module:aug-cont/augment-module:aug-leaf
  *
  * Functions List
  * --------------
- * - lyd_find_xpath()
- * - lys_find_xpath()
+ * - lys_find_path()
+ * - lys_path()
+ *
+ *
+ * Data
+ * ====
+ *
+ * As for paths evaluated on YANG data, we opted for standardized JSON paths ([RFC 7951](https://tools.ietf.org/html/rfc7951#section-6.11)). Summarized, it follows these conventions:
+ *   - generally, you can use almost a full XPath in these paths where it makes sense, but only data nodes (node sets)
+ *     will always be returned (except for paths, predicates are mostly used),
+ *   - as per the specification, prefixes are actually __module names__,
+ *   - also in the specification, for _absolute_ paths, the first (leftmost) node _MUST_ have a prefix,
+ *   - for _relative_ paths, you specify the __context node__, which then acts as a parent for the first node in the path,
+ *   - nodes always inherit their module (prefix) from their __parent node__ so whenever a node is from a different
+ *     module than its parent, it _MUST_ have a prefix,
+ *   - nodes from the same module as their __parent__ _MUST NOT_ have a prefix,
+ *   - different from schema paths, non-data nodes (choice, case, uses, input, output) are skipped and not included
+ *     in the path.
+ *
+ * Examples
+ * --------
+ *
+ * - get __list__ instance with __key1__ of value __1__ and __key2__ of value __2__ (this can return more __list__ instances if there are more keys than __key1__ and __key2__)
+ *
+ *       /module-name:container/list[key1='1'][key2='2']
+ *
+ * - get __leaf-list__ instance with the value __val__
+ *
+ *       /module-name:container/leaf-list[.='val']
+ *
+ * - get __aug-list__ with __aug-list-key__, which was added to __module-name__ from an augment module __augment-module__
+ *
+ *       /module-name:container/container2/augment-module:aug-cont/aug-list[aug-list-key='value']
+ *
+ * Functions List
+ * --------------
+ * - lyd_find_path()
  * - lyd_new_path()
- * - ly_ctx_get_node()
+ * - lyd_path()
+ * - lys_data_path()
+ *
  */
 
 /**
@@ -1337,30 +1340,6 @@
 const struct lys_submodule *ly_ctx_get_submodule2(const struct lys_module *main_module, const char *submodule);
 
 /**
- * @brief Get schema node according to the given schema node identifier in JSON format.
- *
- * If the \p nodeid is absolute, the first node identifier must be prefixed with
- * the module name. Then every other identifier either has an explicit module name or
- * the module name of the previous node is assumed. Examples:
- *
- * /ietf-netconf-monitoring:get-schema/input/identifier
- * /ietf-interfaces:interfaces/interface/ietf-ip:ipv4/address/ip
- *
- * If the \p nodeid is relative, \p start is mandatory and is the starting point
- * for the resolution. The first node identifier does not need a module name.
- *
- * Predicates on lists are accepted (ignored) in the form of "<key>(=<value>)"
- * and on leaves/leaf-lists ".(=<value>)".
- *
- * @param[in] ctx Context to work in.
- * @param[in] start Starting node for a relative schema node identifier, in which
- * case it is mandatory.
- * @param[in] nodeid JSON schema node identifier.
- * @return Resolved schema node or NULL.
- */
-const struct lys_node *ly_ctx_get_node(struct ly_ctx *ctx, const struct lys_node *start, const char *nodeid);
-
-/**
  * @brief Remove the specified module from its context.
  *
  * Beside the selected module, also all other modules depending on all the modules being removed
diff --git a/src/log.c b/src/log.c
index 4fa4d28..16f53a0 100644
--- a/src/log.c
+++ b/src/log.c
@@ -425,13 +425,13 @@
 }
 
 void
-ly_vlog_build_path_reverse(enum LY_VLOG_ELEM elem_type, const void *elem, char *path, uint16_t *index, int prefix_all)
+ly_vlog_build_path_reverse(enum LY_VLOG_ELEM elem_type, const void *elem, char *path, uint16_t *index)
 {
     int i, j;
     struct lys_node_list *slist;
-    struct lys_node *sparent;
+    struct lys_node *sparent = NULL;
     struct lyd_node *dlist, *diter;
-    struct lys_module *top_module = NULL;
+    const struct lys_module *top_smodule = NULL;
     const char *name, *prefix = NULL, *val_end, *val_start;
     char *str;
     size_t len;
@@ -444,14 +444,9 @@
             elem = ((struct lyxml_elem *)elem)->parent;
             break;
         case LY_VLOG_LYS:
-            if (!top_module) {
-                /* find and store the top-level node module */
-                if (((struct lys_node *)elem)->nodetype == LYS_EXT) {
-                    top_module = ((struct lys_ext_instance *)elem)->module;
-                } else {
-                    for (sparent = (struct lys_node *)elem; lys_parent(sparent); sparent = lys_parent(sparent));
-                    top_module = lys_node_module(sparent);
-                }
+            if (!top_smodule) {
+                /* remember the top module, it will act as the current module */
+                top_smodule = lys_node_module((struct lys_node *)elem);
             }
 
             if (((struct lys_node *)elem)->nodetype & (LYS_AUGMENT | LYS_GROUPING)) {
@@ -474,7 +469,7 @@
                 name = ((struct lys_node *)elem)->name;
             }
 
-            if (prefix_all || !lys_parent((struct lys_node *)elem) || (lys_node_module((struct lys_node *)elem) != top_module)) {
+            if (lys_node_module((struct lys_node *)elem) != top_smodule) {
                 prefix = lys_node_module((struct lys_node *)elem)->name;
             } else {
                 prefix = NULL;
@@ -496,14 +491,9 @@
             } while (elem && (((struct lys_node *)elem)->nodetype == LYS_USES));
             break;
         case LY_VLOG_LYD:
-            if (!top_module) {
-                /* find and store the top-level node module */
-                for (diter = (struct lyd_node *)elem; diter->parent; diter = diter->parent);
-                top_module = lyd_node_module(diter);
-            }
-
             name = ((struct lyd_node *)elem)->schema->name;
-            if (prefix_all || !((struct lyd_node *)elem)->parent || (lyd_node_module((struct lyd_node *)elem) != top_module)) {
+            if (!((struct lyd_node *)elem)->parent ||
+                    lyd_node_module((struct lyd_node *)elem) != lyd_node_module(((struct lyd_node *)elem)->parent)) {
                 prefix = lyd_node_module((struct lyd_node *)elem)->name;
             } else {
                 prefix = NULL;
@@ -540,7 +530,7 @@
                             len = strlen(diter->schema->name);
                             (*index) -= len;
                             memcpy(&path[(*index)], diter->schema->name, len);
-                            if (prefix_all || (lyd_node_module(diter) != top_module)) {
+                            if (lyd_node_module(dlist) != lyd_node_module(diter)) {
                                 path[--(*index)] = ':';
                                 len = strlen(lyd_node_module(diter)->name);
                                 (*index) -= len;
@@ -658,7 +648,7 @@
             /* top-level */
             path[--(*index)] = '/';
         } else {
-            ly_vlog_build_path_reverse(elem_type, elem, path, index, 0);
+            ly_vlog_build_path_reverse(elem_type, elem, path, index);
         }
     } else if (elem_type == LY_VLOG_NONE) {
         /* erase path, the rest will be erased by log_vprintf() since it will get NULL path parameter */
diff --git a/src/parser.c b/src/parser.c
index c3416b6..6497904 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -3412,11 +3412,18 @@
 
     for (i = 0; i < module->deviation_size; i++) {
         target = NULL;
-        resolve_augment_schema_nodeid(module->deviation[i].target_name, NULL, module, (const struct lys_node **)&target);
-        if (!target) {
+        extset = NULL;
+        j = resolve_schema_nodeid(module->deviation[i].target_name, NULL, module, &extset, 0, 0);
+        if (j == -1) {
+            return EXIT_FAILURE;
+        } else if (!extset) {
             /* LY_DEVIATE_NO */
+            ly_set_free(extset);
             continue;
         }
+        target = extset->set.s[0];
+        ly_set_free(extset);
+
         for (j = 0; j < module->deviation[i].deviate_size; j++) {
             dev = &module->deviation[i].deviate[j];
             if (!dev->ext_size) {
diff --git a/src/parser_yang.c b/src/parser_yang.c
index 2564696..a8ec395 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -4336,7 +4336,7 @@
     int rc;
     uint i;
     struct lys_node *dev_target = NULL, *parent;
-    struct ly_set *dflt_check = ly_set_new();
+    struct ly_set *dflt_check = ly_set_new(), *set;
     unsigned int u;
     const char *value, *target_name;
     struct lys_node_leaflist *llist;
@@ -4345,12 +4345,15 @@
     struct lys_module *mod;
 
     /* resolve target node */
-
-    rc = resolve_augment_schema_nodeid(dev->target_name, NULL, module, (const struct lys_node **)&dev_target);
-    if (rc || !dev_target) {
+    rc = resolve_schema_nodeid(dev->target_name, NULL, module, &set, 0, 1);
+    if (rc == -1) {
         LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, dev->target_name, "deviation");
+        ly_set_free(set);
         goto error;
     }
+    dev_target = set->set.s[0];
+    ly_set_free(set);
+
     if (dev_target->module == lys_main_module(module)) {
         LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, dev->target_name, "deviation");
         LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Deviating own module is not allowed.");
diff --git a/src/parser_yin.c b/src/parser_yin.c
index 7d73e93..5f564ea 100644
--- a/src/parser_yin.c
+++ b/src/parser_yin.c
@@ -2013,7 +2013,7 @@
     struct lys_node *node = NULL, *parent, *dev_target = NULL;
     struct lys_node_choice *choice = NULL;
     struct lys_node_leaf *leaf = NULL;
-    struct ly_set *dflt_check = ly_set_new();
+    struct ly_set *dflt_check = ly_set_new(), *set;
     struct lys_node_list *list = NULL;
     struct lys_node_leaflist *llist = NULL;
     struct lys_type *t = NULL;
@@ -2032,11 +2032,15 @@
     }
 
     /* resolve target node */
-    rc = resolve_augment_schema_nodeid(dev->target_name, NULL, module, (const struct lys_node **)&dev_target);
-    if (rc || !dev_target) {
+    rc = resolve_schema_nodeid(dev->target_name, NULL, module, &set, 0, 1);
+    if (rc == -1) {
         LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, dev->target_name, yin->name);
+        ly_set_free(set);
         goto error;
     }
+    dev_target = set->set.s[0];
+    ly_set_free(set);
+
     if (dev_target->module == lys_main_module(module)) {
         LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, dev->target_name, yin->name);
         LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Deviating own module is not allowed.");
diff --git a/src/resolve.c b/src/resolve.c
index 803a123..128120f 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -155,37 +155,92 @@
  * @param[out] mod_name_len Length of the module name, 0 if there is not any.
  * @param[out] name Points to the node name.
  * @param[out] nam_len Length of the node name.
+ * @param[out] all_desc Whether the path starts with '/', only supported in extended paths.
+ * @param[in] extended Whether to accept an extended path (support for [prefix:]*, /[prefix:]*, /[prefix:].).
  *
  * @return Number of characters successfully parsed,
  *         positive on success, negative on failure.
  */
 static int
-parse_node_identifier(const char *id, const char **mod_name, int *mod_name_len, const char **name, int *nam_len)
+parse_node_identifier(const char *id, const char **mod_name, int *mod_name_len, const char **name, int *nam_len,
+                      int *all_desc, int extended)
 {
     int parsed = 0, ret;
 
     assert(id);
+    assert((mod_name && mod_name_len) || (!mod_name && !mod_name_len));
+    assert((name && nam_len) || (!name && !nam_len));
+    assert(!extended || all_desc);
+
     if (mod_name) {
         *mod_name = NULL;
-    }
-    if (mod_name_len) {
         *mod_name_len = 0;
     }
     if (name) {
         *name = NULL;
-    }
-    if (nam_len) {
         *nam_len = 0;
     }
 
+    if (extended) {
+        /* try to parse only the extended expressions */
+        if (id[parsed] == '/') {
+            *all_desc = 1;
+        } else {
+            *all_desc = 0;
+        }
+
+        /* is there a prefix? */
+        ret = parse_identifier(id + *all_desc);
+        if (ret > 0) {
+            if (id[*all_desc + ret] != ':') {
+                /* this is not a prefix, so not an extended id */
+                goto standard_id;
+            }
+
+            if (mod_name) {
+                *mod_name = id + *all_desc;
+                *mod_name_len = ret;
+            }
+
+            /* "/" and ":" */
+            ret += *all_desc + 1;
+        } else {
+            ret = *all_desc;
+        }
+
+        /* parse either "*" or "." */
+        if (!strcmp(id + ret, "*")) {
+            if (name) {
+                *name = id + ret;
+                *nam_len = 1;
+            }
+            ++ret;
+
+            return ret;
+        } else if (!strcmp(id + ret, ".")) {
+            if (!*all_desc) {
+                /* /. is redundant expression, we do not accept it */
+                return -ret;
+            }
+
+            if (name) {
+                *name = id + ret;
+                *nam_len = 1;
+            }
+            ++ret;
+
+            return ret;
+        }
+        /* else a standard id, parse it all again */
+    }
+
+standard_id:
     if ((ret = parse_identifier(id)) < 1) {
         return ret;
     }
 
     if (mod_name) {
         *mod_name = id;
-    }
-    if (mod_name_len) {
         *mod_name_len = ret;
     }
 
@@ -223,8 +278,6 @@
 
     if (name) {
         *name = id;
-    }
-    if (nam_len) {
         *nam_len = ret;
     }
 
@@ -291,7 +344,7 @@
         ++id;
     }
 
-    if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len)) < 1) {
+    if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len, NULL, 0)) < 1) {
         return -parsed+ret;
     }
 
@@ -478,8 +531,8 @@
     }
 
     /* all parent references must be parsed at this point */
-    if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len)) < 1) {
-        return -parsed+ret;
+    if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len, NULL, 0)) < 1) {
+        return -parsed + ret;
     }
 
     parsed += ret;
@@ -564,8 +617,8 @@
     ++id;
 
     /* node-identifier ([prefix:]identifier) */
-    if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len)) < 1) {
-        return -parsed-ret;
+    if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len, NULL, 0)) < 1) {
+        return -parsed - ret;
     }
     if (prefix && !(*prefix)) {
         /* actually we always need prefix even it is not specified */
@@ -756,7 +809,7 @@
             ++id;
 
         } else {
-            if ((ret = parse_node_identifier(id, model, mod_len, name, nam_len)) < 1) {
+            if ((ret = parse_node_identifier(id, model, mod_len, name, nam_len, NULL, 0)) < 1) {
                 return -parsed+ret;
             } else if (model && !*model) {
                 return -parsed;
@@ -847,30 +900,20 @@
  *                         on the first call, must not be changed between consecutive calls.
  * @param[out] has_predicate Flag to mark whether there is a predicate specified. It cannot be
  *                           based on the grammar, in those cases use NULL.
+ * @param[in] extended Whether to accept an extended path (support for /[prefix:]*, //[prefix:]*, //[prefix:].).
  *
  * @return Number of characters successfully parsed,
  *         positive on success, negative on failure.
  */
 int
 parse_schema_nodeid(const char *id, const char **mod_name, int *mod_name_len, const char **name, int *nam_len,
-                    int *is_relative, int *has_predicate)
+                    int *is_relative, int *has_predicate, int *all_desc, int extended)
 {
     int parsed = 0, ret;
 
     assert(id);
     assert(is_relative);
-    if (mod_name) {
-        *mod_name = NULL;
-    }
-    if (mod_name_len) {
-        *mod_name_len = 0;
-    }
-    if (name) {
-        *name = NULL;
-    }
-    if (nam_len) {
-        *nam_len = 0;
-    }
+
     if (has_predicate) {
         *has_predicate = 0;
     }
@@ -893,8 +936,8 @@
         ++id;
     }
 
-    if ((ret = parse_node_identifier(id, mod_name, mod_name_len, name, nam_len)) < 1) {
-        return -parsed+ret;
+    if ((ret = parse_node_identifier(id, mod_name, mod_name_len, name, nam_len, all_desc, extended)) < 1) {
+        return -parsed + ret;
     }
 
     parsed += ret;
@@ -993,7 +1036,7 @@
         if (nam_len) {
             *nam_len = ret;
         }
-    } else if ((ret = parse_node_identifier(id, mod_name, mod_name_len, name, nam_len)) < 1) {
+    } else if ((ret = parse_node_identifier(id, mod_name, mod_name_len, name, nam_len, NULL, 0)) < 1) {
         return -parsed + ret;
     }
 
@@ -1086,7 +1129,7 @@
     assert(feature);
 
     /* check prefix */
-    if ((i = parse_node_identifier(feat_name, &mod_name, &mod_name_len, &name, &nam_len)) < 1) {
+    if ((i = parse_node_identifier(feat_name, &mod_name, &mod_name_len, &name, &nam_len, NULL, 0)) < 1) {
         LOGVAL(LYE_INCHAR, LY_VLOG_NONE, NULL, feat_name[-i], &feat_name[-i]);
         return -1;
     }
@@ -1576,63 +1619,134 @@
     return result;
 }
 
-/*
- *  0 - ok (done)
- *  1 - continue
- *  2 - break
- * -1 - error
- */
 int
-schema_nodeid_siblingcheck(const struct lys_node *sibling, const char *id, const struct lys_module *module,
-                           const char *mod_name, int mod_name_len)
+schema_nodeid_siblingcheck(const struct lys_node *sibling, const struct lys_module *cur_module, const char *mod_name,
+                           int mod_name_len, const char *name, int nam_len)
 {
     const struct lys_module *prefix_mod;
 
+    /* name check */
+    if ((name[0] != '*') && (name[0] != '.') && (strncmp(name, sibling->name, nam_len) || sibling->name[nam_len])) {
+        return 1;
+    }
+
     /* module check */
-    prefix_mod = lys_get_import_module(module, NULL, 0, mod_name, mod_name_len);
-    if (!prefix_mod) {
-        return -1;
+    if (mod_name) {
+        prefix_mod = lys_get_import_module(cur_module, NULL, 0, mod_name, mod_name_len);
+        if (!prefix_mod) {
+            return -1;
+        }
+    } else {
+        prefix_mod = cur_module;
     }
     if (prefix_mod != lys_node_module(sibling)) {
         return 1;
     }
 
-    /* the result node? */
-    if (!id[0]) {
+    /* match */
+    switch (name[0]) {
+    case '*':
+        return 2;
+    case '.':
+        return 3;
+    default:
         return 0;
     }
-
-    /* move down the tree, if possible */
-    if (sibling->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA)) {
-        return -1;
-    }
-
-    return 2;
 }
 
-/* start - relative, module - absolute, -1 error, EXIT_SUCCESS ok (but ret can still be NULL), >0 unexpected char on ret - 1
+/* keys do not have to be ordered and do not have to be all of them */
+static int
+resolve_extended_schema_nodeid_predicate(const char *nodeid, const struct lys_node *node,
+                                         const struct lys_module *cur_module, int *nodeid_end)
+{
+    int mod_len, nam_len, has_predicate, r, i;
+    const char *model, *name;
+    struct lys_node_list *list;
+
+    if (!(node->nodetype & (LYS_LIST | LYS_LEAFLIST))) {
+        return 1;
+    }
+
+    list = (struct lys_node_list *)node;
+    do {
+        r = parse_schema_json_predicate(nodeid, &model, &mod_len, &name, &nam_len, NULL, NULL, &has_predicate);
+        if (r < 1) {
+            LOGVAL(LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, nodeid[r], &nodeid[r]);
+            return -1;
+        }
+        nodeid += r;
+
+        if (node->nodetype == LYS_LEAFLIST) {
+            /* just check syntax */
+            if (model || !name || (name[0] != '.') || has_predicate) {
+                return 1;
+            }
+            break;
+        } else {
+            /* check the key */
+            for (i = 0; i < list->keys_size; ++i) {
+                if (strncmp(list->keys[i]->name, name, nam_len) || list->keys[i]->name[nam_len]) {
+                    continue;
+                }
+                if (model) {
+                    if (strncmp(lys_node_module((struct lys_node *)list->keys[i])->name, model, mod_len)
+                            || lys_node_module((struct lys_node *)list->keys[i])->name[mod_len]) {
+                        continue;
+                    }
+                } else {
+                    if (lys_node_module((struct lys_node *)list->keys[i]) != cur_module) {
+                        continue;
+                    }
+                }
+
+                /* match */
+                break;
+            }
+
+            if (i == list->keys_size) {
+                return 1;
+            }
+        }
+    } while (has_predicate);
+
+    if (!nodeid[0]) {
+        *nodeid_end = 1;
+    }
+    return 0;
+}
+
+/* start - relative, module - absolute, -1 error (logged), EXIT_SUCCESS ok
  */
 int
-resolve_augment_schema_nodeid(const char *nodeid, const struct lys_node *start, const struct lys_module *cur_module,
-                              const struct lys_node **ret)
+resolve_schema_nodeid(const char *nodeid, const struct lys_node *start, const struct lys_module *cur_module,
+                      struct ly_set **ret, int extended, int no_node_error)
 {
     const char *name, *mod_name, *id;
-    const struct lys_node *sibling, *start_parent;
+    const struct lys_node *sibling, *start_parent, *next, *elem;
     struct lys_node_augment *last_aug;
-    int r, nam_len, mod_name_len = 0, is_relative = -1;
+    int r, nam_len, mod_name_len = 0, is_relative = -1, all_desc, has_predicate, nodeid_end = 0;
     /* resolved import module from the start module, it must match the next node-name-match sibling */
     const struct lys_module *start_mod, *aux_mod;
+    char *str;
 
-    assert(nodeid && (start || cur_module) && !(start && cur_module) && ret);
+    assert(nodeid && (start || cur_module) && ret);
+    *ret = NULL;
 
+    if (!cur_module) {
+        cur_module = lys_node_module(start);
+    }
     id = nodeid;
 
-    if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL)) < 1) {
-        return ((id - nodeid) - r) + 1;
+    r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate,
+                            (extended ? &all_desc : NULL), extended);
+    if (r < 1) {
+        LOGVAL(LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[r], &id[r]);
+        return -1;
     }
     id += r;
 
-    if ((is_relative && !start) || (!is_relative && !cur_module)) {
+    if (is_relative && !start) {
+        LOGINT;
         return -1;
     }
 
@@ -1645,6 +1759,9 @@
     } else {
         start_mod = lys_get_import_module(cur_module, NULL, 0, mod_name, mod_name_len);
         if (!start_mod) {
+            str = strndup(mod_name, mod_name_len);
+            LOGVAL(LYE_PATH_INMOD, LY_VLOG_STR, str);
+            free(str);
             return -1;
         }
         start_parent = NULL;
@@ -1660,6 +1777,9 @@
                 /* we are getting into another module (augment) */
                 aux_mod = lys_get_import_module(cur_module, NULL, 0, mod_name, mod_name_len);
                 if (!aux_mod) {
+                    str = strndup(mod_name, mod_name_len);
+                    LOGVAL(LYE_PATH_INMOD, LY_VLOG_STR, str);
+                    free(str);
                     return -1;
                 }
             } else {
@@ -1670,7 +1790,7 @@
             }
 
             /* if the module is implemented, all the augments will be connected */
-            if (!aux_mod->implemented) {
+            if (!aux_mod->implemented && !extended) {
 get_next_augment:
                 last_aug = lys_getnext_target_aug(last_aug, aux_mod, start_parent);
             }
@@ -1678,21 +1798,88 @@
 
         while ((sibling = lys_getnext(sibling, (last_aug ? (struct lys_node *)last_aug : start_parent), start_mod,
                 LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHINOUT | LYS_GETNEXT_PARENTUSES))) {
-            /* name match */
-            if (sibling->name && !strncmp(name, sibling->name, nam_len) && !sibling->name[nam_len]) {
-                r = schema_nodeid_siblingcheck(sibling, id, cur_module, mod_name, mod_name_len);
-                if (r == 0) {
-                    *ret = sibling;
-                    return EXIT_SUCCESS;
-                } else if (r == 1) {
+            r = schema_nodeid_siblingcheck(sibling, cur_module, mod_name, mod_name_len, name, nam_len);
+
+            /* resolve predicate */
+            if (extended && ((r == 0) || (r == 2) || (r == 3)) && has_predicate) {
+                r = resolve_extended_schema_nodeid_predicate(id, sibling, cur_module, &nodeid_end);
+                if (r == 1) {
                     continue;
-                } else if (r == 2) {
-                    start_parent = sibling;
-                    break;
-                } else {
+                } else if (r == -1) {
                     return -1;
                 }
+            } else if (!id[0]) {
+                nodeid_end = 1;
             }
+
+            if (r == 0) {
+                /* one matching result */
+                if (nodeid_end) {
+                    *ret = ly_set_new();
+                    LY_CHECK_ERR_RETURN(!*ret, LOGMEM, -1);
+                    ly_set_add(*ret, (void *)sibling, LY_SET_OPT_USEASLIST);
+                } else {
+                    if (sibling->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA)) {
+                        return -1;
+                    }
+                    start_parent = sibling;
+                }
+                break;
+            } else if (r == 1) {
+                continue;
+            } else if (r == 2) {
+                /* "*" */
+                if (!*ret) {
+                    *ret = ly_set_new();
+                    LY_CHECK_ERR_RETURN(!*ret, LOGMEM, -1);
+                }
+                ly_set_add(*ret, (void *)sibling, LY_SET_OPT_USEASLIST);
+                if (all_desc) {
+                    LY_TREE_DFS_BEGIN(sibling, next, elem) {
+                        if (elem != sibling) {
+                            ly_set_add(*ret, (void *)elem, LY_SET_OPT_USEASLIST);
+                        }
+
+                        LY_TREE_DFS_END(sibling, next, elem);
+                    }
+                }
+            } else if (r == 3) {
+                /* "." */
+                if (!*ret) {
+                    *ret = ly_set_new();
+                    LY_CHECK_ERR_RETURN(!*ret, LOGMEM, -1);
+                    ly_set_add(*ret, (void *)start_parent, LY_SET_OPT_USEASLIST);
+                }
+                ly_set_add(*ret, (void *)sibling, LY_SET_OPT_USEASLIST);
+                if (all_desc) {
+                    LY_TREE_DFS_BEGIN(sibling, next, elem) {
+                        if (elem != sibling) {
+                            ly_set_add(*ret, (void *)elem, LY_SET_OPT_USEASLIST);
+                        }
+
+                        LY_TREE_DFS_END(sibling, next, elem);
+                    }
+                }
+            } else {
+                LOGINT;
+                return -1;
+            }
+        }
+
+        /* skip predicate */
+        if (extended && has_predicate) {
+            while (id[0] == '[') {
+                id = strchr(id, ']');
+                if (!id) {
+                    LOGINT;
+                    return -1;
+                }
+                ++id;
+            }
+        }
+
+        if (nodeid_end && ((r == 0) || (r == 2) || (r == 3))) {
+            return EXIT_SUCCESS;
         }
 
         /* no match */
@@ -1701,12 +1888,21 @@
                 /* it still could be in another augment */
                 goto get_next_augment;
             }
+            if (no_node_error) {
+                str = strndup(nodeid, (name - nodeid) + nam_len);
+                LOGVAL(LYE_PATH_INNODE, LY_VLOG_STR, str);
+                free(str);
+                return -1;
+            }
             *ret = NULL;
             return EXIT_SUCCESS;
         }
 
-        if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL)) < 1) {
-            return ((id - nodeid) - r) + 1;
+        r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate,
+                                (extended ? &all_desc : NULL), extended);
+        if (r < 1) {
+            LOGVAL(LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[r], &id[r]);
+            return -1;
         }
         id += r;
     }
@@ -1740,9 +1936,9 @@
     }
 
     id = nodeid;
-    module = start->module;
+    module = lys_node_module(start);
 
-    if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL)) < 1) {
+    if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL, NULL, 0)) < 1) {
         return ((id - nodeid) - r) + 1;
     }
     id += r;
@@ -1760,24 +1956,22 @@
         sibling = NULL;
         while ((sibling = lys_getnext(sibling, start_parent, module,
                                       LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE | LYS_GETNEXT_PARENTUSES))) {
-            /* name match */
-            if (sibling->name && !strncmp(name, sibling->name, nam_len) && !sibling->name[nam_len]) {
-                r = schema_nodeid_siblingcheck(sibling, id, module, mod_name, mod_name_len);
-                if (r == 0) {
+            r = schema_nodeid_siblingcheck(sibling, module, mod_name, mod_name_len, name, nam_len);
+            if (r == 0) {
+                if (!id[0]) {
                     if (!(sibling->nodetype & ret_nodetype)) {
                         /* wrong node type, too bad */
                         continue;
                     }
                     *ret = sibling;
                     return EXIT_SUCCESS;
-                } else if (r == 1) {
-                    continue;
-                } else if (r == 2) {
-                    start_parent = sibling;
-                    break;
-                } else {
-                    return -1;
                 }
+                start_parent = sibling;
+                break;
+            } else if (r == 1) {
+                continue;
+            } else {
+                return -1;
             }
         }
 
@@ -1790,7 +1984,7 @@
             return -2;
         }
 
-        if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL)) < 1) {
+        if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL, NULL, 0)) < 1) {
             return ((id - nodeid) - r) + 1;
         }
         id += r;
@@ -1822,7 +2016,7 @@
     int i, mod_prefix_len, nam_len;
 
     /* parse the identifier, it must be parsed on one call */
-    if (((i = parse_node_identifier(nodeid, &mod_prefix, &mod_prefix_len, &name, &nam_len)) < 1) || nodeid[i]) {
+    if (((i = parse_node_identifier(nodeid, &mod_prefix, &mod_prefix_len, &name, &nam_len, NULL, 0)) < 1) || nodeid[i]) {
         return -i + 1;
     }
 
@@ -1854,7 +2048,7 @@
     id = nodeid;
     start_parent = NULL;
 
-    if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL)) < 1) {
+    if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL, NULL, 0)) < 1) {
         return ((id - nodeid) - r) + 1;
     }
     id += r;
@@ -1872,24 +2066,22 @@
         sibling = NULL;
         while ((sibling = lys_getnext(sibling, start_parent, abs_start_mod, LYS_GETNEXT_WITHCHOICE
                                       | LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHINOUT | LYS_GETNEXT_WITHGROUPING))) {
-            /* name match */
-            if (sibling->name && !strncmp(name, sibling->name, nam_len) && !sibling->name[nam_len]) {
-                r = schema_nodeid_siblingcheck(sibling, id, module, mod_name, mod_name_len);
-                if (r == 0) {
+            r = schema_nodeid_siblingcheck(sibling, module, mod_name, mod_name_len, name, nam_len);
+            if (r == 0) {
+                if (!id[0]) {
                     if (!(sibling->nodetype & ret_nodetype)) {
                         /* wrong node type, too bad */
                         continue;
                     }
                     *ret = sibling;
                     return EXIT_SUCCESS;
-                } else if (r == 1) {
-                    continue;
-                } else if (r == 2) {
-                    start_parent = sibling;
-                    break;
-                } else {
-                    return -1;
                 }
+                start_parent = sibling;
+                break;
+            } else if (r == 1) {
+                continue;
+            } else {
+                return -1;
             }
         }
 
@@ -1899,7 +2091,7 @@
             return EXIT_SUCCESS;
         }
 
-        if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL)) < 1) {
+        if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL, NULL, 0)) < 1) {
             return ((id - nodeid) - r) + 1;
         }
         id += r;
@@ -1911,8 +2103,7 @@
 }
 
 static int
-resolve_json_schema_list_predicate(const char *predicate, const struct lys_node_list *list,
-                                   const struct lys_module *cur_module, int *parsed)
+resolve_json_schema_list_predicate(const char *predicate, const struct lys_node_list *list, int *parsed)
 {
     const char *mod_name, *name;
     int mod_name_len, nam_len, has_predicate, i;
@@ -1931,15 +2122,7 @@
         for (i = 0; i < list->keys_size; ++i) {
             key = (struct lys_node *)list->keys[i];
             if (!strncmp(key->name, name, nam_len) && !key->name[nam_len]) {
-                if (mod_name) {
-                    if (!strncmp(lys_node_module(key)->name, mod_name, mod_name_len) && !lys_node_module(key)->name[mod_name_len]) {
-                        break;
-                    }
-                } else {
-                    if (!strcmp(lys_node_module(key)->name, cur_module->name)) {
-                        break;
-                    }
-                }
+                break;
             }
         }
 
@@ -1951,7 +2134,7 @@
 
     /* more predicates? */
     if (has_predicate) {
-        return resolve_json_schema_list_predicate(predicate, list, cur_module, parsed);
+        return resolve_json_schema_list_predicate(predicate, list, parsed);
     }
 
     return 0;
@@ -1966,7 +2149,7 @@
     const struct lys_node *sibling, *start_parent;
     int r, nam_len, mod_name_len, is_relative = -1, has_predicate;
     /* resolved import module from the start module, it must match the next node-name-match sibling */
-    const struct lys_module *prefix_mod, *cur_module;
+    const struct lys_module *prefix_mod, *module, *prev_mod;
 
     assert(nodeid && (ctx || start));
     if (!ctx) {
@@ -1975,7 +2158,7 @@
 
     id = nodeid;
 
-    if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate)) < 1) {
+    if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate, NULL, 0)) < 1) {
         LOGVAL(LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
         return NULL;
     }
@@ -1987,7 +2170,7 @@
         while (start_parent && (start_parent->nodetype == LYS_USES)) {
             start_parent = lys_parent(start_parent);
         }
-        cur_module = start->module;
+        module = start->module;
     } else {
         if (!mod_name) {
             str = strndup(nodeid, (name + nam_len) - nodeid);
@@ -2006,7 +2189,7 @@
 
         memmove(module_name, mod_name, mod_name_len);
         module_name[mod_name_len] = '\0';
-        cur_module = ly_ctx_get_module(ctx, module_name, NULL);
+        module = ly_ctx_get_module(ctx, module_name, NULL);
 
         if (buf_backup) {
             /* return previous internal buffer content */
@@ -2016,7 +2199,7 @@
         }
         ly_buf_used--;
 
-        if (!cur_module) {
+        if (!module) {
             str = strndup(nodeid, (mod_name + mod_name_len) - nodeid);
             LOGVAL(LYE_PATH_INMOD, LY_VLOG_STR, str);
             free(str);
@@ -2029,9 +2212,11 @@
         mod_name_len = 0;
     }
 
+    prev_mod = module;
+
     while (1) {
         sibling = NULL;
-        while ((sibling = lys_getnext(sibling, start_parent, cur_module,
+        while ((sibling = lys_getnext(sibling, start_parent, module,
                 LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHINOUT))) {
             /* name match */
             if (sibling->name && !strncmp(name, sibling->name, nam_len) && !sibling->name[nam_len]) {
@@ -2067,7 +2252,7 @@
                         return NULL;
                     }
                 } else {
-                    prefix_mod = cur_module;
+                    prefix_mod = prev_mod;
                 }
                 if (prefix_mod != lys_node_module(sibling)) {
                     continue;
@@ -2082,7 +2267,7 @@
                             return NULL;
                         }
                     } else if (sibling->nodetype == LYS_LIST) {
-                        if (resolve_json_schema_list_predicate(id, (const struct lys_node_list *)sibling, cur_module, &r)) {
+                        if (resolve_json_schema_list_predicate(id, (const struct lys_node_list *)sibling, &r)) {
                             return NULL;
                         }
                     } else {
@@ -2103,6 +2288,9 @@
                     return NULL;
                 }
                 start_parent = sibling;
+
+                /* update prev mod */
+                prev_mod = (start_parent->child ? lys_node_module(start_parent->child) : module);
                 break;
             }
         }
@@ -2115,7 +2303,7 @@
             return NULL;
         }
 
-        if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate)) < 1) {
+        if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate, NULL, 0)) < 1) {
             LOGVAL(LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
             return NULL;
         }
@@ -2129,7 +2317,7 @@
 
 static int
 resolve_partial_json_data_list_predicate(const char *predicate, const char *node_name, struct lyd_node *node,
-                                         int position, const struct lys_module *cur_module, int *parsed)
+                                         int position, int *parsed)
 {
     const char *mod_name, *name, *value, *key_val;
     int mod_name_len, nam_len, val_len, has_predicate = 1, r;
@@ -2194,13 +2382,21 @@
         }
 
         if (mod_name) {
+            /* specific module, check that the found key is from that module */
             if (strncmp(lyd_node_module((struct lyd_node *)key)->name, mod_name, mod_name_len)
                     || lyd_node_module((struct lyd_node *)key)->name[mod_name_len]) {
                 LOGVAL(LYE_PATH_INKEY, LY_VLOG_NONE, NULL, name);
                 return -1;
             }
+
+            /* but if the module is the same as the parent, it should have been omitted */
+            if (lyd_node_module((struct lyd_node *)key) == lyd_node_module(node)) {
+                LOGVAL(LYE_PATH_INKEY, LY_VLOG_NONE, NULL, name);
+                return -1;
+            }
         } else {
-            if (strcmp(lyd_node_module((struct lyd_node *)key)->name, cur_module->name)) {
+            /* no module, so it must be the same as the list (parent) */
+            if (lyd_node_module((struct lyd_node *)key) != lyd_node_module(node)) {
                 LOGVAL(LYE_PATH_INKEY, LY_VLOG_NONE, NULL, name);
                 return -1;
             }
@@ -2250,7 +2446,7 @@
     int has_predicate, last_parsed, llval_len, pred_name_len, last_has_pred;
     struct lyd_node *sibling, *last_match = NULL;
     struct lyd_node_leaf_list *llist;
-    const struct lys_module *prefix_mod, *cur_module;
+    const struct lys_module *prefix_mod, *prev_mod;
     struct ly_ctx *ctx;
 
     assert(nodeid && start && parsed);
@@ -2258,7 +2454,7 @@
     ctx = start->schema->module->ctx;
     id = nodeid;
 
-    if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate)) < 1) {
+    if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate, NULL, 0)) < 1) {
         LOGVAL(LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
         *parsed = -1;
         return NULL;
@@ -2268,11 +2464,11 @@
     last_parsed = r;
 
     if (is_relative) {
-        cur_module = lyd_node_module(start);
+        prev_mod = lyd_node_module(start);
         start = start->child;
     } else {
         for (; start->parent; start = start->parent);
-        cur_module = lyd_node_module(start);
+        prev_mod = lyd_node_module(start);
     }
 
     while (1) {
@@ -2333,7 +2529,7 @@
                         return NULL;
                     }
                 } else {
-                    prefix_mod = cur_module;
+                    prefix_mod = prev_mod;
                 }
                 if (prefix_mod != lyd_node_module(sibling)) {
                     continue;
@@ -2390,7 +2586,7 @@
 
                     ++list_instance_position;
                     r = 0;
-                    ret = resolve_partial_json_data_list_predicate(id, name, sibling, list_instance_position, cur_module, &r);
+                    ret = resolve_partial_json_data_list_predicate(id, name, sibling, list_instance_position, &r);
                     if (ret == -1) {
                         *parsed = -1;
                         return NULL;
@@ -2416,6 +2612,7 @@
                     return NULL;
                 }
                 last_match = sibling;
+                prev_mod = lyd_node_module(sibling);
                 start = sibling->child;
                 break;
             }
@@ -2426,7 +2623,7 @@
             return last_match;
         }
 
-        if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate)) < 1) {
+        if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate, NULL, 0)) < 1) {
             LOGVAL(LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
             *parsed = -1;
             return NULL;
@@ -3973,6 +4170,7 @@
     int rc;
     struct lys_node *sub;
     struct lys_module *mod;
+    struct ly_set *set;
 
     assert(aug);
     mod = lys_main_module(aug->module);
@@ -3983,18 +4181,17 @@
     /* it can already be resolved in case we returned EXIT_FAILURE from if block below */
     if (!aug->target) {
         /* resolve target node */
-        rc = resolve_augment_schema_nodeid(aug->target_name, siblings, (siblings ? NULL : aug->module),
-                                        (const struct lys_node **)&aug->target);
+        rc = resolve_schema_nodeid(aug->target_name, siblings, (siblings ? NULL : lys_node_module((struct lys_node *)aug)), &set, 0, 0);
         if (rc == -1) {
-            return -1;
-        } else if (rc > 0) {
-            LOGVAL(LYE_INCHAR, LY_VLOG_LYS, aug, aug->target_name[rc - 1], &aug->target_name[rc - 1]);
+            LOGVAL(LYE_PATH, LY_VLOG_LYS, aug);
             return -1;
         }
-        if (!aug->target) {
+        if (!set) {
             LOGVAL(LYE_INRESOLV, LY_VLOG_LYS, aug, "augment", aug->target_name);
             return EXIT_FAILURE;
         }
+        aug->target = set->set.s[0];
+        ly_set_free(set);
     }
 
     /* check for mandatory nodes - if the target node is in another module
@@ -5016,7 +5213,7 @@
 resolve_identref(struct lys_type *type, const char *ident_name, struct lyd_node *node, struct lys_module *mod, int dflt)
 {
     const char *mod_name, *name;
-    int mod_name_len, rc, i, j;
+    int mod_name_len, nam_len, rc, i, j;
     int make_implemented = 0;
     unsigned int u;
     struct lys_ident *der, *cur;
@@ -5028,7 +5225,7 @@
         return NULL;
     }
 
-    rc = parse_node_identifier(ident_name, &mod_name, &mod_name_len, &name, NULL);
+    rc = parse_node_identifier(ident_name, &mod_name, &mod_name_len, &name, &nam_len, NULL, 0);
     if (rc < 1) {
         LOGVAL(LYE_INCHAR, LY_VLOG_LYD, node, ident_name[-rc], &ident_name[-rc]);
         return NULL;
@@ -7003,7 +7200,7 @@
     }
 
     /* find the first schema node */
-    set = lys_find_xpath(sleaf, buf, 0);
+    set = lys_find_path(NULL, sleaf, buf);
     if (!set || !set->number) {
         free(buf);
         ly_set_free(set);
@@ -7155,7 +7352,7 @@
     *ret = NULL;
 
     /* syntax was already checked, so just evaluate the path using standard XPath */
-    set = lyd_find_xpath((struct lyd_node *)leaf, path);
+    set = lyd_find_path((struct lyd_node *)leaf, path);
     if (!set) {
         return -1;
     }
diff --git a/src/resolve.h b/src/resolve.h
index 4df8c10..65c1348 100644
--- a/src/resolve.h
+++ b/src/resolve.h
@@ -143,7 +143,7 @@
 int parse_identifier(const char *id);
 
 int parse_schema_nodeid(const char *id, const char **mod_name, int *mod_name_len, const char **name, int *nam_len,
-                        int *is_relative, int *has_predicate);
+                        int *is_relative, int *has_predicate, int *all_desc, int extended);
 
 int parse_schema_json_predicate(const char *id, const char **mod_name, int *mod_name_len, const char **name,
                                 int *nam_len, const char **value, int *val_len, int *has_predicate);
@@ -164,8 +164,8 @@
 
 struct lyd_node *resolve_data_descendant_schema_nodeid(const char *nodeid, struct lyd_node *start);
 
-int resolve_augment_schema_nodeid(const char *nodeid, const struct lys_node *start, const struct lys_module *module,
-                                  const struct lys_node **ret);
+int resolve_schema_nodeid(const char *nodeid, const struct lys_node *start, const struct lys_module *cur_module,
+                          struct ly_set **ret, int extended, int no_node_error);
 
 int resolve_descendant_schema_nodeid(const char *nodeid, const struct lys_node *start, int ret_nodetype,
                                      int no_innerlist, const struct lys_node **ret);
@@ -237,7 +237,7 @@
 void unres_data_del(struct unres_data *unres, uint32_t i);
 
 int resolve_unres_data(struct unres_data *unres, struct lyd_node **root, int options);
-int schema_nodeid_siblingcheck(const struct lys_node *sibling, const char *id, const struct lys_module *module,
-                               const char *mod_name, int mod_name_len);
+int schema_nodeid_siblingcheck(const struct lys_node *sibling, const struct lys_module *cur_module,
+                           const char *mod_name, int mod_name_len, const char *name, int nam_len);
 
 #endif /* _RESOLVE_H */
diff --git a/src/tree_data.c b/src/tree_data.c
index 9a12c6a..b6e185f 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -1028,8 +1028,7 @@
 }
 
 static int
-lyd_new_path_list_predicate(struct lyd_node *list, const char *list_name, const char *predicate,
-                            const struct lys_module *cur_module, int *parsed)
+lyd_new_path_list_predicate(struct lyd_node *list, const char *list_name, const char *predicate, int *parsed)
 {
     const char *mod_name, *name, *value;
     char *key_val;
@@ -1079,7 +1078,7 @@
         *parsed += r;
         predicate += r;
 
-        if (!value || (!mod_name && strcmp(lys_node_module(key)->name, cur_module->name))
+        if (!value || (!mod_name && (lys_node_module(key) != lys_node_module((struct lys_node *)slist)))
                 || (mod_name && (strncmp(lys_node_module(key)->name, mod_name, mod_name_len) || lys_node_module(key)->name[mod_name_len]))
                 || strncmp(key->name, name, nam_len) || key->name[nam_len]) {
             LOGVAL(LYE_PATH_INKEY, LY_VLOG_NONE, NULL, name);
@@ -1205,7 +1204,7 @@
     struct lyd_node *ret = NULL, *node, *parent = NULL;
     const struct lys_node *schild, *sparent, *tmp;
     const struct lys_node_list *slist;
-    const struct lys_module *cur_module;
+    const struct lys_module *module, *prev_mod;
     int r, i, parsed = 0, mod_name_len, nam_len, val_name_len, val_len;
     int is_relative = -1, has_predicate, first_iter = 1;
 
@@ -1246,7 +1245,7 @@
         }
     }
 
-    if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate)) < 1) {
+    if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate, NULL, 0)) < 1) {
         LOGVAL(LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
         return NULL;
     }
@@ -1263,7 +1262,7 @@
             parent = data_tree;
         }
         sparent = parent->schema;
-        cur_module = lys_node_module(sparent);
+        module = prev_mod = lys_node_module(sparent);
     } else {
         /* we are starting from scratch, absolute path */
         assert(!parent);
@@ -1284,7 +1283,7 @@
 
         memmove(module_name, mod_name, mod_name_len);
         module_name[mod_name_len] = '\0';
-        cur_module = ly_ctx_get_module(ctx, module_name, NULL);
+        module = ly_ctx_get_module(ctx, module_name, NULL);
 
         if (buf_backup) {
             /* return previous internal buffer content */
@@ -1293,7 +1292,7 @@
         }
         ly_buf_used--;
 
-        if (!cur_module) {
+        if (!module) {
             str = strndup(path, (mod_name + mod_name_len) - path);
             LOGVAL(LYE_PATH_INMOD, LY_VLOG_STR, str);
             free(str);
@@ -1301,6 +1300,7 @@
         }
         mod_name = NULL;
         mod_name_len = 0;
+        prev_mod = module;
 
         sparent = NULL;
     }
@@ -1309,7 +1309,7 @@
     while (1) {
         /* find the schema node */
         schild = NULL;
-        while ((schild = lys_getnext(schild, sparent, cur_module, 0))) {
+        while ((schild = lys_getnext(schild, sparent, module, 0))) {
             if (schild->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST
                                     | LYS_ANYDATA | LYS_NOTIF | LYS_RPC | LYS_ACTION)) {
                 /* module comparison */
@@ -1318,7 +1318,7 @@
                     if (strncmp(node_mod_name, mod_name, mod_name_len) || node_mod_name[mod_name_len]) {
                         continue;
                     }
-                } else if (lys_node_module(schild) != cur_module) {
+                } else if (lys_node_module(schild) != prev_mod) {
                     continue;
                 }
 
@@ -1453,8 +1453,7 @@
         }
 
         parsed = 0;
-        if ((schild->nodetype == LYS_LIST) && has_predicate
-                && lyd_new_path_list_predicate(node, name, id, cur_module, &parsed)) {
+        if ((schild->nodetype == LYS_LIST) && has_predicate && lyd_new_path_list_predicate(node, name, id, &parsed)) {
             lyd_free(ret);
             return NULL;
         }
@@ -1474,9 +1473,10 @@
         /* prepare for another iteration */
         parent = node;
         sparent = schild;
+        prev_mod = lys_node_module(schild);
 
         /* parse another node */
-        if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate)) < 1) {
+        if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate, NULL, 0)) < 1) {
             LOGVAL(LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
             lyd_free(ret);
             return NULL;
@@ -5035,7 +5035,7 @@
         case LYS_CONTAINER:
             if (last) {
                 /* find instance in the data */
-                r = lyd_find_xpath(last, parent->name);
+                r = lyd_find_path(last, parent->name);
                 if (!r || r->number > 1) {
                     ly_set_free(r);
                     LOGINT;
@@ -5093,8 +5093,8 @@
     return dflt;
 }
 
-static char *
-_lyd_path(const struct lyd_node *node, int prefix_all)
+API char *
+lyd_path(const struct lyd_node *node)
 {
     char *buf_backup = NULL, *buf = ly_buf(), *result = NULL;
     uint16_t index = LY_BUF_SIZE - 1;
@@ -5112,7 +5112,7 @@
 
     /* build the path */
     buf[index] = '\0';
-    ly_vlog_build_path_reverse(LY_VLOG_LYD, node, buf, &index, prefix_all);
+    ly_vlog_build_path_reverse(LY_VLOG_LYD, node, buf, &index);
     result = strdup(&buf[index]);
     if (!result) {
         LOGMEM;
@@ -5129,18 +5129,6 @@
     return result;
 }
 
-API char *
-lyd_path(const struct lyd_node *node)
-{
-    return _lyd_path(node, 0);
-}
-
-API char *
-lyd_qualified_path(const struct lyd_node *node)
-{
-    return _lyd_path(node, 1);
-}
-
 static int
 lyd_build_relative_data_path(const struct lys_module *module, const struct lyd_node *node, const char *schema_id,
                              char *buf)
@@ -5154,7 +5142,7 @@
     schema = node->schema;
 
     while (*schema_id) {
-        if ((r = parse_schema_nodeid(schema_id, &mod_name, &mod_name_len, &name, &name_len, &is_relative, NULL)) < 1) {
+        if ((r = parse_schema_nodeid(schema_id, &mod_name, &mod_name_len, &name, &name_len, &is_relative, NULL, NULL, 0)) < 1) {
             LOGINT;
             return -1;
         }
@@ -5162,17 +5150,14 @@
 
         snode = NULL;
         while ((snode = lys_getnext(snode, schema, NULL, LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE))) {
-            /* name match */
-            if (!strncmp(name, snode->name, name_len) && !snode->name[name_len]) {
-                r = schema_nodeid_siblingcheck(snode, schema_id, module, mod_name, mod_name_len);
-                if (r == 0 || r == 2) {
-                    schema = snode;
-                    break;
-                } else if (r == 1) {
-                    continue;
-                } else {
-                    return -1;
-                }
+            r = schema_nodeid_siblingcheck(snode, module, mod_name, mod_name_len, name, name_len);
+            if (r == 0) {
+                schema = snode;
+                break;
+            } else if (r == 1) {
+                continue;
+            } else {
+                return -1;
             }
         }
         /* no match */
@@ -5295,8 +5280,8 @@
                     idx1 = idx2 = LY_BUF_SIZE - 1;
                     path1[idx1] = '\0';
                     path2[idx2] = '\0';
-                    ly_vlog_build_path_reverse(LY_VLOG_LYD, first, path1, &idx1, 0);
-                    ly_vlog_build_path_reverse(LY_VLOG_LYD, second, path2, &idx2, 0);
+                    ly_vlog_build_path_reverse(LY_VLOG_LYD, first, path1, &idx1);
+                    ly_vlog_build_path_reverse(LY_VLOG_LYD, second, path2, &idx2);
 
                     /* use internal buffer to rebuild the unique string */
                     if (ly_buf_used && uniq_str[0]) {
@@ -5373,28 +5358,34 @@
 }
 
 API struct ly_set *
-lyd_find_xpath(const struct lyd_node *ctx_node, const char *expr)
+lyd_find_path(const struct lyd_node *ctx_node, const char *path)
 {
     struct lyxp_set xp_set;
     struct ly_set *set;
+    char *yang_xpath;
     uint16_t i;
 
-    if (!ctx_node || !expr) {
+    if (!ctx_node || !path) {
         ly_errno = LY_EINVAL;
         return NULL;
     }
 
+    /* transform JSON into YANG XPATH */
+    yang_xpath = transform_json2xpath(lyd_node_module(ctx_node), path);
+    if (!yang_xpath) {
+        return NULL;
+    }
+
     memset(&xp_set, 0, sizeof xp_set);
 
-    if (lyxp_eval(expr, ctx_node, LYXP_NODE_ELEM, lyd_node_module(ctx_node), &xp_set, 0) != EXIT_SUCCESS) {
+    if (lyxp_eval(yang_xpath, ctx_node, LYXP_NODE_ELEM, lyd_node_module(ctx_node), &xp_set, 0) != EXIT_SUCCESS) {
+        free(yang_xpath);
         return NULL;
     }
+    free(yang_xpath);
 
     set = ly_set_new();
-    if (!set) {
-        LOGMEM;
-        return NULL;
-    }
+    LY_CHECK_ERR_RETURN(!set, LOGMEM, NULL);
 
     if (xp_set.type == LYXP_SET_NODE_SET) {
         for (i = 0; i < xp_set.used; ++i) {
diff --git a/src/tree_data.h b/src/tree_data.h
index 9519108..93bd6c9 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -378,7 +378,7 @@
 /**@} diffoptions */
 
 /**
- * @brief Build path (usable as XPath) of the data node.
+ * @brief Build data path (usable as path, see @ref howtoxpath) of the data node.
  * @param[in] node Data node to be processed. Note that the node should be from a complete data tree, having a subtree
  *            (after using lyd_unlink()) can cause generating invalid paths.
  * @return NULL on error, on success the buffer for the resulting path is allocated and caller is supposed to free it
@@ -387,16 +387,6 @@
 char *lyd_path(const struct lyd_node *node);
 
 /**
- * @brief Build path (usable as instance-identified) of the data node with all the nodes fully qualified (having their
- * model as prefix).
- * @param[in] node Data node to be processed. Note that the node should be from a complete data tree, having a subtree
- *            (after using lyd_unlink()) can cause generating invalid paths.
- * @return NULL on error, on success the buffer for the resulting path is allocated and caller is supposed to free it
- * with free().
- */
-char *lyd_qualified_path(const struct lyd_node *node);
-
-/**
  * @defgroup parseroptions Data parser options
  * @ingroup datatree
  *
@@ -784,13 +774,12 @@
  * @param[in] data_tree Existing data tree to add to/modify. If creating RPCs/actions, there should only be one
  * RPC/action and either input or output, not both. Can be NULL.
  * @param[in] ctx Context to use. Mandatory if \p data_tree is NULL.
- * @param[in] path Simple absolute data XPath of the new node. It can contain only simple node addressing with optional
- * module names as prefixes. List nodes can have predicates, one for each list key in the correct order and
- * with its value as well or using specific instance position, leaves and leaf-lists can have predicates too that
- * have preference over \p value, see @ref howtoxpath.
+ * @param[in] path Simple data path (see @ref howtoxpath). List nodes can have predicates, one for each list key
+ * in the correct order and with its value as well or using specific instance position, leaves and leaf-lists
+ * can have predicates too that have preference over \p value.
  * @param[in] value Value of the new leaf/lealf-list (const char*). If creating anydata or anyxml, the following
- *            \p value_type parameter is required to be specified correctly. If creating nodes of other types, the
- *            parameter is ignored.
+ * \p value_type parameter is required to be specified correctly. If creating nodes of other types, the
+ * parameter is ignored.
  * @param[in] value_type Type of the provided \p value parameter in case of creating anydata or anyxml node.
  * @param[in] options Bitmask of options flags, see @ref pathoptions.
  * @return First created (or updated with #LYD_PATH_OPT_UPDATE) node,
@@ -1000,20 +989,16 @@
 int lyd_schema_sort(struct lyd_node *sibling, int recursive);
 
 /**
- * @brief Search in the given data for instances of nodes matching the provided XPath expression.
+ * @brief Search in the given data for instances of nodes matching the provided path.
  *
- * The XPath expression is evaluated on data -> skip all non-data nodes (input, output, choice, case).
+ * Learn more about the path format on page @ref howtoxpath.
  *
- * Expr examples:
- *      "/modules-state/module[name = 'ietf-yang-library']/namespace" with context node "ietf-yang-library:modules-state"
- *      "/ietf-netconf:get-config/ietf-netconf:source" with an arbitrary context node (all node names are prefixed)
- *
- * @param[in] ctx_node Context node.
- * @param[in] expr XPath expression filtering the matching nodes.
- * @return Set of found data nodes. If no nodes are matching \p expr or the result
+ * @param[in] ctx_node Path context node.
+ * @param[in] path Data path expression filtering the matching nodes.
+ * @return Set of found data nodes. If no nodes are matching \p path or the result
  * would be a number, a string, or a boolean, the returned set is empty. In case of an error, NULL is returned.
  */
-struct ly_set *lyd_find_xpath(const struct lyd_node *ctx_node, const char *expr);
+struct ly_set *lyd_find_path(const struct lyd_node *ctx_node, const char *path);
 
 /**
  * @brief Search in the given data for instances of the provided schema node.
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 2ca4352..9ceec0f 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -3843,65 +3843,6 @@
 #endif
 
 API struct ly_set *
-lys_find_xpath(const struct lys_node *ctx_node, const char *expr, int options)
-{
-    struct lyxp_set set;
-    struct ly_set *ret_set;
-    uint32_t i;
-    int opts;
-
-    if (!ctx_node || !expr) {
-        ly_errno = LY_EINVAL;
-        return NULL;
-    }
-
-    memset(&set, 0, sizeof set);
-
-    opts = LYXP_SNODE;
-    if (options & LYS_FIND_OUTPUT) {
-        opts |= LYXP_SNODE_OUTPUT;
-    }
-
-    if (lyxp_atomize(expr, ctx_node, LYXP_NODE_ELEM, &set, opts, NULL)) {
-        /* just find a relevant node to put in path, if it fails, use the original one */
-        for (i = 0; i < set.used; ++i) {
-            if (set.val.snodes[i].in_ctx == 1) {
-                ctx_node = set.val.snodes[i].snode;
-                break;
-            }
-        }
-        free(set.val.snodes);
-        LOGVAL(LYE_SPEC, LY_VLOG_LYS, ctx_node, "Resolving XPath expression \"%s\" failed.", expr);
-        return NULL;
-    }
-
-    ret_set = ly_set_new();
-
-    for (i = 0; i < set.used; ++i) {
-        if (!set.val.snodes[i].in_ctx) {
-            continue;
-        }
-        assert(set.val.snodes[i].in_ctx == 1);
-
-        switch (set.val.snodes[i].type) {
-        case LYXP_NODE_ELEM:
-            if (ly_set_add(ret_set, set.val.snodes[i].snode, LY_SET_OPT_USEASLIST) == -1) {
-                ly_set_free(ret_set);
-                free(set.val.snodes);
-                return NULL;
-            }
-            break;
-        default:
-            /* ignore roots, text and attr should not ever appear */
-            break;
-        }
-    }
-
-    free(set.val.snodes);
-    return ret_set;
-}
-
-API struct ly_set *
 lys_xpath_atomize(const struct lys_node *ctx_node, enum lyxp_node_type ctx_node_type, const char *expr, int options)
 {
     struct lyxp_set set;
@@ -4140,6 +4081,7 @@
     int ret;
     char *parent_path;
     struct lys_node *target = NULL, *parent;
+    struct ly_set *set;
 
     if (!dev->deviate) {
         return;
@@ -4178,12 +4120,16 @@
                 } else {
                     /* non-augment, non-toplevel */
                     parent_path = strndup(dev->target_name, strrchr(dev->target_name, '/') - dev->target_name);
-                    ret = resolve_augment_schema_nodeid(parent_path, NULL, module, (const struct lys_node **)&target);
+                    ret = resolve_schema_nodeid(parent_path, NULL, module, &set, 0, 1);
                     free(parent_path);
-                    if (ret || !target) {
+                    if (ret == -1) {
                         LOGINT;
+                        ly_set_free(set);
                         return;
                     }
+                    target = set->set.s[0];
+                    ly_set_free(set);
+
                     lys_node_addchild(target, NULL, dev->orig_node);
                 }
             } else {
@@ -4194,11 +4140,14 @@
             dev->orig_node = NULL;
         } else {
             /* adding not-supported deviation */
-            ret = resolve_augment_schema_nodeid(dev->target_name, NULL, module, (const struct lys_node **)&target);
-            if (ret || !target) {
+            ret = resolve_schema_nodeid(dev->target_name, NULL, module, &set, 0, 1);
+            if (ret == -1) {
                 LOGINT;
+                ly_set_free(set);
                 return;
             }
+            target = set->set.s[0];
+            ly_set_free(set);
 
             /* unlink and store the original node */
             parent = target->parent;
@@ -4212,11 +4161,14 @@
             dev->orig_node = target;
         }
     } else {
-        ret = resolve_augment_schema_nodeid(dev->target_name, NULL, module, (const struct lys_node **)&target);
-        if (ret || !target) {
+        ret = resolve_schema_nodeid(dev->target_name, NULL, module, &set, 0, 1);
+        if (ret == -1) {
             LOGINT;
+            ly_set_free(set);
             return;
         }
+        target = set->set.s[0];
+        ly_set_free(set);
 
         lys_node_switch(target, dev->orig_node);
         dev->orig_node = target;
@@ -4604,7 +4556,7 @@
 
     /* build the path */
     buf[index] = '\0';
-    ly_vlog_build_path_reverse(LY_VLOG_LYS, node, buf, &index, 0);
+    ly_vlog_build_path_reverse(LY_VLOG_LYS, node, buf, &index);
     result = strdup(&buf[index]);
     if (!result) {
         LOGMEM;
@@ -4621,6 +4573,59 @@
     return result;
 }
 
+API char *
+lys_data_path(const struct lys_node *node)
+{
+    char *buf_backup = NULL, *buf = ly_buf(), *result = NULL;
+    int i, used;
+    struct ly_set *set;
+    const struct lys_module *prev_mod;
+
+    if (!node) {
+        LOGERR(LY_EINVAL, "%s: NULL node parameter", __func__);
+        return NULL;
+    }
+
+    /* backup the shared internal buffer */
+    if (ly_buf_used && buf[0]) {
+        buf_backup = strndup(buf, LY_BUF_SIZE - 1);
+    }
+    ly_buf_used++;
+
+    set = ly_set_new();
+    LY_CHECK_ERR_GOTO(!set, LOGMEM, error);
+
+    while (node) {
+        ly_set_add(set, (void *)node, 0);
+        do {
+            node = lys_parent(node);
+        } while (node && (node->nodetype & (LYS_USES | LYS_CHOICE | LYS_CASE | LYS_INPUT | LYS_OUTPUT)));
+    }
+
+    prev_mod = NULL;
+    used = 0;
+    for (i = set->number - 1; i > -1; --i) {
+        node = set->set.s[i];
+        used += sprintf(buf + used, "/%s%s%s", (lys_node_module(node) == prev_mod ? "" : lys_node_module(node)->name),
+                        (lys_node_module(node) == prev_mod ? "" : ":"), node->name);
+        prev_mod = lys_node_module(node);
+    }
+
+    result = strdup(buf);
+    LY_CHECK_ERR_GOTO(!result, LOGMEM, error);
+
+error:
+    ly_set_free(set);
+    /* restore the shared internal buffer */
+    if (buf_backup) {
+        strcpy(buf, buf_backup);
+        free(buf_backup);
+    }
+    ly_buf_used--;
+
+    return result;
+}
+
 struct lys_node_augment *
 lys_getnext_target_aug(struct lys_node_augment *last, const struct lys_module *mod, const struct lys_node *aug_target)
 {
@@ -4674,6 +4679,24 @@
     return NULL;
 }
 
+API struct ly_set *
+lys_find_path(const struct lys_module *cur_module, const struct lys_node *cur_node, const char *path)
+{
+    struct ly_set *ret;
+    int rc;
+
+    if ((!cur_module && !cur_node) || !path) {
+        return NULL;
+    }
+
+    rc = resolve_schema_nodeid(path, cur_node, cur_module, &ret, 1, 1);
+    if (rc == -1) {
+        return NULL;
+    }
+
+    return ret;
+}
+
 static void
 lys_extcomplex_free_str(struct ly_ctx *ctx, struct lys_ext_instance_complex *ext, LY_STMT stmt)
 {
diff --git a/src/tree_schema.h b/src/tree_schema.h
index fc0fc5f..4a296be 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -2102,21 +2102,18 @@
 #define LYS_GETNEXT_PARENTUSES   0x80 /**< lys_getnext() option to allow parent to be #LYS_USES, in which case only the direct children are traversed */
 
 /**
- * @brief Search for schema nodes matching the provided XPath expression.
+ * @brief Search for schema nodes matching the provided path.
  *
- * XPath always requires a context node to be able to evaluate an expression because
- * non-prefixed node names will use its module prefix. However, if \p expr is absolute
- * and all the node names are prefixed, the context node can be an arbitrary node.
+ * Learn more about the path format at page @ref howtoxpath.
+ * Either \p cur_module or \p cur_node must be set.
  *
- * @param[in] ctx_node Context schema node.
- * @param[in] expr XPath expression filtering the matching nodes.
- * @param[in] options Bitmask of LYS_FIND_* options.
- * @return Set of found schema nodes. If no nodes are matching \p expr or the result
- * would be a number, a string, or a boolean, the returned set is empty. In case of an error, NULL is returned.
+ * @param[in] cur_module Current module name.
+ * @param[in] cur_node Current (context) schema node.
+ * @param[in] path Schema path expression filtering the matching nodes.
+ * @return Set of found schema nodes. If no nodes are matching \p path the returned set is empty.
+ * In case of an error, NULL is returned.
  */
-struct ly_set *lys_find_xpath(const struct lys_node *ctx_node, const char *expr, int options);
-
-#define LYS_FIND_OUTPUT 0x01 /**< lys_find_xpath() option to search RPC output nodes instead input ones */
+struct ly_set *lys_find_path(const struct lys_module *cur_module, const struct lys_node *cur_node, const char *path);
 
 /**
  * @brief Types of context nodes, #LYXP_NODE_ROOT_CONFIG used only in when or must conditions.
@@ -2139,7 +2136,8 @@
  * and then this node can be any node from the module (so, for example, do not put node added by an augment from another module).
  * @param[in] ctx_node_type Context (current) schema node type. Most commonly is #LYXP_NODE_ELEM, but if
  * your context node is supposed to be the root, you can specify what kind of root it is.
- * @param[in] expr XPath expression to be evaluated. Must be in JSON data format (prefixes are model names).
+ * @param[in] expr XPath expression to be evaluated. Must be in JSON data format (prefixes are model names). Otherwise
+ * follows must or when YANG expression syntax (XPath 1.0).
  * @param[in] options Whether to apply some evaluation restrictions #LYXP_MUST or #LYXP_WHEN.
  *
  * @return Set of atoms (schema nodes), NULL on error.
@@ -2163,7 +2161,7 @@
 #define LYXP_NO_LOCAL 0x02  /**< lys_node_xpath_atomize() option to discard schema node dependencies from the local subtree */
 
 /**
- * @brief Build path (usable as XPath) of the schema node.
+ * @brief Build schema path (usable as path, see @ref howtoxpath) of the schema node.
  * @param[in] node Schema node to be processed.
  * @return NULL on error, on success the buffer for the resulting path is allocated and caller is supposed to free it
  * with free().
@@ -2171,6 +2169,14 @@
 char *lys_path(const struct lys_node *node);
 
 /**
+ * @brief Build data path (usable as path, see @ref howtoxpath) of the schema node.
+ * @param[in] node Schema node to be processed.
+ * @return NULL on error, on success the buffer for the resulting path is allocated and caller is supposed to free it
+ * with free().
+ */
+char *lys_data_path(const struct lys_node *node);
+
+/**
  * @brief Return parent node in the schema tree.
  *
  * In case of augmenting node, it returns the target tree node where the augmenting