data tree NEW opaque data node

Used for unknown anyxml/anydata nodes.
Some refactoring including making context
const for cases when only the dictionary
is modified or replacing unsigned int with
uint32_t.
diff --git a/src/common.h b/src/common.h
index bcc2426..2b58fbf 100644
--- a/src/common.h
+++ b/src/common.h
@@ -15,7 +15,6 @@
 #ifndef LY_COMMON_H_
 #define LY_COMMON_H_
 
-#define _DEFAULT_SOURCE
 #define _GNU_SOURCE
 
 #include <pthread.h>
@@ -445,7 +444,7 @@
  *
  * Implemented in parser_json.c
  */
-const struct lys_module *lydjson_resolve_prefix(struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *parser);
+const struct lys_module *lydjson_resolve_prefix(const struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *parser);
 
 /**
  * @brief mmap(2) wrapper to map input files into memory to unify parsing.
diff --git a/src/dict.h b/src/dict.h
index 35f9148..88e9647 100644
--- a/src/dict.h
+++ b/src/dict.h
@@ -47,7 +47,7 @@
  * byte is added automatically. If \p len is 0, it is count automatically using strlen().
  * @return pointer to the string stored in the dictionary, NULL if \p value was NULL.
  */
-const char *lydict_insert(struct ly_ctx *ctx, const char *value, size_t len);
+const char *lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len);
 
 /**
  * @brief Insert string into dictionary - zerocopy version. If the string is
@@ -63,7 +63,7 @@
  * value address anymore. If NULL, function does nothing.
  * @return pointer to the string stored in the dictionary, NULL if \p value was NULL.
  */
-const char *lydict_insert_zc(struct ly_ctx *ctx, char *value);
+const char *lydict_insert_zc(const struct ly_ctx *ctx, char *value);
 
 /**
  * @brief Remove specified string from the dictionary. It decrement reference
@@ -74,9 +74,9 @@
  * must match the stored value, but also the address is being compared and the
  * counter is decremented only if it matches. If NULL, function does nothing.
  */
-void lydict_remove(struct ly_ctx *ctx, const char *value);
+void lydict_remove(const struct ly_ctx *ctx, const char *value);
 
-/**@} dict */
+/** @} dict */
 
 #ifdef __cplusplus
 }
diff --git a/src/hash_table.c b/src/hash_table.c
index 2d9af2a..c115e38 100644
--- a/src/hash_table.c
+++ b/src/hash_table.c
@@ -124,7 +124,7 @@
 }
 
 API void
-lydict_remove(struct ly_ctx *ctx, const char *value)
+lydict_remove(const struct ly_ctx *ctx, const char *value)
 {
     size_t len;
     int ret;
@@ -143,7 +143,7 @@
     rec.value = (char *)value;
     rec.refcount = 0;
 
-    pthread_mutex_lock(&ctx->dict.lock);
+    pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
     /* set len as data for compare callback */
     lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
     /* check if value is already inserted */
@@ -168,11 +168,11 @@
     }
 
 finish:
-    pthread_mutex_unlock(&ctx->dict.lock);
+    pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
 }
 
 static char *
-dict_insert(struct ly_ctx *ctx, char *value, size_t len, int zerocopy)
+dict_insert(const struct ly_ctx *ctx, char *value, size_t len, int zerocopy)
 {
     struct dict_rec *match = NULL, rec;
     int ret = 0;
@@ -216,7 +216,7 @@
 }
 
 API const char *
-lydict_insert(struct ly_ctx *ctx, const char *value, size_t len)
+lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len)
 {
     const char *result;
 
@@ -226,23 +226,23 @@
         len = strlen(value);
     }
 
-    pthread_mutex_lock(&ctx->dict.lock);
+    pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
     result = dict_insert(ctx, (char *)value, len, 0);
-    pthread_mutex_unlock(&ctx->dict.lock);
+    pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
 
     return result;
 }
 
 API const char *
-lydict_insert_zc(struct ly_ctx *ctx, char *value)
+lydict_insert_zc(const struct ly_ctx *ctx, char *value)
 {
     const char *result;
 
     LY_CHECK_ARG_RET(ctx, ctx, value, NULL);
 
-    pthread_mutex_lock(&ctx->dict.lock);
+    pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
     result = dict_insert(ctx, value, strlen(value), 1);
-    pthread_mutex_unlock(&ctx->dict.lock);
+    pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
 
     return result;
 }
diff --git a/src/parser_json.c b/src/parser_json.c
index 400b078..5b59fed 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -34,7 +34,7 @@
  * in the values to the schema via context module names.
  */
 const struct lys_module *
-lydjson_resolve_prefix(struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *UNUSED(parser))
+lydjson_resolve_prefix(const struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *UNUSED(parser))
 {
     const struct lys_module *mod;
     char *name;
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 28238bf..34e3161 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -42,12 +42,12 @@
     struct ly_set elements;          /**< list of not-yet-closed elements */
     struct ly_set ns;                /**< handled with LY_SET_OPT_USEASLIST */
 
-    uint16_t options;                /**< various @ref dataparseroptions. */
-    uint16_t path_len;               /**< used bytes in the path buffer */
+    uint32_t options;                /**< various @ref dataparseroptions. */
+    uint32_t path_len;               /**< used bytes in the path buffer */
 #define LYD_PARSER_BUFSIZE 4078
     char path[LYD_PARSER_BUFSIZE];   /**< buffer for the generated path */
-    struct ly_set incomplete_type_validation; /**< set of nodes validated with LY_EINCOMPLETE result */
-    struct ly_set incomplete_type_validation_meta; /**< set of metdata validated with LY_EINCOMPLETE result */
+    struct ly_set unres_node_type;   /**< set of nodes validated with LY_EINCOMPLETE result */
+    struct ly_set unres_meta_type;   /**< set of metadata validated with LY_EINCOMPLETE result */
     struct ly_set when_check;        /**< set of nodes with "when" conditions */
 };
 
@@ -56,7 +56,7 @@
  * via XML namespaces.
  */
 static const struct lys_module *
-lydxml_resolve_prefix(struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *parser)
+lydxml_resolve_prefix(const struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *parser)
 {
     const struct lyxml_ns *ns;
     struct lyxml_context *xmlctx = (struct lyxml_context*)parser;
@@ -82,14 +82,14 @@
 /**
  * @brief Parse XML attributes of the XML element of YANG data.
  *
- * @param[in] ctx XML YANG data parser context.
+ * @param[in] xmlctx XML parser context.
  * @param[in,out] data Pointer to the XML string representation of the YANG data to parse.
  * @param[out] attributes Resulting list of the parsed attributes. XML namespace definitions are not parsed
  * as attributes, they are stored internally in the parser context.
  * @reutn LY_ERR value.
  */
 static LY_ERR
-lydxml_attributes_parse(struct lyd_xml_ctx *ctx, const char **data, struct ly_set *attrs_data)
+lydxml_attributes_parse(struct lyxml_context *xmlctx, const char **data, struct ly_set *attrs_data)
 {
     LY_ERR ret = LY_SUCCESS;
     unsigned int u;
@@ -97,8 +97,8 @@
     size_t prefix_len, name_len;
     struct attr_data_s *attr_data;
 
-    while(ctx->status == LYXML_ATTRIBUTE &&
-            lyxml_get_attribute((struct lyxml_context*)ctx, data, &prefix, &prefix_len, &name, &name_len) == LY_SUCCESS) {
+    while (xmlctx->status == LYXML_ATTRIBUTE &&
+            lyxml_get_attribute(xmlctx, data, &prefix, &prefix_len, &name, &name_len) == LY_SUCCESS) {
         char *buffer = NULL;
         size_t buffer_size = 0;
 
@@ -115,7 +115,7 @@
         attr_data->name = name;
         attr_data->prefix_len = prefix_len;
         attr_data->name_len = name_len;
-        ret = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &attr_data->value, &attr_data->value_len, &attr_data->dynamic);
+        ret = lyxml_get_string(xmlctx, data, &buffer, &buffer_size, &attr_data->value, &attr_data->value_len, &attr_data->dynamic);
         LY_CHECK_ERR_GOTO(ret, free(attr_data), error);
         ly_set_add(attrs_data, attr_data, LY_SET_OPT_USEASLIST);
     }
@@ -133,7 +133,8 @@
 }
 
 static LY_ERR
-lydxml_metadata(struct lyd_xml_ctx *ctx, struct ly_set *attrs_data, const struct lysc_node *sparent, struct lyd_meta **meta)
+lydxml_metadata(struct lyxml_context *xmlctx, struct ly_set *attrs_data, const struct lysc_node *sparent, int strict,
+                struct ly_set *type_meta_check,  struct lyd_meta **meta)
 {
     LY_ERR ret = LY_EVALID, rc;
     const struct lyxml_ns *ns;
@@ -145,8 +146,8 @@
         if (!attr_data->prefix_len) {
             /* in XML, all attributes must be prefixed
              * TODO exception for NETCONF filters which are supposed to map to the ietf-netconf without prefix */
-            if (ctx->options & LYD_OPT_STRICT) {
-                LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Missing mandatory prefix for XML metadata \"%.*s\".",
+            if (strict) {
+                LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_REFERENCE, "Missing mandatory prefix for XML metadata \"%.*s\".",
                        attr_data->name_len, attr_data->name);
             }
 skip_attr:
@@ -158,17 +159,18 @@
         }
 
         /* get namespace of the attribute to find its annotation definition */
-        ns = lyxml_ns_get((struct lyxml_context *)ctx, attr_data->prefix, attr_data->prefix_len);
+        ns = lyxml_ns_get(xmlctx, attr_data->prefix, attr_data->prefix_len);
         if (!ns) {
-            /* unknown namespace, ignore the attribute */
-            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", attr_data->prefix_len, attr_data->prefix);
+            /* unknown namespace, XML error */
+            LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".",
+                   attr_data->prefix_len, attr_data->prefix);
             goto cleanup;
         }
-        mod = ly_ctx_get_module_implemented_ns(ctx->ctx, ns->uri);
+        mod = ly_ctx_get_module_implemented_ns(xmlctx->ctx, ns->uri);
         if (!mod) {
             /* module is not implemented or not present in the schema */
-            if (ctx->options & LYD_OPT_STRICT) {
-                LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE,
+            if (strict) {
+                LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_REFERENCE,
                        "Unknown (or not implemented) YANG module with namespace \"%s\" for metadata \"%.*s%s%.*s\".",
                        ns, attr_data->prefix_len, attr_data->prefix, attr_data->prefix_len ? ":" : "", attr_data->name_len,
                        attr_data->name);
@@ -176,19 +178,21 @@
             goto skip_attr;
         }
 
-        rc = lyd_create_meta(NULL, meta, mod, attr_data->name, attr_data->name_len, attr_data->value,
-                             attr_data->value_len, &attr_data->dynamic, lydxml_resolve_prefix, ctx, LYD_XML, sparent);
+        rc = lyd_create_meta(NULL, meta, mod, attr_data->name, attr_data->name_len, attr_data->value, attr_data->value_len,
+                             &attr_data->dynamic, lydxml_resolve_prefix, xmlctx, LYD_XML, sparent);
         if (rc == LY_EINCOMPLETE) {
-            ly_set_add(&ctx->incomplete_type_validation_meta, meta, LY_SET_OPT_USEASLIST);
+            if (type_meta_check) {
+                ly_set_add(type_meta_check, meta, LY_SET_OPT_USEASLIST);
+            }
         } else if (rc) {
             ret = rc;
             goto cleanup;
         }
     }
+
     ret = LY_SUCCESS;
 
 cleanup:
-
     for (unsigned int u = 0; u < attrs_data->count; ++u) {
         if (((struct attr_data_s*)attrs_data->objs[u])->dynamic) {
             free(((struct attr_data_s*)attrs_data->objs[u])->value);
@@ -199,6 +203,62 @@
     return ret;
 }
 
+static LY_ERR
+lydxml_attrs(struct lyxml_context *xmlctx, struct ly_set *attrs_data, struct ly_attr **attr)
+{
+    LY_ERR ret = LY_SUCCESS;
+    const struct lyxml_ns *ns;
+    struct ly_prefix *val_prefs;
+    struct ly_attr *attr2;
+
+    assert(attr);
+
+    for (unsigned int u = 0; u < attrs_data->count; ++u) {
+        struct attr_data_s *attr_data = (struct attr_data_s *)attrs_data->objs[u];
+
+        ns = NULL;
+        if (attr_data->prefix_len) {
+            /* get namespace of the attribute */
+            ns = lyxml_ns_get(xmlctx, attr_data->prefix, attr_data->prefix_len);
+            if (!ns) {
+                LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".",
+                       attr_data->prefix_len, attr_data->prefix);
+                ret = LY_EVALID;
+                goto cleanup;
+            }
+        }
+
+        if (*attr) {
+            attr2 = *attr;
+        } else {
+            attr2 = NULL;
+        }
+
+        /* get value prefixes */
+        ret = lyxml_get_prefixes(xmlctx, attr_data->value, attr_data->value_len, &val_prefs);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* attr2 is always changed to the created attribute */
+        ret = ly_create_attr(NULL, &attr2, xmlctx->ctx, attr_data->name, attr_data->name_len, attr_data->value,
+                             attr_data->value_len, &attr_data->dynamic, LYD_XML, val_prefs, attr_data->prefix,
+                             attr_data->prefix_len, ns ? ns->uri : NULL);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        if (!*attr) {
+            *attr = attr2;
+        }
+    }
+
+cleanup:
+    for (unsigned int u = 0; u < attrs_data->count; ++u) {
+        if (((struct attr_data_s *)attrs_data->objs[u])->dynamic) {
+            free(((struct attr_data_s *)attrs_data->objs[u])->value);
+        }
+    }
+    ly_set_erase(attrs_data, free);
+    return ret;
+}
+
 /**
  * @brief Parse XML elements as YANG data node children the specified parent node.
  *
@@ -216,11 +276,14 @@
     size_t prefix_len, name_len;
     struct ly_set attrs_data = {0};
     const struct lyxml_ns *ns;
-    struct lyd_meta *meta = NULL, *meta2;
+    struct lyd_meta *meta = NULL, *meta2, *prev_meta;
+    struct ly_attr *attr = NULL;
     const struct lysc_node *snode;
     struct lys_module *mod;
     unsigned int parents_count = ctx->elements.count;
-    struct lyd_node *cur = NULL, *key_anchor;
+    uint32_t prev_opts;
+    struct lyd_node *cur = NULL, *anchor;
+    struct ly_prefix *val_prefs;
     int dynamic = 0;
     char *buffer = NULL, *value;
     size_t buffer_size = 0, value_len;
@@ -240,7 +303,7 @@
 
         if (ctx->status == LYXML_ATTRIBUTE) {
             /* first parse all attributes so we have all the namespaces available */
-            if (lydxml_attributes_parse(ctx, data, &attrs_data) != LY_SUCCESS) {
+            if (lydxml_attributes_parse((struct lyxml_context *)ctx, data, &attrs_data) != LY_SUCCESS) {
                 ret = LY_EVALID;
                 goto cleanup;
             }
@@ -253,37 +316,97 @@
             goto cleanup;
         }
         mod = ly_ctx_get_module_implemented_ns(ctx->ctx, ns->uri);
-        if (!mod) {
+        if (!mod && (ctx->options & LYD_OPT_STRICT)) {
             LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "No module with namespace \"%s\" in the context.", ns->uri);
             ret = LY_EVALID;
             goto cleanup;
         }
-        /* leave if-feature check for validation */
-        snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
-        if (!snode) {
-            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Element \"%.*s\" not found in the \"%s\" module.",
-                   name_len, name, mod->name);
-            ret = LY_EVALID;
-            goto cleanup;
-        }
-        if ((ctx->options & LYD_OPT_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
-            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LY_VCODE_INSTATE, snode->name);
-            return LY_EVALID;
+
+        snode = NULL;
+        if (mod && (!parent || parent->schema)) {
+            /* leave if-feature check for validation */
+            snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
+            if (!snode && (ctx->options & LYD_OPT_STRICT)) {
+                LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Element \"%.*s\" not found in the \"%s\" module.",
+                       name_len, name, mod->name);
+                ret = LY_EVALID;
+                goto cleanup;
+            }
+            if (snode) {
+                if ((ctx->options & LYD_OPT_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
+                    LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LY_VCODE_INSTATE, snode->name);
+                    ret = LY_EVALID;
+                    goto cleanup;
+                }
+                if (snode->nodetype & (LYS_ACTION | LYS_NOTIF)) {
+                    LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_DATA, "Unexpected %s element \"%.*s\".",
+                           snode->nodetype == LYS_ACTION ? "RPC/action" : "notification", name_len, name);
+                    ret = LY_EVALID;
+                    goto cleanup;
+                }
+            }
         }
 
         /* create actual metadata so that prefixes are available in the context */
         if (attrs_data.count) {
-            LY_CHECK_GOTO(ret = lydxml_metadata(ctx, &attrs_data, snode, &meta), cleanup);
+            if (snode) {
+                ret = lydxml_metadata((struct lyxml_context *)ctx, &attrs_data, snode, ctx->options & LYD_OPT_STRICT,
+                                    &ctx->unres_meta_type, &meta);
+                LY_CHECK_GOTO(ret, cleanup);
+            } else if (ctx->options & LYD_OPT_OPAQ) {
+                ret = lydxml_attrs((struct lyxml_context *)ctx, &attrs_data, &attr);
+                LY_CHECK_GOTO(ret, cleanup);
+            } else {
+                /* free attr data */
+                for (uint32_t u = 0; u < attrs_data.count; ++u) {
+                    if (((struct attr_data_s*)attrs_data.objs[u])->dynamic) {
+                        free(((struct attr_data_s*)attrs_data.objs[u])->value);
+                    }
+                }
+                ly_set_erase(&attrs_data, free);
+            }
         }
 
