data FEATURE parser for YANG data in JSON format
diff --git a/src/tree_data.c b/src/tree_data.c
index 5ca3f4a..a5572d7 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -55,14 +55,14 @@
                                       struct lyd_node **match);
 
 LY_ERR
-lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int *dynamic, int second,
-                ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node *tree)
+lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int *dynamic, int second, int value_hint,
+                ly_resolve_prefix_clb get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node *tree)
 {
     LY_ERR ret = LY_SUCCESS;
     struct ly_err_item *err = NULL;
     struct ly_ctx *ctx;
     struct lysc_type *type;
-    int options = LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
+    int options = value_hint | LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
             (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
     assert(node);
 
@@ -96,7 +96,7 @@
 /* similar to lyd_value_parse except can be used just to store the value, hence also does not support a second call */
 LY_ERR
 lyd_value_store(struct lyd_value *val, const struct lysc_node *schema, const char *value, size_t value_len, int *dynamic,
-                ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format)
+                ly_resolve_prefix_clb get_prefix, void *parser, LYD_FORMAT format)
 {
     LY_ERR ret = LY_SUCCESS;
     struct ly_err_item *err = NULL;
@@ -128,13 +128,13 @@
 
 LY_ERR
 lyd_value_parse_meta(const struct ly_ctx *ctx, struct lyd_meta *meta, const char *value, size_t value_len, int *dynamic,
-                     int second, ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,
+                     int second, int value_hint, ly_resolve_prefix_clb get_prefix, void *parser, LYD_FORMAT format,
                      const struct lysc_node *ctx_snode, const struct lyd_node *tree)
 {
     LY_ERR ret = LY_SUCCESS;
     struct ly_err_item *err = NULL;
     struct lyext_metadata *ant;
-    int options = LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
+    int options = value_hint | LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
             (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
 
     assert(ctx && meta && ((tree && meta->parent) || ctx_snode));
@@ -163,7 +163,7 @@
 
 LY_ERR
 _lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
-                    ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format)
+                    ly_resolve_prefix_clb resolve_prefix, void *prefix_data, LYD_FORMAT format)
 {
     LY_ERR rc = LY_SUCCESS;
     struct ly_err_item *err = NULL;
@@ -309,10 +309,8 @@
 
         if (len >= 5 && !strncmp(&path[len - 4], ".xml", 4)) {
             format = LYD_XML;
-#if 0
         } else if (len >= 6 && !strncmp(&path[len - 5], ".json", 5)) {
             format = LYD_JSON;
-#endif
         } else if (len >= 5 && !strncmp(&path[len - 4], ".lyb", 4)) {
             format = LYD_LYB;
         } /* else still unknown */
@@ -325,6 +323,9 @@
 lyd_parse_data(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, int parse_options, int validate_options,
                struct lyd_node **tree)
 {
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_ctx *lydctx = NULL;
+
     LY_CHECK_ARG_RET(ctx, ctx, in, tree, LY_EINVAL);
     LY_CHECK_ARG_RET(ctx, !(parse_options & ~LYD_PARSE_OPTS_MASK), LY_EINVAL);
     LY_CHECK_ARG_RET(ctx, !(validate_options & ~LYD_VALIDATE_OPTS_MASK), LY_EINVAL);
@@ -332,26 +333,73 @@
     format = lyd_parse_get_format(in, format);
     LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
 
+    /* init */
+    *tree = NULL;
+
     /* remember input position */
     in->func_start = in->current;
 
     switch (format) {
     case LYD_XML:
-        return lyd_parse_xml_data(ctx, in, parse_options, validate_options, tree);
-#if 0
+        LY_CHECK_RET(lyd_parse_xml_data(ctx, in, parse_options, validate_options, tree, &lydctx));
+        break;
     case LYD_JSON:
-        return lyd_parse_json_data(ctx, in, parse_options, validate_options, tree);
-#endif
+        LY_CHECK_RET(lyd_parse_json_data(ctx, in, parse_options, validate_options, tree, &lydctx));
+        break;
     case LYD_LYB:
-        return lyd_parse_lyb_data(ctx, in, parse_options, validate_options, tree);
+        LY_CHECK_RET(lyd_parse_lyb_data(ctx, in, parse_options, validate_options, tree, &lydctx));
+        break;
     case LYD_SCHEMA:
         LOGINT_RET(ctx);
     }
 
-    /* TODO move here the top-level validation from parser_xml.c's lyd_parse_xml_data() and make
-     * it common for all the lyd_parse_*_data() functions */
+    if (!(parse_options & LYD_PARSE_ONLY)) {
+        uint32_t i = 0;
+        const struct lys_module *mod;
+        struct lyd_node *first, *next, **first2;
 
-    LOGINT_RET(ctx);
+        next = *tree;
+        while (1) {
+            if (validate_options & LYD_VALIDATE_PRESENT) {
+                mod = lyd_data_next_module(&next, &first);
+            } else {
+                mod = lyd_mod_next_module(next, NULL, ctx, &i, &first);
+            }
+            if (!mod) {
+                break;
+            }
+            if (first == *tree) {
+                /* make sure first2 changes are carried to tree */
+                first2 = tree;
+            } else {
+                first2 = &first;
+            }
+
+            /* validate new top-level nodes, autodelete CANNOT occur, all nodes are new */
+            LY_CHECK_GOTO(ret = lyd_validate_new(first2, NULL, mod, NULL), cleanup);
+
+            /* add all top-level defaults for this module */
+            ret = lyd_new_implicit_r(NULL, first2, NULL, mod, &lydctx->unres_node_type, &lydctx->when_check,
+                                     (validate_options & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+            LY_CHECK_GOTO(ret, cleanup);
+
+            /* finish incompletely validated terminal values/attributes and when conditions */
+            ret = lyd_validate_unres(tree, &lydctx->when_check, &lydctx->unres_node_type, &lydctx->unres_meta_type, LYD_XML,
+                                     lydctx->resolve_prefix, lydctx->data_ctx, NULL);
+            LY_CHECK_GOTO(ret, cleanup);
+
+            /* perform final validation that assumes the data tree is final */
+            LY_CHECK_GOTO(ret = lyd_validate_final_r(*first2, NULL, mod, validate_options, 0), cleanup);
+        }
+    }
+
+cleanup:
+    lydctx->free((struct lyd_ctx *)lydctx);
+    if (ret) {
+        lyd_free_all(*tree);
+        *tree = NULL;
+    }
+    return ret;
 }
 
 API LY_ERR
@@ -404,16 +452,20 @@
     format = lyd_parse_get_format(in, format);
     LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
 
+    /* init */
+    *tree = NULL;
+    if (op) {
+        *op = NULL;
+    }
+
     /* remember input position */
     in->func_start = in->current;
 
     switch (format) {
     case LYD_XML:
         return lyd_parse_xml_rpc(ctx, in, tree, op);
-#if 0
     case LYD_JSON:
         return lyd_parse_json_rpc(ctx, in, tree, op);
-#endif
     case LYD_LYB:
         return lyd_parse_lyb_rpc(ctx, in, tree, op);
     case LYD_SCHEMA:
@@ -428,21 +480,27 @@
                 struct lyd_node **op)
 {
     LY_CHECK_ARG_RET(NULL, request, LY_EINVAL);
-    LY_CHECK_ARG_RET(LYD_NODE_CTX(request), in, tree, LY_EINVAL);
+    LY_CHECK_ARG_RET(LYD_NODE_CTX(request), in, tree || op, LY_EINVAL);
 
     format = lyd_parse_get_format(in, format);
     LY_CHECK_ARG_RET(LYD_NODE_CTX(request), format, LY_EINVAL);
 
+    /* init */
+    if (tree) {
+        *tree = NULL;
+    }
+    if (op) {
+        *op = NULL;
+    }
+
     /* remember input position */
     in->func_start = in->current;
 
     switch (format) {
     case LYD_XML:
         return lyd_parse_xml_reply(request, in, tree, op);
-#if 0
     case LYD_JSON:
         return lyd_parse_json_reply(request, in, tree, op);
-#endif
     case LYD_LYB:
         return lyd_parse_lyb_reply(request, in, tree, op);
     case LYD_SCHEMA:
@@ -455,21 +513,27 @@
 API LY_ERR
 lyd_parse_notif(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, struct lyd_node **tree, struct lyd_node **ntf)
 {
-    LY_CHECK_ARG_RET(ctx, ctx, in, tree, LY_EINVAL);
+    LY_CHECK_ARG_RET(ctx, ctx, in, tree || ntf, LY_EINVAL);
 
     format = lyd_parse_get_format(in, format);
     LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
 
+    /* init */
+    if (tree) {
+        *tree = NULL;
+    }
+    if (ntf) {
+        *ntf = NULL;
+    }
+
     /* remember input position */
     in->func_start = in->current;
 
     switch (format) {
     case LYD_XML:
         return lyd_parse_xml_notif(ctx, in, tree, ntf);
-#if 0
     case LYD_JSON:
         return lyd_parse_json_notif(ctx, in, tree, ntf);
-#endif
     case LYD_LYB:
         return lyd_parse_lyb_notif(ctx, in, tree, ntf);
     case LYD_SCHEMA:
@@ -480,8 +544,8 @@
 }
 
 LY_ERR
-lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, int *dynamic,
-                ly_clb_resolve_prefix get_prefix, void *prefix_data, LYD_FORMAT format, struct lyd_node **node)
+lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, int *dynamic, int value_hint,
+                ly_resolve_prefix_clb get_prefix, void *prefix_data, LYD_FORMAT format, struct lyd_node **node)
 {
     LY_ERR ret;
     struct lyd_node_term *term;
@@ -495,7 +559,7 @@
     term->prev = (struct lyd_node *)term;
     term->flags = LYD_NEW;
 
-    ret = lyd_value_parse(term, value, value_len, dynamic, 0, get_prefix, prefix_data, format, NULL);
+    ret = lyd_value_parse(term, value, value_len, dynamic, 0, value_hint, get_prefix, prefix_data, format, NULL);
     if (ret && (ret != LY_EINCOMPLETE)) {
         free(term);
         return ret;
@@ -630,7 +694,8 @@
     any->prev = (struct lyd_node *)any;
     any->flags = LYD_NEW;
 
-    any->value.xml = value;
+    /* TODO: convert XML/JSON strings into a opaq data tree */
+    any->value.str = value;
     any->value_type = value_type;
     lyd_hash((struct lyd_node *)any);
 
@@ -640,12 +705,12 @@
 
 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)
+                int *dynamic, int value_hint, LYD_FORMAT format, struct ly_prefix *val_prefs, const char *prefix, size_t pref_len,
+                const char *module_key, size_t module_key_len, struct lyd_node **node)
 {
     struct lyd_node_opaq *opaq;
 
-    assert(ctx && name && name_len && ns);
+    assert(ctx && name && name_len);
 
     if (!value_len) {
         value = "";
@@ -659,9 +724,12 @@
     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.id = lydict_insert(ctx, prefix, pref_len);
     }
-    opaq->prefix.ns = lydict_insert(ctx, ns, 0);
+    if (module_key_len) {
+        opaq->prefix.module_ns = lydict_insert(ctx, module_key, module_key_len);
+    }
+
     opaq->val_prefs = val_prefs;
     if (dynamic && *dynamic) {
         opaq->value = lydict_insert_zc(ctx, (char *)value);
@@ -669,6 +737,7 @@
     } else {
         opaq->value = lydict_insert(ctx, value, value_len);
     }
+    opaq->hint = value_hint;
     opaq->ctx = ctx;
 
     *node = (struct lyd_node *)opaq;
@@ -731,7 +800,7 @@
     for (key_s = lysc_node_children(schema, 0); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
         key_val = va_arg(ap, const char *);
 
-        rc = lyd_create_term(key_s, key_val, key_val ? strlen(key_val) : 0, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &key);
+        rc = lyd_create_term(key_s, key_val, key_val ? strlen(key_val) : 0, NULL, 0, lydjson_resolve_prefix, NULL, LYD_JSON, &key);
         LY_CHECK_GOTO(rc && (rc != LY_EINCOMPLETE), cleanup);
         rc = LY_SUCCESS;
         lyd_insert_node(ret, NULL, key);
@@ -808,7 +877,7 @@
     schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYD_NODE_TERM, 0);
     LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Term node \"%s\" not found.", name), LY_ENOTFOUND);
 
-    rc = lyd_create_term(schema, val_str, val_str ? strlen(val_str) : 0, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &ret);
+    rc = lyd_create_term(schema, val_str, val_str ? strlen(val_str) : 0, NULL, 0, lydjson_resolve_prefix, NULL, LYD_JSON, &ret);
     LY_CHECK_RET(rc && (rc != LY_EINCOMPLETE), rc);
     if (parent) {
         lyd_insert_node(parent, NULL, ret);
@@ -952,7 +1021,7 @@
         val_str = "";
     }
 
-    LY_CHECK_RET(lyd_create_meta(parent, &ret, module, name, name_len, val_str, strlen(val_str), NULL,
+    LY_CHECK_RET(lyd_create_meta(parent, &ret, module, name, name_len, val_str, strlen(val_str), NULL, 0,
                                  lydjson_resolve_prefix, NULL, LYD_JSON, parent->schema));
 
     if (meta) {
@@ -976,8 +1045,8 @@
         value = "";
     }
 
-    LY_CHECK_RET(lyd_create_opaq(ctx, name, strlen(name), value, strlen(value), NULL, LYD_JSON, NULL, NULL, 0,
-                                 module_name, &ret));
+    LY_CHECK_RET(lyd_create_opaq(ctx, name, strlen(name), value, strlen(value), NULL, 0,
+                                 LYD_JSON, NULL, NULL, 0, module_name, strlen(module_name), &ret));
     if (parent) {
         lyd_insert_node(parent, NULL, ret);
     }
@@ -990,9 +1059,9 @@
 
 API LY_ERR
 lyd_new_attr(struct lyd_node *parent, const char *module_name, const char *name, const char *val_str,
-             struct ly_attr **attr)
+             struct lyd_attr **attr)
 {
-    struct ly_attr *ret = NULL;
+    struct lyd_attr *ret = NULL;
     const struct ly_ctx *ctx;
     const char *prefix, *tmp;
     size_t pref_len, name_len;
@@ -1013,8 +1082,8 @@
         val_str = "";
     }
 
-    LY_CHECK_RET(ly_create_attr(parent, &ret, ctx, name, name_len, val_str, strlen(val_str), NULL, LYD_JSON, NULL,
-                                prefix, pref_len, module_name));
+    LY_CHECK_RET(lyd_create_attr(parent, &ret, ctx, name, name_len, val_str, strlen(val_str), NULL, 0, LYD_JSON, NULL,
+                                 prefix, pref_len, module_name, module_name ? strlen(module_name) : 0));
 
     if (attr) {
         *attr = ret;
@@ -1117,7 +1186,7 @@
 
     /* parse the new value into a new meta structure */
     LY_CHECK_GOTO(ret = lyd_create_meta(NULL, &m2, meta->annotation->module, meta->name, strlen(meta->name), val_str,
-                                        strlen(val_str), NULL, lydjson_resolve_prefix, NULL, LYD_JSON, NULL), cleanup);
+                                        strlen(val_str), NULL, 0, lydjson_resolve_prefix, NULL, LYD_JSON, NULL), cleanup);
 
     /* compare original and new value */
     if (lyd_compare_meta(meta, m2)) {
@@ -1238,8 +1307,9 @@
             if (!(schema->flags & LYS_KEYLESS)) {
                 if ((options & LYD_NEWOPT_OPAQ) && (p[path_idx].pred_type == LY_PATH_PREDTYPE_NONE)) {
                     /* creating opaque list without keys */
-                    LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL,
-                                                        LYD_JSON, NULL, NULL, 0, schema->module->name, &node), cleanup);
+                    LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL, 0,
+                                                        LYD_JSON, NULL, NULL, 0, schema->module->name, strlen(schema->module->name), &node),
+                                  cleanup);
                 } else {
                     assert(p[path_idx].pred_type == LY_PATH_PREDTYPE_LIST);
                     LY_CHECK_GOTO(ret = lyd_create_list(schema, p[path_idx].predicates, &node), cleanup);
@@ -1256,8 +1326,9 @@
         case LYS_LEAFLIST:
             if ((options & LYD_NEWOPT_OPAQ) && (p[path_idx].pred_type == LY_PATH_PREDTYPE_NONE)) {
                 /* creating opaque leaf-list without value */
-                LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL,
-                                                    LYD_JSON, NULL, NULL, 0, schema->module->name, &node), cleanup);
+                LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL, 0,
+                                                    LYD_JSON, NULL, NULL, 0, schema->module->name, strlen(schema->module->name), &node),
+                              cleanup);
             } else {
                 assert(p[path_idx].pred_type == LY_PATH_PREDTYPE_LEAFLIST);
                 LY_CHECK_GOTO(ret = lyd_create_term2(schema, &p[path_idx].predicates[0].value, &node), cleanup);
@@ -1274,12 +1345,13 @@
                 r = lys_value_validate(NULL, schema, value, strlen(value));
             }
             if (!r) {
-                LY_CHECK_GOTO(ret = lyd_create_term(schema, value, strlen(value), NULL, lydjson_resolve_prefix, NULL,
+                LY_CHECK_GOTO(ret = lyd_create_term(schema, value, strlen(value), NULL, 0, lydjson_resolve_prefix, NULL,
                                                     LYD_JSON, &node), cleanup);
             } else {
                 /* creating opaque leaf without value */
-                LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL,
-                                                    LYD_JSON, NULL, NULL, 0, schema->module->name, &node), cleanup);
+                LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL, 0,
+                                                    LYD_JSON, NULL, NULL, 0, schema->module->name, strlen(schema->module->name), &node),
+                              cleanup);
             }
             break;
         case LYS_ANYDATA:
@@ -2030,8 +2102,37 @@
 }
 
 LY_ERR
+lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta)
+{
+    struct lyd_meta *last, *iter;
+
+    assert(parent);
+    assert(meta);
+
+    for (iter = meta; iter; iter = iter->next) {
+        iter->parent = parent;
+    }
+
+    /* insert as the last attribute */
+    if (parent->meta) {
+        for (last = parent->meta; last->next; last = last->next);
+        last->next = meta;
+    } else {
+        parent->meta = meta;
+    }
+
+    /* remove default flags from NP containers */
+    while (parent && (parent->schema->nodetype == LYS_CONTAINER) && (parent->flags & LYD_DEFAULT)) {
+        parent->flags &= ~LYD_DEFAULT;
+        parent = (struct lyd_node *)parent->parent;
+    }
+
+    return LY_SUCCESS;
+}
+
+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 resolve_prefix,
+                size_t name_len, const char *value, size_t value_len, int *dynamic, int value_hint, ly_resolve_prefix_clb resolve_prefix,
                 void *prefix_data, LYD_FORMAT format, const struct lysc_node *ctx_snode)
 {
     LY_ERR ret;
@@ -2060,7 +2161,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, resolve_prefix, prefix_data, format, ctx_snode, NULL);
+    ret = lyd_value_parse_meta(mod->ctx, mt, value, value_len, dynamic, 0, value_hint, resolve_prefix, prefix_data, format, ctx_snode, NULL);
     if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
         free(mt);
         return ret;