-        if (snode->nodetype & (LYS_ACTION | LYS_NOTIF)) {
-            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_DATA, "Unexpected %s element \"%.*s\".",
-                   snode->nodetype == LYS_ACTION ? "RPC/action" : "notification", name_len, name);
-            ret = LY_EVALID;
-            goto cleanup;
-        }
+        if (!snode) {
+            if (ctx->options & LYD_OPT_OPAQ) {
+                /* get the value */
+                LY_ERR r = LY_EINVAL;
+                if (ctx->status == LYXML_ELEM_CONTENT) {
+                    r = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &value, &value_len, &dynamic);
+                    if (r && (r != LY_EINVAL)) {
+                        LOGINT(ctx->ctx);
+                        ret = LY_EINT;
+                        goto cleanup;
+                    }
+                }
+                if (r == LY_EINVAL) {
+                    /* empty value */
+                    value = "";
+                    value_len = 0;
+                    dynamic = 0;
+                }
 
-        if (snode->nodetype & LYD_NODE_TERM) {
+                /* get value prefixes */
+                ret = lyxml_get_prefixes((struct lyxml_context *)ctx, value, value_len, &val_prefs);
+                LY_CHECK_GOTO(ret, cleanup);
+
+                /* create node */
+                ret = lyd_create_opaq(ctx->ctx, name, name_len, value, value_len, &dynamic, LYD_XML, val_prefs, prefix,
+                                      prefix_len, ns->uri, &cur);
+                LY_CHECK_GOTO(ret, cleanup);
+
+                /* process children */
+                if (ctx->status == LYXML_ELEMENT && parents_count != ctx->elements.count) {
+                    ret = lydxml_data_r(ctx, (struct lyd_node_inner *)cur, data, lyd_node_children_p(cur));
+                    LY_CHECK_GOTO(ret, cleanup);
+                }
+            } else {
+                /* skip element */
+                ret = lyxml_skip_element((struct lyxml_context *)ctx, data);
+                LY_CHECK_GOTO(ret, cleanup);
+                break;
+            }
+        } else if (snode->nodetype & LYD_NODE_TERM) {
             if (ctx->status == LYXML_ELEM_CONTENT) {
                 /* get the value */
                 ret = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &value, &value_len, &dynamic);
@@ -308,7 +431,7 @@
             buffer = NULL;
             if (ret == LY_EINCOMPLETE) {
                 if (!(ctx->options & LYD_OPT_PARSE_ONLY)) {
-                    ly_set_add(&ctx->incomplete_type_validation, cur, LY_SET_OPT_USEASLIST);
+                    ly_set_add(&ctx->unres_node_type, cur, LY_SET_OPT_USEASLIST);
                 }
             } else if (ret) {
                 goto cleanup;
@@ -316,8 +439,8 @@
 
             if (parent && (cur->schema->flags & LYS_KEY)) {
                 /* check the key order, the anchor must always be the last child */
-                key_anchor = lyd_get_prev_key_anchor(parent->child, cur->schema);
-                if ((!key_anchor && parent->child) || (key_anchor && key_anchor->next)) {
+                anchor = lyd_get_prev_key_anchor(parent->child, cur->schema);
+                if ((!anchor && parent->child) || (anchor && anchor->next)) {
                     if (ctx->options & LYD_OPT_STRICT) {
                         LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_DATA, "Invalid position of the key \"%s\" in a list.",
                                 cur->schema->name);
@@ -361,7 +484,7 @@
 
                 /* add any missing default children */
                 ret = lyd_validate_defaults_r((struct lyd_node_inner *)cur, lyd_node_children_p(cur), NULL, NULL,
-                                              &ctx->incomplete_type_validation, &ctx->when_check, ctx->options);
+                                              &ctx->unres_node_type, &ctx->when_check, ctx->options);
                 LY_CHECK_GOTO(ret, cleanup);
             }
 
@@ -370,80 +493,75 @@
                 lyd_hash(cur);
             }
         } else if (snode->nodetype & LYD_NODE_ANY) {
-            unsigned int cur_element_index = ctx->elements.count;
-            const char *start = *data, *stop;
-            const char *p, *n;
-            size_t p_len, n_len;
-
-            /* skip children data and store them as a string */
-            while (ctx->status != LYXML_END && cur_element_index <= ctx->elements.count) {
-                switch (ctx->status) {
-                case LYXML_ELEMENT:
-                    ret = lyxml_get_element((struct lyxml_context *)ctx, data, &p, &p_len, &n, &n_len);
-                    LY_CHECK_GOTO(ret, cleanup);
-                    break;
-                case LYXML_ATTRIBUTE:
-                    lyxml_get_attribute((struct lyxml_context *)ctx, data, &p, &p_len, &n, &n_len);
-                    break;
-                case LYXML_ELEM_CONTENT:
-                case LYXML_ATTR_CONTENT:
-                    ret = lyxml_get_string((struct lyxml_context *)ctx, data, NULL, NULL, NULL, NULL, NULL);
-                    /* not an error, just incorrect XML parser status */
-                    if (ret && (ret != LY_EINVAL)) {
-                        goto cleanup;
-                    }
-                    break;
-                case LYXML_END:
-                    /* end of data */
+            /* just incorrect status */
+            if (ctx->status == LYXML_ELEM_CONTENT) {
+                LY_ERR r = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &value, &value_len, &dynamic);
+                if (r != LY_EINVAL && (r != LY_SUCCESS || value_len != 0)) {
                     LOGINT(ctx->ctx);
                     ret = LY_EINT;
                     goto cleanup;
                 }
             }
 
-            /* get value */
-            if (start != *data) {
-                /* data now points after the anydata's closing element tag, we need just end of its content */
-                for (stop = *data - 1; *stop != '<'; --stop);
-                start = lydict_insert(ctx->ctx, start, stop - start);
-            } else {
-                start = NULL;
-            }
+            /* parse any data tree with correct options */
+            prev_opts = ctx->options;
+            ctx->options &= ~LYD_OPT_STRICT;
+            ctx->options |= LYD_OPT_OPAQ;
+            anchor = NULL;
+            ret = lydxml_data_r(ctx, NULL, data, &anchor);
+            ctx->options = prev_opts;
+            LY_CHECK_GOTO(ret, cleanup);
 
             /* create node */
-            ret = lyd_create_any(snode, start, LYD_ANYDATA_XML, &cur);
-            if (ret) {
-                lydict_remove(ctx->ctx, start);
-                goto cleanup;
-            }
+            ret = lyd_create_any(snode, anchor, LYD_ANYDATA_DATATREE, &cur);
+            LY_CHECK_GOTO(ret, cleanup);
         }
 
         /* correct flags */
-        if (!(snode->nodetype & (LYS_ACTION | LYS_NOTIF)) && snode->when) {
-            if (ctx->options & LYD_OPT_TRUSTED) {
-                /* just set it to true */
-                cur->flags |= LYD_WHEN_TRUE;
-            } else {
-                /* remember we need to evaluate this node's when */
-                ly_set_add(&ctx->when_check, cur, LY_SET_OPT_USEASLIST);
+        if (snode) {
+            if (!(snode->nodetype & (LYS_ACTION | LYS_NOTIF)) && snode->when) {
+                if (ctx->options & LYD_OPT_TRUSTED) {
+                    /* just set it to true */
+                    cur->flags |= LYD_WHEN_TRUE;
+                } else {
+                    /* remember we need to evaluate this node's when */
+                    ly_set_add(&ctx->when_check, cur, LY_SET_OPT_USEASLIST);
+                }
             }
-        }
-        if (ctx->options & LYD_OPT_TRUSTED) {
-            /* node is valid */
-            cur->flags &= ~LYD_NEW;
-        }
-        LY_LIST_FOR(meta, meta2) {
-            if (!strcmp(meta2->name, "default") && !strcmp(meta2->annotation->module->name, "ietf-netconf-with-defaults")
-                    && meta2->value.boolean) {
-                /* node is default according to the metadata */
-                cur->flags |= LYD_DEFAULT;
+            if (ctx->options & LYD_OPT_TRUSTED) {
+                /* node is valid */
+                cur->flags &= ~LYD_NEW;
+            }
+            prev_meta = NULL;
+            LY_LIST_FOR(meta, meta2) {
+                if (!strcmp(meta2->name, "default") && !strcmp(meta2->annotation->module->name, "ietf-netconf-with-defaults")
+                        && meta2->value.boolean) {
+                    /* node is default according to the metadata */
+                    cur->flags |= LYD_DEFAULT;
+
+                    /* delete the metadata */
+                    if (prev_meta) {
+                        prev_meta->next = meta2->next;
+                    } else {
+                        meta = meta->next;
+                    }
+                    lyd_free_meta(ctx->ctx, meta2, 0);
+                    break;
+                }
+
+                prev_meta = meta2;
             }
         }
 
-        /* add metdata */
-        assert(!cur->meta);
-        cur->meta = meta;
-        meta = NULL;
+        /* add metadata/attributes */
+        if (snode) {
+            cur->meta = meta;
+            meta = NULL;
+        } else {
+            assert(!cur->schema);
+            ((struct lyd_node_opaq *)cur)->attr = attr;
+            attr = NULL;
+        }
 
         /* insert */
         lyd_insert_node((struct lyd_node *)parent, first, cur);
@@ -457,8 +575,9 @@
 cleanup:
     free(buffer);
     lyd_free_meta(ctx->ctx, meta, 1);
+    ly_free_attr(ctx->ctx, attr, 1);
     lyd_free_tree(cur);
-    for (unsigned int u = 0; u < attrs_data.count; ++u) {
+    for (uint32_t u = 0; u < attrs_data.count; ++u) {
         if (((struct attr_data_s*)attrs_data.objs[u])->dynamic) {
             free(((struct attr_data_s*)attrs_data.objs[u])->value);
         }
@@ -514,13 +633,13 @@
             LY_CHECK_GOTO(ret, cleanup);
 
             /* add all top-level defaults for this module */
-            ret = lyd_validate_defaults_r(NULL, first2, NULL, mod, &xmlctx.incomplete_type_validation,
-                                          &xmlctx.when_check, options & LYD_VALOPT_MASK);
+            ret = lyd_validate_defaults_r(NULL, first2, NULL, mod, &xmlctx.unres_node_type, &xmlctx.when_check,
+                                          options & LYD_VALOPT_MASK);
             LY_CHECK_GOTO(ret, cleanup);
 
             /* finish incompletely validated terminal values/attributes and when conditions */
-            ret = lyd_validate_unres(tree, &xmlctx.when_check, &xmlctx.incomplete_type_validation,
-                                     &xmlctx.incomplete_type_validation_meta, LYD_XML, lydxml_resolve_prefix, ctx);
+            ret = lyd_validate_unres(tree, &xmlctx.when_check, &xmlctx.unres_node_type, &xmlctx.unres_meta_type, LYD_XML,
+                                     lydxml_resolve_prefix, ctx);
             LY_CHECK_GOTO(ret, cleanup);
 
             /* perform final validation that assumes the data tree is final */
@@ -531,11 +650,11 @@
 
 cleanup:
     /* there should be no unresolved types stored */
-    assert(!(options & LYD_OPT_PARSE_ONLY) || (!xmlctx.incomplete_type_validation.count
-           && !xmlctx.incomplete_type_validation_meta.count && !xmlctx.when_check.count));
+    assert(!(options & LYD_OPT_PARSE_ONLY) || (!xmlctx.unres_node_type.count && !xmlctx.unres_meta_type.count
+           && !xmlctx.when_check.count));
 
-    ly_set_erase(&xmlctx.incomplete_type_validation, NULL);
-    ly_set_erase(&xmlctx.incomplete_type_validation_meta, NULL);
+    ly_set_erase(&xmlctx.unres_node_type, NULL);
+    ly_set_erase(&xmlctx.unres_meta_type, NULL);
     ly_set_erase(&xmlctx.when_check, NULL);
     lyxml_context_clear((struct lyxml_context *)&xmlctx);
     if (ret) {
@@ -560,7 +679,7 @@
     }
 
     if (ctx->status == LYXML_ATTRIBUTE) {
-        LY_CHECK_RET(lydxml_attributes_parse(ctx, data, &attrs_data));
+        LY_CHECK_RET(lydxml_attributes_parse((struct lyxml_context *)ctx, data, &attrs_data));
     }
 
     ns = lyxml_ns_get((struct lyxml_context *)ctx, prefix, prefix_len);
diff --git a/src/plugins_types.c b/src/plugins_types.c
index f05b5c2..4c690b3 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -63,7 +63,7 @@
  * Implementation of the ly_type_dup_clb.
  */
 static LY_ERR
-ly_type_dup_original(struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+ly_type_dup_original(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
     dup->canonical_cache = original->canonical_cache;
     dup->original = (void*)lydict_insert(ctx, original->original, strlen(original->original));
@@ -80,7 +80,7 @@
  * Implementation of the ly_type_dup_clb.
  */
 static LY_ERR
-ly_type_dup_canonical(struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+ly_type_dup_canonical(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
     ly_type_dup_original(ctx, original, dup);
     dup->canonical_cache = (void*)lydict_insert(ctx, original->canonical_cache, strlen(original->canonical_cache));
@@ -97,7 +97,7 @@
  * Implementation of the ly_type_free_clb.
  */
 static void
-ly_type_free_original(struct ly_ctx *ctx, struct lyd_value *value)
+ly_type_free_original(const struct ly_ctx *ctx, struct lyd_value *value)
 {
     lydict_remove(ctx, value->original);
     value->original = NULL;
@@ -109,7 +109,7 @@
  * Implementation of the ly_type_free_clb.
  */
 static void
-ly_type_free_canonical(struct ly_ctx *ctx, struct lyd_value *value)
+ly_type_free_canonical(const struct ly_ctx *ctx, struct lyd_value *value)
 {
     ly_type_free_original(ctx, value);
     lydict_remove(ctx, value->canonical_cache);
@@ -117,7 +117,8 @@
 }
 
 API LY_ERR
-ly_type_parse_int(const char *datatype, int base, int64_t min, int64_t max, const char *value, size_t value_len, int64_t *ret, struct ly_err_item **err)
+ly_type_parse_int(const char *datatype, int base, int64_t min, int64_t max, const char *value, size_t value_len,
+                  int64_t *ret, struct ly_err_item **err)
 {
     char *errmsg = NULL;
 
@@ -148,7 +149,8 @@
 }
 
 API struct lyd_value_prefix *
-ly_type_get_prefixes(struct ly_ctx *ctx, const char *value, size_t value_len, ly_clb_resolve_prefix resolve_prefix, void *parser)
+ly_type_get_prefixes(const struct ly_ctx *ctx, const char *value, size_t value_len, ly_clb_resolve_prefix resolve_prefix,
+                     void *parser)
 {
     LY_ERR ret;
     unsigned int c;
@@ -203,7 +205,8 @@
 }
 
 API LY_ERR
-ly_type_parse_uint(const char *datatype, int base, uint64_t max, const char *value, size_t value_len, uint64_t *ret, struct ly_err_item **err)
+ly_type_parse_uint(const char *datatype, int base, uint64_t max, const char *value, size_t value_len, uint64_t *ret,
+                   struct ly_err_item **err)
 {
     char *errmsg = NULL;
 
@@ -380,7 +383,8 @@
 }
 
 API LY_ERR
-ly_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *strval, struct ly_err_item **err)
+ly_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *strval,
+                       struct ly_err_item **err)
 {
     unsigned int u;
     char *errmsg = NULL;
@@ -443,7 +447,7 @@
 }
 
 static void
-ly_type_store_strval(struct ly_ctx *ctx, int options, const char *orig, const char *value,
+ly_type_store_strval(const struct ly_ctx *ctx, int options, const char *orig, const char *value,
                      struct lyd_value *storage, const char **canonized)
 {
     if (options & LY_TYPE_OPTS_CANONIZE) {
@@ -461,7 +465,7 @@
 }
 #if 0
 static void
-ly_type_store_canonized(struct ly_ctx *ctx, int options, const char *value, struct lyd_value *storage, const char **canonized)
+ly_type_store_canonized(const struct ly_ctx *ctx, int options, const char *value, struct lyd_value *storage, const char **canonized)
 {
     if (options & LY_TYPE_OPTS_CANONIZE) {
         *canonized = value;
@@ -482,7 +486,7 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_int(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ly_type_store_int(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                   ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                   const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                   struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
@@ -546,7 +550,7 @@
  * Implementation of the ly_type_dup_clb.
  */
 static LY_ERR
-ly_type_dup_int(struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+ly_type_dup_int(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
     dup->int64 = original->int64;
     return ly_type_dup_canonical(ctx, original, dup);
@@ -558,7 +562,7 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_uint(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ly_type_store_uint(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                    ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                    const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                    struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
@@ -621,7 +625,7 @@
  * Implementation of the ly_type_dup_clb.
  */
 static LY_ERR
-ly_type_dup_uint(struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+ly_type_dup_uint(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
     dup->uint64 = original->uint64;
     return ly_type_dup_canonical(ctx, original, dup);
@@ -633,7 +637,7 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_decimal64(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ly_type_store_decimal64(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                         ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                         const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                         struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
@@ -705,7 +709,7 @@
  * Implementation of the ly_type_dup_clb.
  */
 static LY_ERR
-ly_type_dup_decimal64(struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+ly_type_dup_decimal64(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
    dup->dec64 = original->dec64;
    return ly_type_dup_canonical(ctx, original, dup);
@@ -717,7 +721,7 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_binary(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ly_type_store_binary(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                      ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                      const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                      struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
@@ -822,7 +826,7 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_string(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ly_type_store_string(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                      ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                      const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                      struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
@@ -866,7 +870,7 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_bits(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ly_type_store_bits(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                    ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                    const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                    struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
@@ -1017,7 +1021,7 @@
  * Implementation of the ly_type_dup_clb.
  */
 static LY_ERR
-ly_type_dup_bits(struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+ly_type_dup_bits(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
     unsigned int u;
 
@@ -1036,7 +1040,7 @@
  * Implementation of the ly_type_free_clb.
  */
 static void
-ly_type_free_bits(struct ly_ctx *ctx, struct lyd_value *value)
+ly_type_free_bits(const struct ly_ctx *ctx, struct lyd_value *value)
 {
     LY_ARRAY_FREE(value->bits_items);
     value->bits_items = NULL;
@@ -1051,7 +1055,7 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_enum(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ly_type_store_enum(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                    ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                    const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                    struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
@@ -1115,7 +1119,7 @@
  * Implementation of the ly_type_dup_clb.
  */
 static LY_ERR
-ly_type_dup_enum(struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+ly_type_dup_enum(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
     dup->enum_item = original->enum_item;
     return ly_type_dup_original(ctx, original, dup);
@@ -1127,7 +1131,7 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_boolean(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
+ly_type_store_boolean(const struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
                       ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                       const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                       struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
@@ -1171,7 +1175,7 @@
  * Implementation of the ly_type_dup_clb.
  */
 static LY_ERR
-ly_type_dup_boolean(struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+ly_type_dup_boolean(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
     dup->int64 = original->int64;
     return ly_type_dup_original(ctx, original, dup);
@@ -1183,7 +1187,7 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_empty(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
+ly_type_store_empty(const struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
                     ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
                     const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                     struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
@@ -1244,7 +1248,7 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_identityref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ly_type_store_identityref(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                           ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT UNUSED(format),
                           const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
                           struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
@@ -1365,7 +1369,7 @@
  * Implementation of the ly_type_dup_clb.
  */
 static LY_ERR
-ly_type_dup_identityref(struct ly_ctx *UNUSED(ctx), const struct lyd_value *original, struct lyd_value *dup)
+ly_type_dup_identityref(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *original, struct lyd_value *dup)
 {
     dup->ident = original->ident;
     return LY_SUCCESS;
@@ -1483,7 +1487,7 @@
  * Implementation of the ly_clb_resolve_prefix.
  */
 static const struct lys_module *
-ly_type_stored_prefixes_clb(struct ly_ctx *UNUSED(ctx), const char *prefix, size_t prefix_len, void *private)
+ly_type_stored_prefixes_clb(const struct ly_ctx *UNUSED(ctx), const char *prefix, size_t prefix_len, void *private)
 {
     struct lyd_value_prefix *prefixes = (struct lyd_value_prefix*)private;
     unsigned int u;
@@ -1510,8 +1514,8 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-ly_type_store_instanceid_parse_predicate_value(struct ly_ctx *ctx, const struct lysc_node *key, const char *val, size_t val_len,
-                                               struct lyd_value_prefix *prefixes, LYD_FORMAT format,
+ly_type_store_instanceid_parse_predicate_value(const struct ly_ctx *ctx, const struct lysc_node *key, const char *val,
+                                               size_t val_len, struct lyd_value_prefix *prefixes, LYD_FORMAT format,
                                                struct lyd_value_path_predicate *pred, char **errmsg)
 {
     LY_ERR ret = LY_SUCCESS;
@@ -1581,7 +1585,7 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_instanceid(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ly_type_store_instanceid(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                          ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
                          const void *UNUSED(context_node), const struct lyd_node *tree,
                          struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
@@ -2107,7 +2111,7 @@
  * Implementation of the ly_type_dup_clb.
  */
 static LY_ERR
-ly_type_dup_instanceid(struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+ly_type_dup_instanceid(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
     unsigned int u, v;
 
@@ -2150,7 +2154,7 @@
  * Implementation of the ly_type_free_clb.
  */
 static void
-ly_type_free_instanceid(struct ly_ctx *ctx, struct lyd_value *value)
+ly_type_free_instanceid(const struct ly_ctx *ctx, struct lyd_value *value)
 {
     lyd_value_free_path(ctx, value->target);
     value->target = NULL;
@@ -2161,7 +2165,7 @@
  * @brief Find leafref target in instance data.
  */
 const struct lyd_node *
-ly_type_find_leafref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len,
+ly_type_find_leafref(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len,
                      const struct lyd_node *context_node, const struct lyd_node *tree, struct lyd_value *storage, char **errmsg)
 {
     struct lysc_type_leafref *type_lr = (struct lysc_type_leafref*)type;
@@ -2351,7 +2355,7 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_leafref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ly_type_store_leafref(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                       ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
                       const void *context_node, const struct lyd_node *tree,
                       struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
@@ -2434,7 +2438,7 @@
  * Implementation of the ly_type_dup_clb.
  */
 static LY_ERR
-ly_type_dup_leafref(struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+ly_type_dup_leafref(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
     return original->realtype->plugin->duplicate(ctx, original, dup);
 }
@@ -2445,7 +2449,7 @@
  * Implementation of the ly_type_free_clb.
  */
 static void
-ly_type_free_leafref(struct ly_ctx *ctx, struct lyd_value *value)
+ly_type_free_leafref(const struct ly_ctx *ctx, struct lyd_value *value)
 {
     value->realtype->plugin->free(ctx, value);
 }
@@ -2456,7 +2460,7 @@
  * Implementation of the ly_type_store_clb.
  */
 static LY_ERR
-ly_type_store_union(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ly_type_store_union(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
                     ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
                     const void *context_node, const struct lyd_node *tree,
                     struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
@@ -2588,7 +2592,7 @@
  * Implementation of the ly_type_dup_clb.
  */
 static LY_ERR
-ly_type_dup_union(struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+ly_type_dup_union(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
     unsigned int u;
 
@@ -2616,7 +2620,7 @@
  * Implementation of the ly_type_free_clb.
  */
 static void
-ly_type_free_union(struct ly_ctx *ctx, struct lyd_value *value)
+ly_type_free_union(const struct ly_ctx *ctx, struct lyd_value *value)
 {
     unsigned int u;
 
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 2987382..44497cd 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -158,8 +158,8 @@
  * @return LY_EINCOMPLETE in case the option included LY_TYPE_OPTS_INCOMPLETE_DATA flag and the data @p trees are needed to finish the validation.
  * @return LY_ERR value if an error occurred and the value could not be canonized following the type's rules.
  */
-typedef LY_ERR (*ly_type_store_clb)(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                                    ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
+typedef LY_ERR (*ly_type_store_clb)(const struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len,
+                                    int options, ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
                                     const void *context_node, const struct lyd_node *tree,
                                     struct lyd_value *storage, const char **canonized, struct ly_err_item **err);
 
@@ -207,7 +207,7 @@
  * @return LY_SUCCESS after successful duplication.
  * @return other LY_ERR values on error.
  */
-typedef LY_ERR (*ly_type_dup_clb)(struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
+typedef LY_ERR (*ly_type_dup_clb)(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
 
 /**
  * @brief Callback for freeing the user type values stored by ly_type_store_clb().
@@ -217,7 +217,7 @@
  * @param[in] ctx libyang ctx to enable correct manipulation with values that are in the dictionary.
  * @param[in,out] value Value structure to free the data stored there by the plugin's ly_type_store_clb() callback
  */
-typedef void (*ly_type_free_clb)(struct ly_ctx *ctx, struct lyd_value *value);
+typedef void (*ly_type_free_clb)(const struct ly_ctx *ctx, struct lyd_value *value);
 
 /**
  * @brief Hold type-specific functions for various operations with the data values.
@@ -341,8 +341,8 @@
  * @param[out] errmsg Error message in case of error.
  * @return Leafref target node or NULL on error when @p errmsg is always set.
  */
-const struct lyd_node *ly_type_find_leafref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len,
-                                            const struct lyd_node *context_node, const struct lyd_node *tree,
+const struct lyd_node *ly_type_find_leafref(const struct ly_ctx *ctx, struct lysc_type *type, const char *value,
+                                            size_t value_len, const struct lyd_node *context_node, const struct lyd_node *tree,
                                             struct lyd_value *storage, char **errmsg);
 
 /**
@@ -355,9 +355,9 @@
  * @param[in] parser Parser's data for @p get_prefix.
  * @return Created [sized array](@ref sizedarrays) of prefix mappings, NULL in case of error.
  */
-struct lyd_value_prefix *ly_type_get_prefixes(struct ly_ctx *ctx, const char *value, size_t value_len,
+struct lyd_value_prefix *ly_type_get_prefixes(const struct ly_ctx *ctx, const char *value, size_t value_len,
                                               ly_clb_resolve_prefix get_prefix, void *parser);
 
-/**@} types */
+/** @} types */
 
 #endif /* LY_PLUGINS_TYPES_H_ */
diff --git a/src/printer_data.h b/src/printer_data.h
index 245dbb4..5abb2ac 100644
--- a/src/printer_data.h
+++ b/src/printer_data.h
@@ -1,7 +1,7 @@
 /**
- * @file printer_schema.h
+ * @file printer_data.h
  * @author Radek Krejci <rkrejci@cesnet.cz>
- * @brief Schema printers for libyang
+ * @brief Data printers for libyang
  *
  * Copyright (c) 2015-2019 CESNET, z.s.p.o.
  *
diff --git a/src/printer_xml.c b/src/printer_xml.c
index 44e8ff4..c5785dc 100644
--- a/src/printer_xml.c
+++ b/src/printer_xml.c
@@ -17,6 +17,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 
 #include "log.h"
 #include "plugins_types.h"
@@ -34,7 +35,9 @@
     struct lyout *out;  /**< output specification */
     unsigned int level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
     int options;        /**< [Data printer flags](@ref dataprinterflags) */
-    int toplevel;       /**< top-level flag */
+    const struct ly_ctx *ctx; /**< libyang context */
+    struct ly_set prefix;   /**< printed namespace prefixes */
+    struct ly_set ns;   /**< printed namespaces */
 };
 
 #define LEVEL ctx->level                     /**< current level */
@@ -42,113 +45,62 @@
 #define LEVEL_INC if (LEVEL) {LEVEL++;}      /**< increase indentation level */
 #define LEVEL_DEC if (LEVEL) {LEVEL--;}      /**< decrease indentation level */
 
-/**
- * TODO
- */
-struct mlist {
-    struct mlist *next;
-    struct lys_module *module;
-} *mlist = NULL, *mlist_new;
-
-static LY_ERR
-modlist_add(struct mlist **mlist, const struct lys_module *mod)
-{
-    struct mlist *iter;
-
-    for (iter = *mlist; iter; iter = iter->next) {
-        if (mod == iter->module) {
-            break;
-        }
-    }
-
-    if (!iter) {
-        iter = malloc(sizeof *iter);
-        LY_CHECK_ERR_RET(!iter, LOGMEM(mod->ctx), LY_EMEM);
-        iter->next = *mlist;
-        iter->module = (struct lys_module *)mod;
-        *mlist = iter;
-    }
-
-    return LY_SUCCESS;
-}
+#define LYXML_PREFIX_REQUIRED 0x01  /**< The prefix is not just a suggestion but a requirement. */
 
 /**
- * TODO
+ * @brief Print a namespace if not already printed.
+ *
+ * @param[in] ctx XML printer context.
+ * @param[in] ns Namespace to print, expected to be in dictionary.
+ * @param[in] new_prefix Suggested new prefix, NULL for a default namespace without prefix. Stored in the dictionary.
+ * @param[in] prefix_opts Prefix options changing the meaning of parameters.
+ * @return Printed prefix of the namespace to use.
  */
-static void
-xml_print_ns(struct xmlpr_ctx *ctx, const struct lyd_node *node)
+static const char *
+xml_print_ns(struct xmlpr_ctx *ctx, const char *ns, const char *new_prefix, int prefix_opts)
 {
-    struct lyd_node *next, *cur, *child;
-    struct lyd_meta *meta;
-    struct mlist *mlist = NULL, *miter;
-    const struct lys_module *wdmod = NULL;
+    int i;
 
-    /* add node metadata modules */
-    for (meta = node->meta; meta; meta = meta->next) {
-        if (!strcmp(node->schema->name, "filter") &&
-                (!strcmp(node->schema->module->name, "ietf-netconf") ||
-                 !strcmp(node->schema->module->name, "notifications"))) {
-            /* exception for NETCONF's filter attributes */
-            continue;
-        } else if (modlist_add(&mlist, meta->annotation->module)) {
-            goto print;
-        }
-    }
-
-    /* add node children nodes and attribute modules */
-    switch (node->schema->nodetype) {
-    case LYS_LEAFLIST:
-    case LYS_LEAF:
-        /* ietf-netconf-with-defaults namespace */
-        if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYDP_WD_ALL_TAG | LYDP_WD_IMPL_TAG))) ||
-                ((ctx->options & LYDP_WD_ALL_TAG) && ly_is_default(node))) {
-            /* get with-defaults module and print its namespace */
-            wdmod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
-            if (wdmod && modlist_add(&mlist, wdmod)) {
-                goto print;
-            }
-        }
-        break;
-    case LYS_CONTAINER:
-    case LYS_LIST:
-    case LYS_ACTION:
-    case LYS_NOTIF:
-        if (ctx->options & (LYDP_WD_ALL_TAG | LYDP_WD_IMPL_TAG)) {
-            /* get with-defaults module and print its namespace */
-            wdmod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
-            if (wdmod && modlist_add(&mlist, wdmod)) {
-                goto print;
-            }
-        }
-
-        LY_LIST_FOR(((struct lyd_node_inner*)node)->child, child) {
-            LYD_TREE_DFS_BEGIN(child, next, cur) {
-                for (meta = cur->meta; meta; meta = meta->next) {
-                    if (!strcmp(cur->schema->name, "filter") &&
-                            (!strcmp(cur->schema->module->name, "ietf-netconf") ||
-                             !strcmp(cur->schema->module->name, "notifications"))) {
-                        /* exception for NETCONF's filter attributes */
-                        continue;
-                    } else {
-                        /* TODO annotations r = modlist_add(&mlist, lys_main_module(meta->annotation->module)); */
-                    }
+    for (i = ctx->ns.count - 1; i > -1; --i) {
+        if (!new_prefix) {
+            /* find default namespace */
+            if (!ctx->prefix.objs[i]) {
+                if (ctx->ns.objs[i] != ns) {
+                    /* different default namespace */
+                    i = -1;
                 }
-            LYD_TREE_DFS_END(child, next, cur)}
+                break;
+            }
+        } else {
+            /* find prefixed namespace */
+            if (ctx->ns.objs[i] == ns) {
+                if (!ctx->prefix.objs[i]) {
+                    /* default namespace is not interesting */
+                    continue;
+                }
+
+                if (!strcmp(ctx->prefix.objs[i], new_prefix) || !(prefix_opts & LYXML_PREFIX_REQUIRED)) {
+                    /* the same prefix or can be any */
+                    break;
+                }
+            }
         }
-        break;
-    default:
-        break;
     }
 
-print:
-    /* print used namespaces */
-    while (mlist) {
-        miter = mlist;
-        mlist = mlist->next;
+    if (i == -1) {
+        /* suitable namespace not found, must be printed */
+        ly_print(ctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
 
-        ly_print(ctx->out, " xmlns:%s=\"%s\"", miter->module->prefix, miter->module->ns);
-        free(miter);
+        /* and added into namespaces */
+        if (new_prefix) {
+            new_prefix = lydict_insert(ctx->ctx, new_prefix, 0);
+        }
+        ly_set_add(&ctx->prefix, (void *)new_prefix, LY_SET_OPT_USEASLIST);
+        i = ly_set_add(&ctx->ns, (void *)ns, LY_SET_OPT_USEASLIST);
     }
+
+    /* return it */
+    return ctx->prefix.objs[i];
 }
 
 /**
@@ -168,11 +120,12 @@
 /**
  * TODO
  */
-static LY_ERR
+static void
 xml_print_meta(struct xmlpr_ctx *ctx, const struct lyd_node *node)
 {
     struct lyd_meta *meta;
-    const struct lys_module *wdmod = NULL;
+    const struct lys_module *mod;
+    struct ly_set ns_list = {0};
 #if 0
     const char **prefs, **nss;
     const char *xml_expr = NULL, *mod_name;
@@ -181,7 +134,6 @@
     char *p;
     size_t len;
 #endif
-    struct ly_set ns_list = {0};
     int dynamic;
     unsigned int u;
 
@@ -189,12 +141,10 @@
     if (node->schema->nodetype & LYD_NODE_TERM) {
         if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYDP_WD_ALL_TAG | LYDP_WD_IMPL_TAG))) ||
                 ((ctx->options & LYDP_WD_ALL_TAG) && ly_is_default(node))) {
-            /* we have implicit OR explicit default node */
-            /* get with-defaults module */
-            wdmod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
-            if (wdmod) {
-                /* print attribute only if context include with-defaults schema */
-                ly_print(ctx->out, " %s:default=\"true\"", wdmod->prefix);
+            /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
+            mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
+            if (mod) {
+                ly_print(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0));
             }
         }
     }
@@ -208,10 +158,10 @@
     for (meta = node->meta; meta; meta = meta->next) {
         const char *value = meta->value.realtype->plugin->print(&meta->value, LYD_XML, xml_print_get_prefix, &ns_list, &dynamic);
 
-        /* print namespaces connected with the values's prefixes */
+        /* print namespaces connected with the value's prefixes */
         for (u = 0; u < ns_list.count; ++u) {
-            const struct lys_module *mod = (const struct lys_module*)ns_list.objs[u];
-            ly_print(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
+            mod = (const struct lys_module *)ns_list.objs[u];
+            xml_print_ns(ctx, mod->ns, mod->prefix, 1);
         }
         ly_set_erase(&ns_list, NULL);
 
@@ -235,21 +185,22 @@
             ly_print(out, " %s=\"", meta->name);
         } else {
 #endif
-            ly_print(ctx->out, " %s:%s=\"", meta->annotation->module->prefix, meta->name);
+            /* print the metadata with its namespace */
+            mod = meta->annotation->module;
+            ly_print(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
 #if 0
         }
 #endif
 
+        /* print metadata value */
         if (value && value[0]) {
             lyxml_dump_text(ctx->out, value, 1);
         }
         ly_print(ctx->out, "\"");
         if (dynamic) {
-            free((void*)value);
+            free((void *)value);
         }
     }
-
-    return LY_SUCCESS;
 }
 
 /**
@@ -259,24 +210,75 @@
  *
  * @param[in] ctx XML printer context.
  * @param[in] node Data node to be printed.
- * @return LY_ERR value.
  */
-static LY_ERR
+static void
 xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
 {
-    if (ctx->toplevel || !node->parent || node->schema->module != node->parent->schema->module) {
-        /* print "namespace" */
-        ly_print(ctx->out, "%*s<%s xmlns=\"%s\"", INDENT, node->schema->name, node->schema->module->ns);
-    } else {
-        ly_print(ctx->out, "%*s<%s", INDENT, node->schema->name);
+    /* print node name */
+    ly_print(ctx->out, "%*s<%s", INDENT, node->schema->name);
+
+    /* print default namespace */
+    xml_print_ns(ctx, node->schema->module->ns, NULL, 0);
+
+    /* print metadata */
+    xml_print_meta(ctx, node);
+}
+
+static LY_ERR
+xml_print_attr(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
+{
+    const struct ly_attr *attr;
+    const char *pref;
+    uint32_t u;
+
+    LY_LIST_FOR(node->attr, attr) {
+        pref = NULL;
+        if (attr->prefix.pref) {
+            /* print attribute namespace */
+            switch (attr->format) {
+            case LYD_XML:
+                pref = xml_print_ns(ctx, attr->prefix.ns, attr->prefix.pref, 0);
+                break;
+            case LYD_SCHEMA:
+                /* cannot be created */
+                LOGINT(node->ctx);
+                return LY_EINT;
+            }
+        }
+
+        /* print namespaces connected with the value's prefixes */
+        if (attr->val_prefs) {
+            LY_ARRAY_FOR(attr->val_prefs, u) {
+                xml_print_ns(ctx, attr->val_prefs[u].ns, attr->val_prefs[u].pref, LYXML_PREFIX_REQUIRED);
+            }
+        }
+
+        /* print the attribute with its prefix and value */
+        ly_print(ctx->out, " %s%s%s=\"%s\"", pref ? pref : "", pref ? ":" : "", attr->name, attr->value);
     }
 
-    if (ctx->toplevel) {
-        xml_print_ns(ctx, node);
-        ctx->toplevel = 0;
+    return LY_SUCCESS;
+}
+
+static LY_ERR
+xml_print_opaq_open(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
+{
+    /* print node name */
+    ly_print(ctx->out, "%*s<%s", INDENT, node->name);
+
+    /* print default namespace */
+    switch (node->format) {
+    case LYD_XML:
+        xml_print_ns(ctx, node->prefix.ns, NULL, 0);
+        break;
+    case LYD_SCHEMA:
+        /* cannot be created */
+        LOGINT(node->ctx);
+        return LY_EINT;
     }
 
-    LY_CHECK_RET(xml_print_meta(ctx, node));
+    /* print attributes */
+    LY_CHECK_RET(xml_print_attr(ctx, node));
 
     return LY_SUCCESS;
 }
@@ -288,9 +290,8 @@
  *
  * @param[in] ctx XML printer context.
  * @param[in] node Data node to be printed.
- * @return LY_ERR value.
  */
-static LY_ERR
+static void
 xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
 {
     struct ly_set ns_list = {0};
@@ -298,7 +299,7 @@
     int dynamic;
     const char *value;
 
-    LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
+    xml_print_node_open(ctx, (struct lyd_node *)node);
     value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(&node->value, LYD_XML, xml_print_get_prefix, &ns_list, &dynamic);
 
     /* print namespaces connected with the values's prefixes */
@@ -318,8 +319,6 @@
     if (dynamic) {
         free((void *)value);
     }
-
-    return LY_SUCCESS;
 }
 
 /**
@@ -335,7 +334,7 @@
     LY_ERR ret;
     struct lyd_node *child;
 
-    LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
+    xml_print_node_open(ctx, (struct lyd_node *)node);
 
     if (!node->child) {
         ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
@@ -362,9 +361,10 @@
 {
     struct lyd_node_any *any = (struct lyd_node_any *)node;
     struct lyd_node *iter;
-    int options_backup;
+    int prev_opts;
+    LY_ERR ret;
 
-    LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
+    xml_print_node_open(ctx, (struct lyd_node *)node);
 
     if (!any->value.tree) {
         /* no content */
@@ -375,19 +375,18 @@
         switch (any->value_type) {
         case LYD_ANYDATA_DATATREE:
             /* close opening tag and print data */
-            options_backup = ctx->options;
+            prev_opts = ctx->options;
             ctx->options &= ~(LYDP_WITHSIBLINGS | LYDP_NETCONF);
             LEVEL_INC;
 
             ly_print(ctx->out, ">%s", LEVEL ? "\n" : "");
             LY_LIST_FOR(any->value.tree, iter) {
-                if (xml_print_node(ctx, iter)) {
-                    return EXIT_FAILURE;
-                }
+                ret = xml_print_node(ctx, iter);
+                LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
             }
 
             LEVEL_DEC;
-            ctx->options = options_backup;
+            ctx->options = prev_opts;
             break;
         case LYD_ANYDATA_STRING:
             /* escape XML-sensitive characters */
@@ -425,6 +424,50 @@
     return LY_SUCCESS;
 }
 
+static LY_ERR
+xml_print_opaq(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
+{
+    LY_ERR ret;
+    struct lyd_node *child;
+    uint32_t u;
+
+    LY_CHECK_RET(xml_print_opaq_open(ctx, node));
+
+    if (node->value[0]) {
+        /* print namespaces connected with the value's prefixes */
+        if (node->val_prefs) {
+            LY_ARRAY_FOR(node->val_prefs, u) {
+                xml_print_ns(ctx, node->val_prefs[u].ns, node->val_prefs[u].pref, LYXML_PREFIX_REQUIRED);
+            }
+        }
+
+        ly_print(ctx->out, ">%s", node->value);
+    }
+
+    if (node->child) {
+        /* children */
+        if (!node->value[0]) {
+            ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
+        }
+
+        LEVEL_INC;
+        LY_LIST_FOR(node->child, child) {
+            ret = xml_print_node(ctx, child);
+            LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
+        }
+        LEVEL_DEC;
+
+        ly_print(ctx->out, "%*s</%s>%s", INDENT, node->name, LEVEL ? "\n" : "");
+    } else if (node->value[0]) {
+        ly_print(ctx->out, "</%s>%s", node->name, LEVEL ? "\n" : "");
+    } else {
+        /* no value or children */
+        ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
+    }
+
+    return LY_SUCCESS;
+}
+
 /**
  * @brief Print XML element representing lyd_node.
  *
@@ -436,31 +479,46 @@
 xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
 {
     LY_ERR ret = LY_SUCCESS;
+    uint32_t ns_count;
 
     if (!ly_should_print(node, ctx->options)) {
         /* do not print at all */
-        return EXIT_SUCCESS;
+        return LY_SUCCESS;
     }
 
-    switch (node->schema->nodetype) {
-    case LYS_CONTAINER:
-    case LYS_LIST:
-    case LYS_NOTIF:
-    case LYS_ACTION:
-        ret = xml_print_inner(ctx, (const struct lyd_node_inner*)node);
-        break;
-    case LYS_LEAF:
-    case LYS_LEAFLIST:
-        ret = xml_print_term(ctx, (const struct lyd_node_term*)node);
-        break;
-    case LYS_ANYXML:
-    case LYS_ANYDATA:
-        ret = xml_print_anydata(ctx, (const struct lyd_node_any*)node);
-        break;
-    default:
-        LOGINT(node->schema->module->ctx);
-        ret = LY_EINT;
-        break;
+    /* remember namespace definition count on this level */
+    ns_count = ctx->ns.count;
+
+    if (!node->schema) {
+        ret = xml_print_opaq(ctx, (const struct lyd_node_opaq *)node);
+    } else {
+        switch (node->schema->nodetype) {
+        case LYS_CONTAINER:
+        case LYS_LIST:
+        case LYS_NOTIF:
+        case LYS_ACTION:
+            ret = xml_print_inner(ctx, (const struct lyd_node_inner *)node);
+            break;
+        case LYS_LEAF:
+        case LYS_LEAFLIST:
+            xml_print_term(ctx, (const struct lyd_node_term *)node);
+            break;
+        case LYS_ANYXML:
+        case LYS_ANYDATA:
+            ret = xml_print_anydata(ctx, (const struct lyd_node_any *)node);
+            break;
+        default:
+            LOGINT(node->schema->module->ctx);
+            ret = LY_EINT;
+            break;
+        }
+    }
+
+    /* remove all added namespaces */
+    while (ns_count < ctx->ns.count) {
+        FREE_STRING(ctx->ctx, ctx->prefix.objs[ctx->prefix.count - 1]);
+        ly_set_rm_index(&ctx->prefix, ctx->prefix.count - 1, NULL);
+        ly_set_rm_index(&ctx->ns, ctx->ns.count - 1, NULL);
     }
 
     return ret;
@@ -470,7 +528,7 @@
 xml_print_data(struct lyout *out, const struct lyd_node *root, int options)
 {
     const struct lyd_node *node;
-    struct xmlpr_ctx ctx_ = {.out = out, .level = (options & LYDP_FORMAT ? 1 : 0), .options = options}, *ctx = &ctx_;
+    struct xmlpr_ctx ctx = {0};
 
     if (!root) {
         if (out->type == LYOUT_MEMORY || out->type == LYOUT_CALLBACK) {
@@ -479,18 +537,23 @@
         goto finish;
     }
 
+    ctx.out = out;
+    ctx.level = (options & LYDP_FORMAT ? 1 : 0);
+    ctx.options = options;
+    ctx.ctx = LYD_NODE_CTX(root);
+
     /* content */
     LY_LIST_FOR(root, node) {
-        ctx_.toplevel = 1;
-        if (xml_print_node(ctx, node)) {
-            return EXIT_FAILURE;
-        }
+        LY_CHECK_RET(xml_print_node(&ctx, node));
         if (!(options & LYDP_WITHSIBLINGS)) {
             break;
         }
     }
 
 finish:
+    assert(!ctx.prefix.count && !ctx.ns.count);
+    ly_set_erase(&ctx.prefix, NULL);
+    ly_set_erase(&ctx.ns, NULL);
     ly_print_flush(out);
     return LY_SUCCESS;
 }
diff --git a/src/set.c b/src/set.c
index 3b7f078..378a22e 100644
--- a/src/set.c
+++ b/src/set.c
@@ -16,6 +16,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <stdint.h>
 
 #include "log.h"
 #include "set.h"
@@ -33,7 +34,7 @@
 API void
 ly_set_clean(struct ly_set *set, void (*destructor)(void *obj))
 {
-    unsigned int u;
+    uint32_t u;
 
     if (!set) {
         return;
@@ -76,7 +77,7 @@
 API int
 ly_set_contains(const struct ly_set *set, void *object)
 {
-    unsigned int i;
+    uint32_t i;
 
     LY_CHECK_ARG_RET(NULL, set, -1);
 
@@ -95,7 +96,7 @@
 ly_set_dup(const struct ly_set *set, void *(*duplicator)(void *obj))
 {
     struct ly_set *new;
-    unsigned int u;
+    uint32_t u;
 
     LY_CHECK_ARG_RET(NULL, set, NULL);
 
@@ -119,10 +120,10 @@
 API int
 ly_set_add(struct ly_set *set, void *object, int options)
 {
-    unsigned int i;
+    uint32_t i;
     void **new;
 
-    LY_CHECK_ARG_RET(NULL, set, object, -1);
+    LY_CHECK_ARG_RET(NULL, set, -1);
 
     if (!(options & LY_SET_OPT_USEASLIST)) {
         /* search for duplication */
@@ -149,7 +150,7 @@
 API int
 ly_set_merge(struct ly_set *trg, struct ly_set *src, int options, void *(*duplicator)(void *obj))
 {
-    unsigned int u, c, ret = 0;
+    uint32_t u, c, ret = 0;
     int i;
     void *obj;
 
@@ -164,7 +165,7 @@
         }
         c = trg->count;
         i = ly_set_add(trg, obj, options);
-        if (i > 0 && (unsigned int)i == c) {
+        if (i > 0 && (unsigned)i == c) {
             ++ret;
         }
     }
@@ -173,7 +174,7 @@
 }
 
 API LY_ERR
-ly_set_rm_index(struct ly_set *set, unsigned int index, void (*destructor)(void *obj))
+ly_set_rm_index(struct ly_set *set, uint32_t index, void (*destructor)(void *obj))
 {
     LY_CHECK_ARG_RET(NULL, set, LY_EINVAL);
     LY_CHECK_ERR_RET(index >= set->count, LOGARG(NULL, index), LY_EINVAL);
@@ -197,7 +198,7 @@
 API LY_ERR
 ly_set_rm(struct ly_set *set, void *object, void (*destructor)(void *obj))
 {
-    unsigned int i;
+    uint32_t i;
 
     LY_CHECK_ARG_RET(NULL, set, object, LY_EINVAL);
 
diff --git a/src/set.h b/src/set.h
index 915e91e..2f09f2f 100644
--- a/src/set.h
+++ b/src/set.h
@@ -15,6 +15,8 @@
 #ifndef LY_SET_H_
 #define LY_SET_H_
 
+#include <stdint.h>
+
 #include "log.h"
 
 #ifdef __cplusplus
@@ -43,8 +45,8 @@
  */
 struct ly_set
 {
-    unsigned int size;                /**< allocated size of the set array */
-    unsigned int count;               /**< number of elements in (used size of) the set array */
+    uint32_t size;                    /**< allocated size of the set array */
+    uint32_t count;                   /**< number of elements in (used size of) the set array */
     void **objs;                      /**< set array of generic object pointers */
 };
 
@@ -104,7 +106,8 @@
 int ly_set_merge(struct ly_set *trg, struct ly_set *src, int options, void *(*duplicator)(void *obj));
 
 /**
- * @brief Get know if the set contains the specified object.
+ * @brief Learn whether the set contains the specified object.
+ *
  * @param[in] set Set to explore.
  * @param[in] object Object to be found in the set.
  * @return Index of the object in the set or -1 if the object is not present in the set.
@@ -143,7 +146,7 @@
  * @param[in] destructor Optional function to free the objects being removed.
  * @return LY_ERR return value.
  */
-LY_ERR ly_set_rm_index(struct ly_set *set, unsigned int index, void (*destructor)(void *obj));
+LY_ERR ly_set_rm_index(struct ly_set *set, uint32_t index, void (*destructor)(void *obj));
 
 /**
  * @brief Free the ::ly_set data. If the destructor is not provided, it frees only the set structure
diff --git a/src/tree.h b/src/tree.h
index bf86260..32526f4 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -119,33 +119,6 @@
          (ELEM) = (NEXT))
 
 /**
- * @brief Generic tree node structure.
- */
-struct ly_node {
-    LYD_FORMAT format;
-    union {
-        struct {
-            const struct lysc_node *schema;
-            struct lyd_meta *meta;
-        } sch;
-        struct {
-            const char *name;
-            struct ly_attr *attr;
-        } xml;
-    };
-    const char *value;
-};
-
-/**
- * @brief Generic attribute structure.
- */
-struct ly_attr {
-    struct ly_attr *next;
-    const char *name;
-    const char *value;
-};
-
-/**
  * @brief YANG built-in types
  */
 typedef enum
diff --git a/src/tree_data.c b/src/tree_data.c
index 634ede6..5026d4e 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -29,6 +29,7 @@
 #include "tree_data_internal.h"
 #include "hash_table.h"
 #include "tree_schema.h"
+#include "xml.h"
 #include "plugins_exts_metadata.h"
 #include "plugins_exts_internal.h"
 
@@ -316,7 +317,7 @@
         lyd_parse_lyb(ctx, data, options, trees, &result);
         break;
 #endif
-    case LYD_UNKNOWN:
+    case LYD_SCHEMA:
         LOGINT(ctx);
         break;
     }
@@ -659,6 +660,43 @@
     return LY_SUCCESS;
 }
 
+LY_ERR
+lyd_create_opaq(const struct ly_ctx *ctx, const char *name, size_t name_len, const char *value, size_t value_len,
+                int *dynamic, LYD_FORMAT format, struct ly_prefix *val_prefs, const char *prefix, size_t pref_len,
+                const char *ns, struct lyd_node **node)
+{
+    struct lyd_node_opaq *opaq;
+
+    assert(ctx && name && name_len && ns);
+
+    if (!value_len) {
+        value = "";
+    }
+
+    opaq = calloc(1, sizeof *opaq);
+    LY_CHECK_ERR_RET(!opaq, LOGMEM(ctx), LY_EMEM);
+
+    opaq->prev = (struct lyd_node *)opaq;
+
+    opaq->name = lydict_insert(ctx, name, name_len);
+    opaq->format = format;
+    if (pref_len) {
+        opaq->prefix.pref = lydict_insert(ctx, prefix, pref_len);
+    }
+    opaq->prefix.ns = lydict_insert(ctx, ns, 0);
+    opaq->val_prefs = val_prefs;
+    if (dynamic && *dynamic) {
+        opaq->value = lydict_insert_zc(ctx, (char *)value);
+        *dynamic = 0;
+    } else {
+        opaq->value = lydict_insert(ctx, value, value_len);
+    }
+    opaq->ctx = ctx;
+
+    *node = (struct lyd_node *)opaq;
+    return LY_SUCCESS;
+}
+
 API struct lyd_node *
 lyd_new_inner(struct lyd_node *parent, const struct lys_module *module, const char *name)
 {
@@ -919,7 +957,7 @@
     struct lyd_node_inner *par;
 
     assert(parent && !node->next && (node->prev == node));
-    assert(parent->schema->nodetype & LYD_NODE_INNER);
+    assert(!parent->schema || (parent->schema->nodetype & LYD_NODE_INNER));
 
     par = (struct lyd_node_inner *)parent;
 
@@ -937,7 +975,7 @@
             /* remove default flags from NP containers */
             par->flags &= ~LYD_DEFAULT;
         }
-        if ((par->schema->nodetype == LYS_LIST) && (par->schema->flags & LYS_KEYLESS)) {
+        if (par->schema && (par->schema->nodetype == LYS_LIST) && (par->schema->flags & LYS_KEYLESS)) {
             /* rehash key-less list */
             lyd_hash((struct lyd_node *)par);
         }
@@ -954,14 +992,14 @@
     const struct lysc_node *skey = NULL;
     int has_keys;
 
-    assert((parent || first_sibling) && node && node->hash);
+    assert((parent || first_sibling) && node && (node->hash || !node->schema));
 
     if (!parent && first_sibling && (*first_sibling) && (*first_sibling)->parent) {
         parent = (struct lyd_node *)(*first_sibling)->parent;
     }
 
     if (parent) {
-        if (node->schema->flags & LYS_KEY) {
+        if (node->schema && (node->schema->flags & LYS_KEY)) {
             /* it is key and we need to insert it at the correct place */
             anchor = lyd_get_prev_key_anchor(lyd_node_children(parent), node->schema);
             if (anchor) {
@@ -1275,7 +1313,7 @@
 
 LY_ERR
 lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod, const char *name,
-                size_t name_len, const char *value, size_t value_len, int *dynamic, ly_clb_resolve_prefix get_prefix,
+                size_t name_len, const char *value, size_t value_len, int *dynamic, ly_clb_resolve_prefix resolve_prefix,
                 void *prefix_data, LYD_FORMAT format, const struct lysc_node *ctx_snode)
 {
     LY_ERR ret;
@@ -1304,7 +1342,7 @@
     LY_CHECK_ERR_RET(!mt, LOGMEM(mod->ctx), LY_EMEM);
     mt->parent = parent;
     mt->annotation = ant;
-    ret = lyd_value_parse_meta(mod->ctx, mt, value, value_len, dynamic, 0, get_prefix, prefix_data, format, ctx_snode, NULL);
+    ret = lyd_value_parse_meta(mod->ctx, mt, value, value_len, dynamic, 0, resolve_prefix, prefix_data, format, ctx_snode, NULL);
     if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
         free(mt);
         return ret;
@@ -1336,6 +1374,60 @@
     return ret;
 }
 
+LY_ERR
+ly_create_attr(struct lyd_node *parent, struct ly_attr **attr, const struct ly_ctx *ctx, const char *name,
+               size_t name_len, const char *value, size_t value_len, int *dynamic, LYD_FORMAT format,
+               struct ly_prefix *val_prefs, const char *prefix, size_t prefix_len, const char *ns)
+{
+    struct ly_attr *at, *last;
+    struct lyd_node_opaq *opaq;
+
+    assert(ctx && (parent || attr) && (!parent || !parent->schema));
+    assert(name && name_len);
+    assert((prefix_len && ns) || (!prefix_len && !ns));
+
+    if (!value_len) {
+        value = "";
+    }
+
+    at = calloc(1, sizeof *at);
+    LY_CHECK_ERR_RET(!at, LOGMEM(ctx), LY_EMEM);
+    at->parent = (struct lyd_node_opaq *)parent;
+    at->name = lydict_insert(ctx, name, name_len);
+    if (dynamic && *dynamic) {
+        at->value = lydict_insert_zc(ctx, (char *)value);
+        *dynamic = 0;
+    } else {
+        at->value = lydict_insert(ctx, value, value_len);
+    }
+
+    at->format = format;
+    at->val_prefs = val_prefs;
+    if (ns) {
+        at->prefix.pref = lydict_insert(ctx, prefix, prefix_len);
+        at->prefix.ns = lydict_insert(ctx, ns, 0);
+    }
+
+    /* insert as the last attribute */
+    if (parent) {
+        opaq = (struct lyd_node_opaq *)parent;
+        if (opaq->attr) {
+            for (last = opaq->attr; last->next; last = last->next);
+            last->next = at;
+        } else {
+            opaq->attr = at;
+        }
+    } else if (*attr) {
+        for (last = *attr; last->next; last = last->next);
+        last->next = at;
+    }
+
+    if (attr) {
+        *attr = at;
+    }
+    return LY_SUCCESS;
+}
+
 API const struct lyd_node_term *
 lyd_target(struct lyd_value_path *path, const struct lyd_node *tree)
 {
@@ -1415,6 +1507,7 @@
     const struct lyd_node *iter1, *iter2;
     struct lyd_node_term *term1, *term2;
     struct lyd_node_any *any1, *any2;
+    struct lyd_node_opaq *opaq1, *opaq2;
     struct lysc_type *type;
     size_t len1, len2;
 
@@ -1426,212 +1519,251 @@
         }
     }
 
-    if (node1->schema->module->ctx != node2->schema->module->ctx || node1->schema != node2->schema) {
+    if ((LYD_NODE_CTX(node1) != LYD_NODE_CTX(node2)) || (node1->schema != node2->schema)) {
         return LY_ENOT;
     }
 
     if (node1->hash != node2->hash) {
         return LY_ENOT;
     }
+    /* equal hashes do not mean equal nodes, they can be just in collision (or both be 0) so the nodes must be checked explicitly */
 
-    /* equal hashes do not mean equal nodes, they can be just in collision so the nodes must be checked explicitly */
-
-    switch (node1->schema->nodetype) {
-    case LYS_LEAF:
-    case LYS_LEAFLIST:
-        if (options & LYD_COMPARE_DEFAULTS) {
-            if ((node1->flags & LYD_DEFAULT) != (node2->flags & LYD_DEFAULT)) {
-                return LY_ENOT;
-            }
-        }
-
-        term1 = (struct lyd_node_term*)node1;
-        term2 = (struct lyd_node_term*)node2;
-        type = ((struct lysc_node_leaf*)node1->schema)->type;
-
-        return type->plugin->compare(&term1->value, &term2->value);
-    case LYS_CONTAINER:
-        if (options & LYD_COMPARE_DEFAULTS) {
-            if ((node1->flags & LYD_DEFAULT) != (node2->flags & LYD_DEFAULT)) {
-                return LY_ENOT;
-            }
-        }
-        if (options & LYD_COMPARE_FULL_RECURSION) {
-            iter1 = ((struct lyd_node_inner*)node1)->child;
-            iter2 = ((struct lyd_node_inner*)node2)->child;
-            goto all_children_compare;
-        }
-        return LY_SUCCESS;
-    case LYS_ACTION:
-        if (options & LYD_COMPARE_FULL_RECURSION) {
-            /* TODO action/RPC
-            goto all_children_compare;
-            */
-        }
-        return LY_SUCCESS;
-    case LYS_NOTIF:
-        if (options & LYD_COMPARE_FULL_RECURSION) {
-            /* TODO Notification
-            goto all_children_compare;
-            */
-        }
-        return LY_SUCCESS;
-    case LYS_LIST:
-        iter1 = ((struct lyd_node_inner*)node1)->child;
-        iter2 = ((struct lyd_node_inner*)node2)->child;
-
-        if (!(node1->schema->flags & LYS_KEYLESS) && !(options & LYD_COMPARE_FULL_RECURSION)) {
-            /* lists with keys, their equivalence is based on their keys */
-            for (struct lysc_node *key = ((struct lysc_node_list*)node1->schema)->child;
-                    key && key->nodetype == LYS_LEAF && (key->flags & LYS_KEY);
-                    key = key->next) {
-                if (lyd_compare(iter1, iter2, options)) {
-                    return LY_ENOT;
-                }
-                iter1 = iter1->next;
-                iter2 = iter2->next;
-            }
-        } else {
-            /* lists without keys, their equivalence is based on equivalence of all the children (both direct and indirect) */
-
-all_children_compare:
-            if (!iter1 && !iter2) {
-                /* no children, nothing to compare */
-                return LY_SUCCESS;
-            }
-
-            for (; iter1 && iter2; iter1 = iter1->next, iter2 = iter2->next) {
-                if (lyd_compare(iter1, iter2, options | LYD_COMPARE_FULL_RECURSION)) {
-                    return LY_ENOT;
-                }
-            }
-            if (iter1 || iter2) {
-                return LY_ENOT;
-            }
-        }
-        return LY_SUCCESS;
-    case LYS_ANYXML:
-    case LYS_ANYDATA:
-        any1 = (struct lyd_node_any*)node1;
-        any2 = (struct lyd_node_any*)node2;
-
-        if (any1->value_type != any2->value_type) {
+    if (!node1->schema) {
+        opaq1 = (struct lyd_node_opaq *)node1;
+        opaq2 = (struct lyd_node_opaq *)node2;
+        if ((opaq1->name != opaq2->name) || (opaq1->prefix.ns != opaq2->prefix.ns) || (opaq1->format != opaq2->format)) {
             return LY_ENOT;
         }
-        switch (any1->value_type) {
-        case LYD_ANYDATA_DATATREE:
-            iter1 = any1->value.tree;
-            iter2 = any2->value.tree;
+        switch (opaq1->format) {
+        case LYD_XML:
+            if (lyxml_value_compare(opaq1->value, opaq1->val_prefs, opaq2->value, opaq2->val_prefs)) {
+                return LY_ENOT;
+            }
+            break;
+        case LYD_SCHEMA:
+            /* not allowed */
+            LOGINT(LYD_NODE_CTX(node1));
+            return LY_EINT;
+        }
+        if (options & LYD_COMPARE_FULL_RECURSION) {
+            iter1 = opaq1->child;
+            iter2 = opaq2->child;
             goto all_children_compare;
-        case LYD_ANYDATA_STRING:
-        case LYD_ANYDATA_XML:
-        case LYD_ANYDATA_JSON:
-            len1 = strlen(any1->value.str);
-            len2 = strlen(any2->value.str);
-            if (len1 != len2 || strcmp(any1->value.str, any2->value.str)) {
-                return LY_ENOT;
+        }
+        return LY_SUCCESS;
+    } else {
+        switch (node1->schema->nodetype) {
+        case LYS_LEAF:
+        case LYS_LEAFLIST:
+            if (options & LYD_COMPARE_DEFAULTS) {
+                if ((node1->flags & LYD_DEFAULT) != (node2->flags & LYD_DEFAULT)) {
+                    return LY_ENOT;
+                }
+            }
+
+            term1 = (struct lyd_node_term*)node1;
+            term2 = (struct lyd_node_term*)node2;
+            type = ((struct lysc_node_leaf*)node1->schema)->type;
+
+            return type->plugin->compare(&term1->value, &term2->value);
+        case LYS_CONTAINER:
+            if (options & LYD_COMPARE_DEFAULTS) {
+                if ((node1->flags & LYD_DEFAULT) != (node2->flags & LYD_DEFAULT)) {
+                    return LY_ENOT;
+                }
+            }
+            if (options & LYD_COMPARE_FULL_RECURSION) {
+                iter1 = ((struct lyd_node_inner*)node1)->child;
+                iter2 = ((struct lyd_node_inner*)node2)->child;
+                goto all_children_compare;
             }
             return LY_SUCCESS;
-#if 0 /* TODO LYB format */
-        case LYD_ANYDATA_LYB:
-            int len1 = lyd_lyb_data_length(any1->value.mem);
-            int len2 = lyd_lyb_data_length(any2->value.mem);
-            if (len1 != len2 || memcmp(any1->value.mem, any2->value.mem, len1)) {
-                return LY_ENOT;
+        case LYS_ACTION:
+            if (options & LYD_COMPARE_FULL_RECURSION) {
+                /* TODO action/RPC
+                goto all_children_compare;
+                */
             }
             return LY_SUCCESS;
-#endif
+        case LYS_NOTIF:
+            if (options & LYD_COMPARE_FULL_RECURSION) {
+                /* TODO Notification
+                goto all_children_compare;
+                */
+            }
+            return LY_SUCCESS;
+        case LYS_LIST:
+            iter1 = ((struct lyd_node_inner*)node1)->child;
+            iter2 = ((struct lyd_node_inner*)node2)->child;
+
+            if (!(node1->schema->flags & LYS_KEYLESS) && !(options & LYD_COMPARE_FULL_RECURSION)) {
+                /* lists with keys, their equivalence is based on their keys */
+                for (struct lysc_node *key = ((struct lysc_node_list*)node1->schema)->child;
+                        key && key->nodetype == LYS_LEAF && (key->flags & LYS_KEY);
+                        key = key->next) {
+                    if (lyd_compare(iter1, iter2, options)) {
+                        return LY_ENOT;
+                    }
+                    iter1 = iter1->next;
+                    iter2 = iter2->next;
+                }
+            } else {
+                /* lists without keys, their equivalence is based on equivalence of all the children (both direct and indirect) */
+
+    all_children_compare:
+                if (!iter1 && !iter2) {
+                    /* no children, nothing to compare */
+                    return LY_SUCCESS;
+                }
+
+                for (; iter1 && iter2; iter1 = iter1->next, iter2 = iter2->next) {
+                    if (lyd_compare(iter1, iter2, options | LYD_COMPARE_FULL_RECURSION)) {
+                        return LY_ENOT;
+                    }
+                }
+                if (iter1 || iter2) {
+                    return LY_ENOT;
+                }
+            }
+            return LY_SUCCESS;
+        case LYS_ANYXML:
+        case LYS_ANYDATA:
+            any1 = (struct lyd_node_any*)node1;
+            any2 = (struct lyd_node_any*)node2;
+
+            if (any1->value_type != any2->value_type) {
+                return LY_ENOT;
+            }
+            switch (any1->value_type) {
+            case LYD_ANYDATA_DATATREE:
+                iter1 = any1->value.tree;
+                iter2 = any2->value.tree;
+                goto all_children_compare;
+            case LYD_ANYDATA_STRING:
+            case LYD_ANYDATA_XML:
+            case LYD_ANYDATA_JSON:
+                len1 = strlen(any1->value.str);
+                len2 = strlen(any2->value.str);
+                if (len1 != len2 || strcmp(any1->value.str, any2->value.str)) {
+                    return LY_ENOT;
+                }
+                return LY_SUCCESS;
+    #if 0 /* TODO LYB format */
+            case LYD_ANYDATA_LYB:
+                int len1 = lyd_lyb_data_length(any1->value.mem);
+                int len2 = lyd_lyb_data_length(any2->value.mem);
+                if (len1 != len2 || memcmp(any1->value.mem, any2->value.mem, len1)) {
+                    return LY_ENOT;
+                }
+                return LY_SUCCESS;
+    #endif
+            }
         }
     }
 
-    LOGINT(node1->schema->module->ctx);
+    LOGINT(LYD_NODE_CTX(node1));
     return LY_EINT;
 }
 
 /**
- * @brief Duplicates just a single node and interconnect it into a @p parent (if present) and after the @p prev
- * sibling (if present).
+ * @brief Duplicate a single node and connect it into @p parent (if present) or last of @p first siblings.
  *
  * Ignores LYD_DUP_WITH_PARENTS and LYD_DUP_WITH_SIBLINGS which are supposed to be handled by lyd_dup().
  */
-static struct lyd_node *
-lyd_dup_recursive(const struct lyd_node *node, struct lyd_node_inner *parent, struct lyd_node *prev, int options)
+static LY_ERR
+lyd_dup_recursive(const struct lyd_node *node, struct lyd_node *parent, struct lyd_node **first, int options,
+                  struct lyd_node **dup_p)
 {
-    struct ly_ctx *ctx;
+    LY_ERR ret;
     struct lyd_node *dup = NULL;
+    uint32_t u;
 
-    LY_CHECK_ARG_RET(NULL, node, NULL);
-    ctx = node->schema->module->ctx;
+    LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
 
-    switch (node->schema->nodetype) {
-    case LYS_ACTION:
-    case LYS_NOTIF:
-    case LYS_CONTAINER:
-    case LYS_LIST:
-        dup = calloc(1, sizeof(struct lyd_node_inner));
-        break;
-    case LYS_LEAF:
-    case LYS_LEAFLIST:
-        dup = calloc(1, sizeof(struct lyd_node_term));
-        break;
-    case LYS_ANYDATA:
-    case LYS_ANYXML:
-        dup = calloc(1, sizeof(struct lyd_node_any));
-        break;
-    default:
-        LOGINT(ctx);
-        goto error;
+    if (!node->schema) {
+        dup = calloc(1, sizeof(struct lyd_node_opaq));
+    } else {
+        switch (node->schema->nodetype) {
+        case LYS_ACTION:
+        case LYS_NOTIF:
+        case LYS_CONTAINER:
+        case LYS_LIST:
+            dup = calloc(1, sizeof(struct lyd_node_inner));
+            break;
+        case LYS_LEAF:
+        case LYS_LEAFLIST:
+            dup = calloc(1, sizeof(struct lyd_node_term));
+            break;
+        case LYS_ANYDATA:
+        case LYS_ANYXML:
+            dup = calloc(1, sizeof(struct lyd_node_any));
+            break;
+        default:
+            LOGINT(LYD_NODE_CTX(node));
+            ret = LY_EINT;
+            goto error;
+        }
     }
+    LY_CHECK_ERR_GOTO(!dup, LOGMEM(LYD_NODE_CTX(node)); ret = LY_EMEM, error);
 
     /* TODO implement LYD_DUP_WITH_WHEN */
     dup->flags = node->flags;
     dup->schema = node->schema;
-
-    /* interconnect the node at the end */
-    dup->parent = parent;
-    if (prev) {
-        dup->prev = prev;
-        prev->next = dup;
-    } else {
-        dup->prev = dup;
-        if (parent) {
-            parent->child = dup;
-        }
-    }
-    if (parent) {
-        parent->child->prev = dup;
-    } else if (prev) {
-        struct lyd_node *first;
-        for (first = prev; first->prev != prev; first = first->prev);
-        first->prev = dup;
-    }
+    dup->prev = dup;
 
     /* TODO duplicate attributes, implement LYD_DUP_NO_ATTR */
 
     /* nodetype-specific work */
-    if (dup->schema->nodetype & LYD_NODE_TERM) {
-        struct lyd_node_term *term = (struct lyd_node_term*)dup;
-        struct lyd_node_term *orig = (struct lyd_node_term*)node;
-
-        term->hash = orig->hash;
-        term->value.realtype = orig->value.realtype;
-        LY_CHECK_ERR_GOTO(term->value.realtype->plugin->duplicate(ctx, &orig->value, &term->value),
-                          LOGERR(ctx, LY_EINT, "Value duplication failed."), error);
-    } else if (dup->schema->nodetype & LYD_NODE_INNER) {
-        struct lyd_node_inner *inner = (struct lyd_node_inner*)dup;
-        struct lyd_node_inner *orig = (struct lyd_node_inner*)node;
-        struct lyd_node *child, *last = NULL;
+    if (!dup->schema) {
+        struct lyd_node_opaq *opaq = (struct lyd_node_opaq *)dup;
+        struct lyd_node_opaq *orig = (struct lyd_node_opaq *)node;
+        struct lyd_node *child;
 
         if (options & LYD_DUP_RECURSIVE) {
             /* duplicate all the children */
             LY_LIST_FOR(orig->child, child) {
-                last = lyd_dup_recursive(child, inner, last, options);
-                LY_CHECK_GOTO(!last, error);
+                LY_CHECK_GOTO(ret = lyd_dup_recursive(child, dup, NULL, options, NULL), error);
+            }
+        }
+        opaq->name = lydict_insert(LYD_NODE_CTX(node), orig->name, 0);
+        opaq->format = orig->format;
+        if (orig->prefix.pref) {
+            opaq->prefix.pref = lydict_insert(LYD_NODE_CTX(node), orig->prefix.pref, 0);
+        }
+        if (orig->prefix.ns) {
+            opaq->prefix.ns = lydict_insert(LYD_NODE_CTX(node), orig->prefix.ns, 0);
+        }
+        if (orig->val_prefs) {
+            LY_ARRAY_CREATE_GOTO(LYD_NODE_CTX(node), opaq->val_prefs, LY_ARRAY_SIZE(orig->val_prefs), ret, error);
+            LY_ARRAY_FOR(orig->val_prefs, u) {
+                opaq->val_prefs[u].pref = lydict_insert(LYD_NODE_CTX(node), orig->val_prefs[u].pref, 0);
+                opaq->val_prefs[u].ns = lydict_insert(LYD_NODE_CTX(node), orig->val_prefs[u].ns, 0);
+                LY_ARRAY_INCREMENT(opaq->val_prefs);
+            }
+        }
+        opaq->value = lydict_insert(LYD_NODE_CTX(node), orig->value, 0);
+        opaq->ctx = orig->ctx;
+    } else if (dup->schema->nodetype & LYD_NODE_TERM) {
+        struct lyd_node_term *term = (struct lyd_node_term *)dup;
+        struct lyd_node_term *orig = (struct lyd_node_term *)node;
+
+        term->hash = orig->hash;
+        term->value.realtype = orig->value.realtype;
+        LY_CHECK_ERR_GOTO(term->value.realtype->plugin->duplicate(LYD_NODE_CTX(node), &orig->value, &term->value),
+                          LOGERR(LYD_NODE_CTX(node), LY_EINT, "Value duplication failed."); ret = LY_EINT, error);
+    } else if (dup->schema->nodetype & LYD_NODE_INNER) {
+        struct lyd_node_inner *orig = (struct lyd_node_inner *)node;
+        struct lyd_node *child;
+
+        if (options & LYD_DUP_RECURSIVE) {
+            /* duplicate all the children */
+            LY_LIST_FOR(orig->child, child) {
+                LY_CHECK_GOTO(ret = lyd_dup_recursive(child, dup, NULL, options, NULL), error);
             }
         } else if (dup->schema->nodetype == LYS_LIST && !(dup->schema->flags & LYS_KEYLESS)) {
             /* always duplicate keys of a list */
             child = orig->child;
-            for (struct lysc_node *key = ((struct lysc_node_list*)dup->schema)->child;
+            for (struct lysc_node *key = ((struct lysc_node_list *)dup->schema)->child;
                     key && key->nodetype == LYS_LEAF && (key->flags & LYS_KEY);
                     key = key->next) {
                 if (!child) {
@@ -1642,14 +1774,14 @@
                      * but there can be also some non-key nodes */
                     continue;
                 }
-                last = lyd_dup_recursive(child, inner, last, options);
+                LY_CHECK_GOTO(ret = lyd_dup_recursive(child, dup, NULL, options, NULL), error);
                 child = child->next;
             }
         }
         lyd_hash(dup);
     } else if (dup->schema->nodetype & LYD_NODE_ANY) {
-        struct lyd_node_any *any = (struct lyd_node_any*)dup;
-        struct lyd_node_any *orig = (struct lyd_node_any*)node;
+        struct lyd_node_any *any = (struct lyd_node_any *)dup;
+        struct lyd_node_any *orig = (struct lyd_node_any *)node;
 
         any->hash = orig->hash;
         any->value_type = orig->value_type;
@@ -1664,20 +1796,24 @@
         case LYD_ANYDATA_XML:
         case LYD_ANYDATA_JSON:
             if (orig->value.str) {
-                any->value.str = lydict_insert(ctx, orig->value.str, strlen(orig->value.str));
+                any->value.str = lydict_insert(LYD_NODE_CTX(node), orig->value.str, strlen(orig->value.str));
             }
             break;
         }
     }
 
+    /* insert */
+    lyd_insert_node(parent, first, dup);
     lyd_insert_hash(dup);
-    return dup;
+
+    if (dup_p) {
+        *dup_p = dup;
+    }
+    return LY_SUCCESS;
 
 error:
-    if (!parent && !prev) {
-        lyd_free_tree(dup);
-    }
-    return NULL;
+    lyd_free_tree(dup);
+    return ret;
 }
 
 API struct lyd_node *
@@ -1686,7 +1822,6 @@
     struct ly_ctx *ctx;
     const struct lyd_node *orig;          /* original node to be duplicated */
     struct lyd_node *first = NULL;        /* the first duplicated node, this is returned */
-    struct lyd_node *last = NULL;         /* the last sibling of the duplicated nodes */
     struct lyd_node *top = NULL;          /* the most higher created node */
     struct lyd_node_inner *local_parent = NULL; /* the direct parent node for the duplicated node(s) */
     int keyless_parent_list = 0;
@@ -1710,8 +1845,9 @@
                     }
                 }
             } else {
-                iter = (struct lyd_node_inner*)lyd_dup_recursive((struct lyd_node*)orig_parent, NULL, NULL, 0);
-                LY_CHECK_GOTO(!iter, error);
+                iter = NULL;
+                LY_CHECK_GOTO(lyd_dup_recursive((struct lyd_node *)orig_parent, NULL, (struct lyd_node **)&iter, 0,
+                                                (struct lyd_node **)&iter), error);
             }
             if (!local_parent) {
                 local_parent = iter;
@@ -1745,17 +1881,9 @@
         local_parent = parent;
     }
 
-    if (local_parent && local_parent->child) {
-        last = local_parent->child->prev;
-    }
-
     LY_LIST_FOR(node, orig) {
-        last = lyd_dup_recursive(orig, local_parent, last, options);
-        LY_CHECK_GOTO(!last, error);
-        if (!first) {
-            first = last;
-        }
-
+        /* if there is no local parent, it will be inserted into first */
+        LY_CHECK_GOTO(lyd_dup_recursive(orig, (struct lyd_node *)local_parent, &first, options, first ? NULL : &first), error);
         if (!(options & LYD_DUP_WITH_SIBLINGS)) {
             break;
         }
diff --git a/src/tree_data.h b/src/tree_data.h
index 34ddd46..80ecca5 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -108,16 +108,16 @@
 /**
  * @brief Macro to get context from a data tree node.
  */
-#define LYD_NODE_CTX(node) ((node)->schema->module->ctx)
+#define LYD_NODE_CTX(node) ((node)->schema ? (node)->schema->module->ctx : ((struct lyd_node_opaq *)(node))->ctx)
 
 /**
  * @brief Data input/output formats supported by libyang [parser](@ref howtodataparsers) and
- * [printer](@ref howtodataprinters) functions.
+ * [printer](@ref howtodataprinters) functions. Also used for value prefix format.
  */
 typedef enum {
-    LYD_UNKNOWN = 0,     /**< unknown format, used as return value in case of error */
-    LYD_XML,             /**< XML format of the instance data */
-    LYD_JSON,            /**< JSON format of the instance data */
+    LYD_SCHEMA = 0,      /**< invalid instance data format, value prefixes map to YANG import prefixes */
+    LYD_XML,             /**< XML instance data format, value prefixes map to XML namespace prefixes */
+    LYD_JSON,            /**< JSON instance data format, value prefixes map to module names */
 #if 0
     LYD_LYB,             /**< LYB format of the instance data */
 #endif
@@ -224,6 +224,28 @@
     struct lyd_value value;          /**< metadata value representation */
 };
 
+/**
+ * @brief Generic prefix and namespace mapping, meaning depends on the format.
+ */
+struct ly_prefix {
+    const char *pref;
+    const char *ns;
+};
+
+/**
+ * @brief Generic attribute structure.
+ */
+struct ly_attr {
+    struct lyd_node_opaq *parent;   /**< data node where the attribute is placed */
+    struct ly_attr *next;
+    struct ly_prefix *val_prefs;    /**< list of prefixes in the value ([sized array](@ref sizedarrays)) */
+    const char *name;
+    const char *value;
+
+    LYD_FORMAT format;
+    struct ly_prefix prefix;        /**< name prefix, it is stored because they are a real pain to generate properly */
+
+};
 
 #define LYD_NODE_INNER (LYS_CONTAINER|LYS_LIST|LYS_ACTION|LYS_NOTIF) /**< Schema nodetype mask for lyd_node_inner */
 #define LYD_NODE_TERM (LYS_LEAF|LYS_LEAFLIST)   /**< Schema nodetype mask for lyd_node_term */
@@ -255,6 +277,7 @@
 #define LYD_DEFAULT      0x01        /**< default (implicit) node */
 #define LYD_WHEN_TRUE    0x02        /**< all when conditions of this node were evaluated to true */
 #define LYD_NEW          0x04        /**< node was created after the last validation, is needed for the next validation */
+
 /** @} */
 
 /**
@@ -271,7 +294,8 @@
  * @param[in] private Internal data needed by the callback.
  * @return Pointer to the YANG schema identified by the provided prefix or NULL if no mapping found.
  */
-typedef const struct lys_module *(*ly_clb_resolve_prefix)(struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *private);
+typedef const struct lys_module *(*ly_clb_resolve_prefix)(const struct ly_ctx *ctx, const char *prefix, size_t prefix_len,
+                                                          void *private);
 
 /**
  * @brief Callback provided by the data/schema printers to type plugins to resolve (format-specific) mapping between YANG module of a data object
@@ -396,6 +420,31 @@
 };
 
 /**
+ * @brief Data node structure for unparsed (opaque) nodes.
+ */
+struct lyd_node_opaq {
+    uint32_t hash;                  /**< always 0 */
+    uint32_t flags;                 /**< always 0 */
+    const struct lysc_node *schema; /**< always NULL */
+    struct lyd_node *parent;        /**< pointer to the parent node (NULL in case of root node) */
+    struct lyd_node *next;          /**< pointer to the next sibling node (NULL if there is no one) */
+    struct lyd_node *prev;          /**< pointer to the previous sibling node (last sibling if there is none) */
+    struct ly_attr *attr;
+
+#ifdef LY_ENABLED_LYD_PRIV
+    void *priv;                     /**< private user data, not used by libyang */
+#endif
+
+    struct lyd_node *child;         /**< pointer to the child node (NULL if there are none) */
+    const char *name;
+    LYD_FORMAT format;
+    struct ly_prefix prefix;        /**< name prefix */
+    struct ly_prefix *val_prefs;    /**< list of prefixes in the value ([sized array](@ref sizedarrays)) */
+    const char *value;              /**< original value */
+    const struct ly_ctx *ctx;       /**< libyang context */
+};
+
+/**
  * @defgroup dataparseroptions Data parser options
  * @ingroup datatree
  *
@@ -422,24 +471,31 @@
  */
 
 #define LYD_OPT_DATA       0x0 /**< Default type of data - complete datastore content with configuration as well as
-                                state data. */
-#define LYD_OPT_CONFIG     LYD_OPT_NO_STATE /**< A configuration datastore - complete datastore without state data. */
-#define LYD_OPT_GET        LYD_OPT_PARSE_ONLY /**< Data content from a NETCONF reply message to the NETCONF
-                                \<get\> operation. */
-#define LYD_OPT_GETCONFIG  LYD_OPT_PARSE_ONLY | LYD_OPT_NO_STATE /**< Data content from a NETCONF reply message to
-                                the NETCONF \<get-config\> operation. */
+                                    state data. */
+#define LYD_OPT_CONFIG     LYD_OPT_NO_STATE
+                                        /**< A configuration datastore - complete datastore without state data. */
+#define LYD_OPT_GET        LYD_OPT_PARSE_ONLY
+                                        /**< Data content from a NETCONF reply message to the NETCONF \<get\> operation. */
+#define LYD_OPT_GETCONFIG  LYD_OPT_PARSE_ONLY | LYD_OPT_NO_STATE
+                                        /**< Data content from a NETCONF reply message to the NETCONF \<get-config\> operation. */
+#define LYD_OPT_EDIT       LYD_OPT_OPAQ
+                                        /**< Data content of a NETCONF RPC \<edit-config\> operation. */
 
-#define LYD_OPT_PARSE_ONLY      0x0001 /**< Data will be only parsed and no validation will be performed. When statements
-                                            are kept unevaluated, union types may not be fully resolved, if-feature
-                                            statements are not checked, and default values are not added (only the ones
-                                            parsed are present). */
-#define LYD_OPT_TRUSTED         0x0002 /**< Data are considered trusted so they will be parsed as validated. If the parsed
-                                            data are not valid, using this flag may lead to some unexpected behavior!
-                                            This flag can be used only with #LYD_OPT_PARSE_ONLY. */
-#define LYD_OPT_STRICT          0x0004 /**< Instead of silently ignoring data without schema definition raise an error. */
-#define LYD_OPT_NO_STATE        0x0008 /**< Forbid state data in the parsed data. */
 
-#define LYD_OPT_MASK            0xFFFF /**< Mask for all the parser options. */
+#define LYD_OPT_PARSE_ONLY      0x0001  /**< Data will be only parsed and no validation will be performed. When statements
+                                             are kept unevaluated, union types may not be fully resolved, if-feature
+                                             statements are not checked, and default values are not added (only the ones
+                                             parsed are present). */
+#define LYD_OPT_TRUSTED         0x0002  /**< Data are considered trusted so they will be parsed as validated. If the parsed
+                                             data are not valid, using this flag may lead to some unexpected behavior!
+                                             This flag can be used only with #LYD_OPT_PARSE_ONLY. */
+#define LYD_OPT_STRICT          0x0004  /**< Instead of silently ignoring data without schema definition raise an error.
+                                             Do not combine with #LYD_OPT_OPAQ. */
+#define LYD_OPT_OPAQ            0x0008  /**< Instead of silently ignoring data without definition, parse them into
+                                             an opaq node. Do not combine with #LYD_OPT_STRICT. */
+#define LYD_OPT_NO_STATE        0x0010  /**< Forbid state data in the parsed data. */
+
+#define LYD_OPT_MASK            0xFFFF  /**< Mask for all the parser options. */
 
 /** @} dataparseroptions */
 
@@ -724,10 +780,20 @@
  *
  * @param[in] ctx Context where the metadata was created.
  * @param[in] meta Metadata to destroy
- * @param[in] recursive Zero to destroy only the metadata (the metadata list is corrected),
+ * @param[in] recursive Zero to destroy only the single metadata (the metadata list is corrected),
  * non-zero to destroy also all the subsequent metadata in the list.
  */
-void lyd_free_meta(struct ly_ctx *ctx, struct lyd_meta *meta, int recursive);
+void lyd_free_meta(const struct ly_ctx *ctx, struct lyd_meta *meta, int recursive);
+
+/**
+ * @brief Destroy attributes.
+ *
+ * @param[in] ctx Context where the attributes were created.
+ * @param[in] attr Attributes to destroy.
+ * @param[in] recursive Zero to destroy only the single attribute (the attribute list is corrected),
+ * non-zero to destroy also all the subsequent attributes in the list.
+ */
+void ly_free_attr(const struct ly_ctx *ctx, struct ly_attr *attr, int recursive);
 
 /**
  * @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value.
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
index 7aa63ae..beb1f40 100644
--- a/src/tree_data_free.c
+++ b/src/tree_data_free.c
@@ -26,7 +26,7 @@
 #include "plugins_types.h"
 
 void
-lyd_value_free_path(struct ly_ctx *ctx, struct lyd_value_path *path)
+lyd_value_free_path(const struct ly_ctx *ctx, struct lyd_value_path *path)
 {
     unsigned int u, v;
 
@@ -43,7 +43,7 @@
 }
 
 API void
-lyd_free_meta(struct ly_ctx *ctx, struct lyd_meta *meta, int recursive)
+lyd_free_meta(const struct ly_ctx *ctx, struct lyd_meta *meta, int recursive)
 {
     struct lyd_meta *iter;
 
@@ -75,7 +75,7 @@
         meta->next = NULL;
     }
 
-    for(iter = meta; iter; ) {
+    for (iter = meta; iter; ) {
         meta = iter;
         iter = iter->next;
 
@@ -85,52 +85,128 @@
     }
 }
 
+API void
+ly_free_attr(const struct ly_ctx *ctx, struct ly_attr *attr, int recursive)
+{
+    struct ly_attr *iter;
+    uint32_t u;
+
+    LY_CHECK_ARG_RET(NULL, ctx, );
+    if (!attr) {
+        return;
+    }
+
+    if (attr->parent) {
+        if (attr->parent->attr == attr) {
+            if (recursive) {
+                attr->parent->attr = NULL;
+            } else {
+                attr->parent->attr = attr->next;
+            }
+        } else {
+            for (iter = attr->parent->attr; iter->next != attr; iter = iter->next);
+            if (iter->next) {
+                if (recursive) {
+                    iter->next = NULL;
+                } else {
+                    iter->next = attr->next;
+                }
+            }
+        }
+    }
+
+    if (!recursive) {
+        attr->next = NULL;
+    }
+
+    for (iter = attr; iter; ) {
+        attr = iter;
+        iter = iter->next;
+
+        LY_ARRAY_FOR(attr->val_prefs, u) {
+            FREE_STRING(ctx, attr->val_prefs[u].pref);
+            FREE_STRING(ctx, attr->val_prefs[u].ns);
+        }
+        LY_ARRAY_FREE(attr->val_prefs);
+        FREE_STRING(ctx, attr->name);
+        FREE_STRING(ctx, attr->value);
+        FREE_STRING(ctx, attr->prefix.pref);
+        FREE_STRING(ctx, attr->prefix.ns);
+        free(attr);
+    }
+}
+
 /**
  * @brief Free Data (sub)tree.
- * @param[in] ctx libyang context.
  * @param[in] node Data node to be freed.
  * @param[in] top Recursion flag to unlink the root of the subtree being freed.
  */
 static void
-lyd_free_subtree(struct ly_ctx *ctx, struct lyd_node *node, int top)
+lyd_free_subtree(struct lyd_node *node, int top)
 {
     struct lyd_node *iter, *next;
     struct lyd_node *children;
+    struct lyd_node_opaq *opaq;
+    uint32_t u;
 
     assert(node);
 
-    /* remove children hash table in case of inner data node */
-    if (node->schema->nodetype & LYD_NODE_INNER) {
-        lyht_free(((struct lyd_node_inner*)node)->children_ht);
-        ((struct lyd_node_inner*)node)->children_ht = NULL;
+    if (!node->schema) {
+        opaq = (struct lyd_node_opaq *)node;
 
         /* free the children */
-        children = (struct lyd_node*)lyd_node_children(node);
+        children = (struct lyd_node *)lyd_node_children(node);
         LY_LIST_FOR_SAFE(children, next, iter) {
-            lyd_free_subtree(ctx, iter, 0);
+            lyd_free_subtree(iter, 0);
+        }
+
+        FREE_STRING(LYD_NODE_CTX(opaq), opaq->name);
+        FREE_STRING(LYD_NODE_CTX(opaq), opaq->prefix.pref);
+        FREE_STRING(LYD_NODE_CTX(opaq), opaq->prefix.ns);
+        if (opaq->val_prefs) {
+            LY_ARRAY_FOR(opaq->val_prefs, u) {
+                FREE_STRING(LYD_NODE_CTX(opaq), opaq->val_prefs[u].pref);
+                FREE_STRING(LYD_NODE_CTX(opaq), opaq->val_prefs[u].ns);
+            }
+            LY_ARRAY_FREE(opaq->val_prefs);
+        }
+        FREE_STRING(LYD_NODE_CTX(opaq), opaq->value);
+    } else if (node->schema->nodetype & LYD_NODE_INNER) {
+        /* remove children hash table in case of inner data node */
+        lyht_free(((struct lyd_node_inner *)node)->children_ht);
+        ((struct lyd_node_inner *)node)->children_ht = NULL;
+
+        /* free the children */
+        children = (struct lyd_node *)lyd_node_children(node);
+        LY_LIST_FOR_SAFE(children, next, iter) {
+            lyd_free_subtree(iter, 0);
         }
     } else if (node->schema->nodetype & LYD_NODE_ANY) {
-        switch (((struct lyd_node_any*)node)->value_type) {
+        switch (((struct lyd_node_any *)node)->value_type) {
         case LYD_ANYDATA_DATATREE:
-            lyd_free_all(((struct lyd_node_any*)node)->value.tree);
+            lyd_free_all(((struct lyd_node_any *)node)->value.tree);
             break;
         case LYD_ANYDATA_STRING:
         case LYD_ANYDATA_XML:
         case LYD_ANYDATA_JSON:
-            FREE_STRING(node->schema->module->ctx, ((struct lyd_node_any*)node)->value.str);
+            FREE_STRING(LYD_NODE_CTX(node), ((struct lyd_node_any *)node)->value.str);
             break;
 #if 0 /* TODO LYB format */
         case LYD_ANYDATA_LYB:
-            free(((struct lyd_node_any*)node)->value.mem);
+            free(((struct lyd_node_any *)node)->value.mem);
             break;
 #endif
         }
     } else if (node->schema->nodetype & LYD_NODE_TERM) {
-        ((struct lysc_node_leaf*)node->schema)->type->plugin->free(ctx, &((struct lyd_node_term*)node)->value);
+        ((struct lysc_node_leaf *)node->schema)->type->plugin->free(LYD_NODE_CTX(node), &((struct lyd_node_term *)node)->value);
     }
 
-    /* free the node's metadata */
-    lyd_free_meta(ctx, node->meta, 1);
+    if (!node->schema) {
+        ly_free_attr(LYD_NODE_CTX(node), opaq->attr, 1);
+    } else {
+        /* free the node's metadata */
+        lyd_free_meta(LYD_NODE_CTX(node), node->meta, 1);
+    }
 
     /* unlink only the nodes from the first level, nodes in subtree are freed all, so no unlink is needed */
     if (top) {
@@ -147,7 +223,7 @@
         return;
     }
 
-    lyd_free_subtree(node->schema->module->ctx, node, 1);
+    lyd_free_subtree(node, 1);
 }
 
 static void
@@ -161,7 +237,7 @@
 
     /* get the first (top-level) sibling */
     if (top) {
-        for (; node->parent; node = (struct lyd_node*)node->parent);
+        for (; node->parent; node = (struct lyd_node *)node->parent);
     }
     while (node->prev->next) {
         node = node->prev;
@@ -169,7 +245,7 @@
 
     LY_LIST_FOR_SAFE(node, next, iter) {
         /* in case of the top-level nodes (node->parent is NULL), no unlinking needed */
-        lyd_free_subtree(iter->schema->module->ctx, iter, iter->parent ? 1 : 0);
+        lyd_free_subtree(iter, iter->parent ? 1 : 0);
     }
 }
 
diff --git a/src/tree_data_hash.c b/src/tree_data_hash.c
index 33104cc..ff64300 100644
--- a/src/tree_data_hash.c
+++ b/src/tree_data_hash.c
@@ -46,6 +46,10 @@
 {
     struct lyd_node *iter;
 
+    if (!node->schema) {
+        return LY_SUCCESS;
+    }
+
     node->hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
     node->hash = dict_hash_multi(node->hash, node->schema->name, strlen(node->schema->name));
 
@@ -117,7 +121,7 @@
 {
     struct lyd_node *iter;
 
-    if (!node->parent) {
+    if (!node->parent || !node->schema || !node->parent->schema) {
         /* nothing to do */
         return LY_SUCCESS;
     }
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index 90f5736..8328ad7 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -26,14 +26,19 @@
 lyd_node_children_p(struct lyd_node *node)
 {
     assert(node);
-    switch (node->schema->nodetype) {
-    case LYS_CONTAINER:
-    case LYS_LIST:
-    case LYS_ACTION:
-    case LYS_NOTIF:
-        return &((struct lyd_node_inner*)node)->child;
-    default:
-        return NULL;
+
+    if (!node->schema) {
+        return &((struct lyd_node_opaq *)node)->child;
+    } else {
+        switch (node->schema->nodetype) {
+        case LYS_CONTAINER:
+        case LYS_LIST:
+        case LYS_ACTION:
+        case LYS_NOTIF:
+            return &((struct lyd_node_inner *)node)->child;
+        default:
+            return NULL;
+        }
     }
 }
 
@@ -59,7 +64,7 @@
 {
     const struct lysc_node *schema;
 
-    if (!node) {
+    if (!node || !node->schema) {
         return NULL;
     }
 
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index 2d92f2e..a942f77 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -18,6 +18,11 @@
 #include "tree_data.h"
 #include "plugins_types.h"
 
+#include <assert.h>
+#include <stddef.h>
+
+static_assert(offsetof(struct lyd_node, flags) == offsetof(struct lyd_node_opaq, flags), "");
+
 /**
  * @brief Check whether a node to be deleted is the first top-level sibling.
  *
@@ -113,6 +118,28 @@
                       struct lyd_node **node);
 
 /**
+ * @brief Create an opaque node.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] name Element name.
+ * @param[in] name_len Length of @p name, must be set correctly.
+ * @param[in] value String value to be parsed.
+ * @param[in] value_len Length of @p value, must be set correctly.
+ * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
+ * @param[in] format Input format of @p value and @p ns.
+ * @param[in] val_prefs Possible value prefixes, array is spent.
+ * @param[in] prefix Element prefix.
+ * @param[in] pref_len Length of @p prefix, must be set correctly.
+ * @param[in] ns Node namespace, meaning depends on @p format.
+ * @param[out] node Created node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR lyd_create_opaq(const struct ly_ctx *ctx, const char *name, size_t name_len, const char *value, size_t value_len,
+                       int *dynamic, LYD_FORMAT format, struct ly_prefix *val_prefs, const char *prefix, size_t pref_len,
+                       const char *ns, struct lyd_node **node);
+
+/**
  * @brief Find the key after which to insert the new key.
  *
  * @param[in] first_sibling List first sibling.
@@ -135,9 +162,9 @@
 void lyd_insert_node(struct lyd_node *parent, struct lyd_node **first_sibling, struct lyd_node *node);
 
 /**
- * @brief Create and insert an attribute (last) into a parent.
+ * @brief Create and insert a metadata (last) into a parent.
  *
- * @param[in] parent Parent of the attribute, can be NULL.
+ * @param[in] parent Parent of the metadata, can be NULL.
  * @param[in,out] meta Metadata list to add at its end if @p parent is NULL, returned created attribute.
  * @param[in] mod Metadata module (with the annotation definition).
  * @param[in] name Attribute name.
@@ -145,7 +172,7 @@
  * @param[in] value String value to be parsed.
  * @param[in] value_len Length of @p value, must be set correctly.
  * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
- * @param[in] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
+ * @param[in] resolve_prefix Parser-specific getter to resolve prefixes used in the @p value string.
  * @param[in] prefix_data User data for @p get_prefix.
  * @param[in] format Input format of @p value.
  * @param[in] ctx_snode Context node for value resolution in schema.
@@ -154,10 +181,33 @@
  * @return LY_ERR value if an error occurred.
  */
 LY_ERR lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod, const char *name,
-                       size_t name_len, const char *value, size_t value_len, int *dynamic, ly_clb_resolve_prefix get_prefix,
+                       size_t name_len, const char *value, size_t value_len, int *dynamic, ly_clb_resolve_prefix resolve_prefix,
                        void *prefix_data, LYD_FORMAT format, const struct lysc_node *ctx_snode);
 
 /**
+ * @brief Create and insert a generic attribute (last) into a parent.
+ *
+ * @param[in] parent Parent of the attribute, can be NULL.
+ * @param[in,out] attr Attribute list to add at its end if @p parent is NULL, returned created attribute.
+ * @param[in] ctx libyang context.
+ * @param[in] name Attribute name.
+ * @param[in] name_len Length of @p name, must be set correctly.
+ * @param[in] value String value to be parsed.
+ * @param[in] value_len Length of @p value, must be set correctly.
+ * @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
+ * @param[in] format Input format of @p value and @p ns.
+ * @param[in] val_prefs Possible value prefixes, array is spent.
+ * @param[in] prefix Attribute prefix.
+ * @param[in] prefix_len Attribute prefix length.
+ * @param[in] ns Attribute namespace, meaning depends on @p format.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value if an error occurred.
+ */
+LY_ERR ly_create_attr(struct lyd_node *parent, struct ly_attr **attr, const struct ly_ctx *ctx, const char *name,
+                      size_t name_len, const char *value, size_t value_len, int *dynamic, LYD_FORMAT format,
+                      struct ly_prefix *val_prefs, const char *prefix, size_t prefix_len, const char *ns);
+
+/**
  * @brief Validate, canonize and store the given @p value into the node according to the node's type's rules.
  *
  * @param[in] node Data node for the @p value.
@@ -270,7 +320,7 @@
  * @param[in] ctx libyang context.
  * @param[in] path The structure ([sized array](@ref sizedarrays)) to free.
  */
-void lyd_value_free_path(struct ly_ctx *ctx, struct lyd_value_path *path);
+void lyd_value_free_path(const struct ly_ctx *ctx, struct lyd_value_path *path);
 
 /**
  * @brief Find the node, in the list, satisfying the given restrictions.
diff --git a/src/tree_schema.h b/src/tree_schema.h
index d139498..0d23f0c 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -21,9 +21,9 @@
 #include <stdint.h>
 #include <stdio.h>
 
+#include "tree_data.h"
 #include "log.h"
 #include "tree.h"
-#include "tree_data.h"
 
 struct ly_ctx;
 
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index a9649e3..5dff020 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -6995,7 +6995,7 @@
         do {
             LY_ARRAY_FOR(node->when, j) {
                 when = node->when[j];
-                ret = lyxp_atomize(when->cond, LYD_UNKNOWN, when->module, when->context,
+                ret = lyxp_atomize(when->cond, LYD_SCHEMA, when->module, when->context,
                                 when->context ? LYXP_NODE_ELEM : LYXP_NODE_ROOT_CONFIG, &tmp_set, LYXP_SCNODE_SCHEMA);
                 if (ret != LY_SUCCESS) {
                     LOGVAL(set->ctx, LY_VLOG_LYSC, node, LYVE_SEMANTICS, "Invalid when condition \"%s\".", when->cond->expr);
@@ -7099,7 +7099,7 @@
 
     /* check "when" */
     LY_ARRAY_FOR(when, i) {
-        ret = lyxp_atomize(when[i]->cond, LYD_UNKNOWN, when[i]->module, when[i]->context,
+        ret = lyxp_atomize(when[i]->cond, LYD_SCHEMA, when[i]->module, when[i]->context,
                            when[i]->context ? LYXP_NODE_ELEM : LYXP_NODE_ROOT_CONFIG, &tmp_set, opts);
         if (ret != LY_SUCCESS) {
             LOGVAL(ctx->ctx, LY_VLOG_LYSC, node, LYVE_SEMANTICS, "Invalid when condition \"%s\".", when[i]->cond->expr);
@@ -7137,7 +7137,7 @@
 check_musts:
     /* check "must" */
     LY_ARRAY_FOR(musts, i) {
-        ret = lyxp_atomize(musts[i].cond, LYD_UNKNOWN, musts[i].module, node, LYXP_NODE_ELEM, &tmp_set, opts);
+        ret = lyxp_atomize(musts[i].cond, LYD_SCHEMA, musts[i].module, node, LYXP_NODE_ELEM, &tmp_set, opts);
         if (ret != LY_SUCCESS) {
             LOGVAL(ctx->ctx, LY_VLOG_LYSC, node, LYVE_SEMANTICS, "Invalid must restriction \"%s\".", musts[i].cond->expr);
             goto cleanup;
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 3fd214a..4c65540 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -1678,7 +1678,7 @@
  * In this case the @p prefix is searched in the list of imports' prefixes (not the prefixes of the imported modules themselves).
  */
 const struct lys_module *
-lys_resolve_prefix(struct ly_ctx *UNUSED(ctx), const char *prefix, size_t prefix_len, void *private)
+lys_resolve_prefix(const struct ly_ctx *UNUSED(ctx), const char *prefix, size_t prefix_len, void *private)
 {
     return lys_module_find_prefix((const struct lys_module*)private, prefix, prefix_len);
 }
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 457a596..9b927e0 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -194,7 +194,7 @@
  *
  * In this case the @p prefix is searched in the list of imports' prefixes (not the prefixes of the imported modules themselves).
  */
-const struct lys_module *lys_resolve_prefix(struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *private);
+const struct lys_module *lys_resolve_prefix(const struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *private);
 
 /**
  * @brief Check the currently present prefixes in the module for collision with the new one.
diff --git a/src/validation.c b/src/validation.c
index b8168dd..5be3030 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -12,10 +12,13 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
+#include "common.h"
+
 #include <assert.h>
 #include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
 
-#include "common.h"
 #include "xpath.h"
 #include "tree_data_internal.h"
 #include "tree_schema_internal.h"
@@ -96,7 +99,7 @@
     }
 
     /* evaluate when */
-    ret = lyxp_eval(when->cond, LYD_UNKNOWN, when->module, ctx_node, ctx_node ? LYXP_NODE_ELEM : LYXP_NODE_ROOT_CONFIG,
+    ret = lyxp_eval(when->cond, LYD_SCHEMA, when->module, ctx_node, ctx_node ? LYXP_NODE_ELEM : LYXP_NODE_ROOT_CONFIG,
                     *tree, &xp_set, LYXP_SCHEMA);
     lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
 
diff --git a/src/xml.c b/src/xml.c
index a65c1ba..ff76c31 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -487,6 +487,38 @@
 }
 
 LY_ERR
+lyxml_skip_element(struct lyxml_context *context, const char **input)
+{
+    LY_ERR ret;
+    unsigned int parents_count = context->elements.count;
+
+    while (context->elements.count >= parents_count) {
+        /* skip attributes */
+        while (context->status == LYXML_ATTRIBUTE) {
+            LY_CHECK_RET(lyxml_get_attribute(context, input, NULL, NULL, NULL, NULL));
+        }
+
+        /* skip content */
+        if (context->status == LYXML_ELEM_CONTENT) {
+            ret = lyxml_get_string(context, input, NULL, NULL, NULL, NULL, NULL);
+            if (ret && (ret != LY_EINVAL)) {
+                return ret;
+            }
+        }
+
+        if (context->status != LYXML_ELEMENT) {
+            LOGINT(context->ctx);
+            return LY_EINT;
+        }
+
+        /* nested element/closing element */
+        LY_CHECK_RET(lyxml_get_element(context, input, NULL, NULL, NULL, NULL));
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
 lyxml_get_string(struct lyxml_context *context, const char **input, char **buffer, size_t *buffer_size, char **output,
                  size_t *length, int *dynamic)
 {
@@ -837,91 +869,92 @@
     unsigned int c;
     size_t endtag_len;
     int is_ns = 0;
-    const char *ns_prefix = NULL;
-    size_t ns_prefix_len = 0;
+    const char *ns_prefix;
+    size_t ns_prefix_len;
 
-start:
     /* initialize output variables */
     (*prefix) = (*name) = NULL;
     (*prefix_len) = (*name_len) = 0;
 
-    /* skip initial whitespaces */
-    ign_xmlws(context, in);
+    do {
+        /* skip initial whitespaces */
+        ign_xmlws(context, in);
 
-    if (in[0] == '\0') {
-        /* EOF - not expected at this place */
-        LOGVAL(ctx, LY_VLOG_LINE, &context->line, LY_VCODE_EOF);
-        return LY_EVALID;
-    }
+        if (in[0] == '\0') {
+            /* EOF - not expected at this place */
+            LOGVAL(ctx, LY_VLOG_LINE, &context->line, LY_VCODE_EOF);
+            return LY_EVALID;
+        }
 
-    /* remember the identifier start before checking its format */
-    id = in;
-    rc = lyxml_check_qname(context, &in, &c, &endtag_len);
-    LY_CHECK_RET(rc);
-    if (c == ':') {
-        /* we have prefixed identifier */
-        endtag = in - endtag_len;
-
+        /* remember the identifier start before checking its format */
+        id = in;
         rc = lyxml_check_qname(context, &in, &c, &endtag_len);
         LY_CHECK_RET(rc);
+        if (c == ':') {
+            /* we have prefixed identifier */
+            endtag = in - endtag_len;
 
-        (*prefix) = id;
-        (*prefix_len) = endtag - id;
-        id = endtag + 1;
-    }
-    if (!is_xmlws(c) && c != '=') {
-        in = in - endtag_len;
-        LOGVAL(ctx, LY_VLOG_LINE, &context->line, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(in), in, "whitespace or '='");
-        return LY_EVALID;
-    }
-    in = in - endtag_len;
-    (*name) = id;
-    (*name_len) = in - id;
+            rc = lyxml_check_qname(context, &in, &c, &endtag_len);
+            LY_CHECK_RET(rc);
 
-    /* eat '=' and stop at the value beginning */
-    ign_xmlws(context, in);
-    if (in[0] != '=') {
-        LOGVAL(ctx, LY_VLOG_LINE, &context->line, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(in), in, "'='");
-        return LY_EVALID;
-    }
-    ++in;
-    ign_xmlws(context, in);
-    if (in[0] != '\'' && in[0] != '"') {
-        LOGVAL(ctx, LY_VLOG_LINE, &context->line, LY_VCODE_INSTREXP,
-               LY_VCODE_INSTREXP_len(in), in, "either single or double quotation mark");
-        return LY_EVALID;
-    }
-    context->status = LYXML_ATTR_CONTENT;
-
-    is_ns = 0;
-    if (*prefix && *prefix_len == 5 && !strncmp(*prefix, "xmlns", 5)) {
-        is_ns = 1;
-        ns_prefix = *name;
-        ns_prefix_len = *name_len;
-    } else if (*name_len == 5 && !strncmp(*name, "xmlns", 5)) {
-        is_ns = 1;
-    }
-    if (is_ns) {
-        /* instead of attribute, we have namespace specification,
-         * so process it automatically and then move to another attribute (if any) */
-        char *value = NULL;
-        size_t value_len = 0;
-        int dynamic = 0;
-
-        LY_CHECK_RET(lyxml_get_string(context, &in, &value, &value_len, &value, &value_len, &dynamic));
-        if ((rc = lyxml_ns_add(context, ns_prefix, ns_prefix_len, dynamic ? value : strndup(value, value_len)))) {
-            if (dynamic) {
-                free(value);
-                return rc;
-            }
+            (*prefix) = id;
+            (*prefix_len) = endtag - id;
+            id = endtag + 1;
         }
-        if (context->status == LYXML_ATTRIBUTE) {
-            goto start;
-        } else {
+        if (!is_xmlws(c) && c != '=') {
+            in = in - endtag_len;
+            LOGVAL(ctx, LY_VLOG_LINE, &context->line, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(in), in, "whitespace or '='");
+            return LY_EVALID;
+        }
+        in = in - endtag_len;
+        (*name) = id;
+        (*name_len) = in - id;
+
+        /* eat '=' and stop at the value beginning */
+        ign_xmlws(context, in);
+        if (in[0] != '=') {
+            LOGVAL(ctx, LY_VLOG_LINE, &context->line, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(in), in, "'='");
+            return LY_EVALID;
+        }
+        ++in;
+        ign_xmlws(context, in);
+        if (in[0] != '\'' && in[0] != '"') {
+            LOGVAL(ctx, LY_VLOG_LINE, &context->line, LY_VCODE_INSTREXP,
+                LY_VCODE_INSTREXP_len(in), in, "either single or double quotation mark");
+            return LY_EVALID;
+        }
+        context->status = LYXML_ATTR_CONTENT;
+
+        is_ns = 0;
+        if (*prefix && *prefix_len == 5 && !strncmp(*prefix, "xmlns", 5)) {
+            is_ns = 1;
+            ns_prefix = *name;
+            ns_prefix_len = *name_len;
+        } else if (*name_len == 5 && !strncmp(*name, "xmlns", 5)) {
+            is_ns = 1;
+            ns_prefix = NULL;
+            ns_prefix_len = 0;
+        }
+        if (is_ns) {
+            /* instead of attribute, we have namespace specification,
+            * so process it automatically and then move to another attribute (if any) */
+            char *value = NULL;
+            size_t value_len = 0;
+            int dynamic = 0;
+
+            LY_CHECK_RET(lyxml_get_string(context, &in, &value, &value_len, &value, &value_len, &dynamic));
+            if ((rc = lyxml_ns_add(context, ns_prefix, ns_prefix_len, dynamic ? value : strndup(value, value_len)))) {
+                if (dynamic) {
+                    free(value);
+                    return rc;
+                }
+            }
+
+            /* do not return ns */
             (*prefix) = (*name) = NULL;
             (*prefix_len) = (*name_len) = 0;
         }
-    }
+    } while (is_ns && (context->status == LYXML_ATTRIBUTE));
 
     /* move caller's input */
     (*input) = in;
@@ -980,3 +1013,118 @@
     return ret;
 }
 
+LY_ERR
+lyxml_get_prefixes(struct lyxml_context *ctx, const char *value, size_t value_len, struct ly_prefix **val_prefs)
+{
+    LY_ERR ret;
+    uint32_t u, c;
+    const struct lyxml_ns *ns;
+    const char *start, *stop;
+    struct ly_prefix *prefixes = NULL;
+    size_t len;
+
+    for (stop = start = value; (size_t)(stop - value) < value_len; start = stop) {
+        size_t bytes;
+        ly_getutf8(&stop, &c, &bytes);
+        if (is_xmlqnamestartchar(c)) {
+            for (ly_getutf8(&stop, &c, &bytes);
+                    is_xmlqnamechar(c) && (size_t)(stop - value) < value_len;
+                    ly_getutf8(&stop, &c, &bytes));
+            stop = stop - bytes;
+            if (*stop == ':') {
+                /* we have a possible prefix */
+                len = stop - start;
+                ns = lyxml_ns_get(ctx, start, len);
+                if (ns) {
+                    struct ly_prefix *p = NULL;
+
+                    /* check whether we do not already have this prefix stored */
+                    LY_ARRAY_FOR(prefixes, u) {
+                        if (!ly_strncmp(prefixes[u].pref, start, len)) {
+                            p = &prefixes[u];
+                            break;
+                        }
+                    }
+                    if (!p) {
+                        LY_ARRAY_NEW_GOTO(ctx->ctx, prefixes, p, ret, error);
+                        p->pref = lydict_insert(ctx->ctx, start, len);
+                        p->ns = lydict_insert(ctx->ctx, ns->uri, 0);
+                    } /* else the prefix already present */
+                }
+            }
+            stop = stop + bytes;
+        }
+    }
+
+    *val_prefs = prefixes;
+    return LY_SUCCESS;
+
+error:
+    LY_ARRAY_FOR(prefixes, u) {
+        lydict_remove(ctx->ctx, prefixes[u].pref);
+    }
+    LY_ARRAY_FREE(prefixes);
+    return ret;
+}
+
+LY_ERR
+lyxml_value_compare(const char *value1, const struct ly_prefix *prefs1, const char *value2, const struct ly_prefix *prefs2)
+{
+    const char *ptr1, *ptr2, *ns1, *ns2;
+    uint32_t u1, u2;
+    int len;
+
+    if (!value1 && !value2) {
+        return LY_SUCCESS;
+    }
+    if ((value1 && !value2) || (!value1 && value2)) {
+        return LY_ENOT;
+    }
+
+    ptr1 = value1;
+    ptr2 = value2;
+    while (ptr1[0] && ptr2[0]) {
+        if (ptr1[0] != ptr2[0]) {
+            /* it can be a start of prefix that maps to the same module */
+            ns1 = ns2 = NULL;
+            if (prefs1) {
+                /* find module of the first prefix, if any */
+                LY_ARRAY_FOR(prefs1, u1) {
+                    len = strlen(prefs1[u1].pref);
+                    if (!strncmp(ptr1, prefs1[u1].pref, len) && (ptr1[len] == ':')) {
+                        ns1 = prefs1[u1].ns;
+                        break;
+                    }
+                }
+            }
+            if (prefs2) {
+                /* find module of the second prefix, if any */
+                LY_ARRAY_FOR(prefs2, u2) {
+                    len = strlen(prefs2[u2].pref);
+                    if (!strncmp(ptr2, prefs2[u2].pref, len) && (ptr2[len] == ':')) {
+                        ns2 = prefs2[u2].ns;
+                        break;
+                    }
+                }
+            }
+
+            if (!ns1 || !ns2 || (ns1 != ns2)) {
+                /* not a prefix or maps to different namespaces */
+                break;
+            }
+
+            /* skip prefixes in both values (':' is skipped as iter) */
+            ptr1 += strlen(prefs1[u1].pref);
+            ptr2 += strlen(prefs2[u2].pref);
+        }
+
+        ++ptr1;
+        ++ptr2;
+    }
+    if (ptr1[0] || ptr2[0]) {
+        /* not a match or simply different lengths */
+        return LY_ENOT;
+    }
+
+    return LY_SUCCESS;
+}
diff --git a/src/xml.h b/src/xml.h
index 7965e7e..f81c49b 100644
--- a/src/xml.h
+++ b/src/xml.h
@@ -22,6 +22,7 @@
 #include "set.h"
 
 struct lyout;
+struct ly_prefix;
 
 /* Macro to test if character is whitespace */
 #define is_xmlws(c) (c == 0x20 || c == 0x9 || c == 0xa || c == 0xd)
@@ -105,6 +106,15 @@
                          const char **prefix, size_t *prefix_len, const char **name, size_t *name_len);
 
 /**
+ * @brief Skip an element after its opening tag was parsed.
+ *
+ * @param[in] context XML context.
+ * @param[in,out] input Input string to process, updated according to the read data.
+ * @return LY_ERR values.
+ */
+LY_ERR lyxml_skip_element(struct lyxml_context *context, const char **input);
+
+/**
  * @brief Parse input expecting an XML attribute (including XML namespace).
  *
  * Input string is not being modified, so the returned values are not NULL-terminated, instead their length
@@ -193,4 +203,29 @@
  */
 void lyxml_context_clear(struct lyxml_context *context);
 
+/**
+ * @brief Find all possible prefixes in a value.
+ *
+ * @param[in] ctx XML context to use.
+ * @param[in] value Value to check.
+ * @param[in] value_len Value length.
+ * @param[out] val_prefs Array of found prefixes.
+ * @return LY_ERR value.
+ */
+LY_ERR lyxml_get_prefixes(struct lyxml_context *ctx, const char *value, size_t value_len, struct ly_prefix **val_prefs);
+
+/**
+ * @brief Compare values and their prefix mappings.
+ *
+ * @param[in] value1 First value.
+ * @param[in] prefs1 First value prefixes.
+ * @param[in] value2 Second value.
+ * @param[in] prefs2 Second value prefixes.
+ * @return LY_SUCCESS if values are equal.
+ * @return LY_ENOT if values are not equal.
+ * @return LY_ERR on error.
+ */
+LY_ERR lyxml_value_compare(const char *value1, const struct ly_prefix *prefs1, const char *value2,
+                           const struct ly_prefix *prefs2);
+
 #endif /* LY_XML_H_ */
diff --git a/src/xpath.c b/src/xpath.c
index f9a8009..e84bad9 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -298,7 +298,7 @@
  * @return LY_ERR
  */
 static LY_ERR
-cast_string_realloc(struct ly_ctx *ctx, uint16_t needed, char **str, uint16_t *used, uint16_t *size)
+cast_string_realloc(const struct ly_ctx *ctx, uint16_t needed, char **str, uint16_t *used, uint16_t *size)
 {
     if (*size - *used < needed) {
         do {
@@ -4127,13 +4127,13 @@
 
     if (mod && name) {
         switch (set->format) {
-        case LYD_UNKNOWN:
+        case LYD_SCHEMA:
             rc = asprintf(&str, "%s:%s", lys_prefix_find_module(set->local_mod, mod), name);
             break;
         case LYD_JSON:
             rc = asprintf(&str, "%s:%s", mod->name, name);
             break;
-        default:
+        case LYD_XML:
             LOGINT_RET(set->ctx);
         }
         LY_CHECK_ERR_RET(rc == -1, LOGMEM(set->ctx), LY_EMEM);
@@ -5164,7 +5164,7 @@
         pref_len = ptr - *qname;
 
         switch (set->format) {
-        case LYD_UNKNOWN:
+        case LYD_SCHEMA:
             /* schema, search all local module imports */
             mod = lys_module_find_prefix(set->local_mod, *qname, pref_len);
             break;
@@ -5174,7 +5174,7 @@
             mod = ly_ctx_get_module(set->ctx, str, NULL);
             free(str);
             break;
-        default:
+        case LYD_XML:
             LOGINT_RET(set->ctx);
         }