@@ -2069,23 +2170,12 @@
 
     /* insert as the last attribute */
     if (parent) {
-        if (parent->meta) {
-            for (last = parent->meta; last->next; last = last->next);
-            last->next = mt;
-        } else {
-            parent->meta = mt;
-        }
+        LY_CHECK_RET(lyd_insert_meta(parent, mt))
     } else if (*meta) {
         for (last = *meta; last->next; last = last->next);
         last->next = mt;
     }
 
-    /* remove default flags from NP containers */
-    while (parent && (parent->schema->nodetype == LYS_CONTAINER) && (parent->flags & LYD_DEFAULT)) {
-        parent->flags &= ~LYD_DEFAULT;
-        parent = (struct lyd_node *)parent->parent;
-    }
-
     if (meta) {
         *meta = mt;
     }
@@ -2093,16 +2183,15 @@
 }
 
 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)
+lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const struct ly_ctx *ctx, const char *name,
+                size_t name_len, const char *value, size_t value_len, int *dynamic, int value_hint, LYD_FORMAT format,
+                struct ly_prefix *val_prefs, const char *prefix, size_t prefix_len, const char *module_key, size_t module_key_len)
 {
-    struct ly_attr *at, *last;
+    struct lyd_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 = "";
@@ -2119,11 +2208,14 @@
         at->value = lydict_insert(ctx, value, value_len);
     }
 
+    at->hint = value_hint;
     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);
+    if (prefix_len) {
+        at->prefix.id = lydict_insert(ctx, prefix, prefix_len);
+    }
+    if (module_key_len) {
+        at->prefix.module_ns = lydict_insert(ctx, module_key, module_key_len);
     }
 
     /* insert as the last attribute */
@@ -2187,7 +2279,7 @@
     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)) {
+        if ((opaq1->name != opaq2->name) || (opaq1->format != opaq2->format) || (opaq1->prefix.module_ns != opaq2->prefix.module_ns)) {
             return LY_ENOT;
         }
         switch (opaq1->format) {
@@ -2196,6 +2288,12 @@
                 return LY_ENOT;
             }
             break;
+        case LYD_JSON:
+            /* prefixes in JSON are unique, so it is not necessary to canonize the values */
+            if (strcmp(opaq1->value, opaq2->value)) {
+                return LY_ENOT;
+            }
+            break;
         case LYD_SCHEMA:
         case LYD_LYB:
             /* not allowed */
@@ -2438,17 +2536,15 @@
         }
         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.id) {
+            opaq->prefix.id = lydict_insert(LYD_NODE_CTX(node), orig->prefix.id, 0);
         }
-        if (orig->prefix.ns) {
-            opaq->prefix.ns = lydict_insert(LYD_NODE_CTX(node), orig->prefix.ns, 0);
-        }
+        opaq->prefix.module_ns = lydict_insert(LYD_NODE_CTX(node), orig->prefix.module_ns, 0);
         if (orig->val_prefs) {
             LY_ARRAY_CREATE_GOTO(LYD_NODE_CTX(node), opaq->val_prefs, LY_ARRAY_COUNT(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);
+                opaq->val_prefs[u].id = lydict_insert(LYD_NODE_CTX(node), orig->val_prefs[u].id, 0);
+                opaq->val_prefs[u].module_ns = lydict_insert(LYD_NODE_CTX(node), orig->val_prefs[u].module_ns, 0);
                 LY_ARRAY_INCREMENT(opaq->val_prefs);
             }
         }
@@ -2954,22 +3050,23 @@
 iter_print:
             /* print prefix and name */
             mod = NULL;
-            if (!iter->parent || (iter->schema->module != iter->parent->schema->module)) {
+            if (iter->schema && (!iter->parent || iter->schema->module != iter->parent->schema->module)) {
                 mod = iter->schema->module;
             }
 
             /* realloc string */
-            len = 1 + (mod ? strlen(mod->name) + 1 : 0) + strlen(iter->schema->name);
+            len = 1 + (mod ? strlen(mod->name) + 1 : 0) + (iter->schema ? strlen(iter->schema->name) : strlen(((struct lyd_node_opaq*)iter)->name));
             rc = lyd_path_str_enlarge(&buffer, &buflen, bufused + len, is_static);
             if (rc != LY_SUCCESS) {
                 break;
             }
 
             /* print next node */
-            bufused += sprintf(buffer + bufused, "/%s%s%s", mod ? mod->name : "", mod ? ":" : "", iter->schema->name);
+            bufused += sprintf(buffer + bufused, "/%s%s%s", mod ? mod->name : "", mod ? ":" : "",
+                               iter->schema ? iter->schema->name : ((struct lyd_node_opaq*)iter)->name);
 
             /* do not always print the last (first) predicate */
-            if (bufused || (pathtype == LYD_PATH_LOG)) {
+            if (iter->schema && (bufused || (pathtype == LYD_PATH_LOG))) {
                 switch (iter->schema->nodetype) {
                 case LYS_LIST:
                     if (iter->schema->flags & LYS_KEYLESS) {
@@ -3216,7 +3313,7 @@
         /* create a data node and find the instance */
         if (schema->nodetype == LYS_LEAFLIST) {
             /* target used attributes: schema, hash, value */
-            rc = lyd_create_term(schema, key_or_value, val_len, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &target);
+            rc = lyd_create_term(schema, key_or_value, val_len, NULL, 0, lydjson_resolve_prefix, NULL, LYD_JSON, &target);
             LY_CHECK_RET(rc && (rc != LY_EINCOMPLETE), rc);
         } else {
             /* target used attributes: schema, hash, child (all keys) */