libyang CHANGE add LY_VALUE_CANON

Add a separate value format of the canonical representation of the type.
The print callback API of the type plugins was modified to reflect the
need of creating canonical representation of the value.
diff --git a/src/path.c b/src/path.c
index 1fbe07b..7ca8616 100644
--- a/src/path.c
+++ b/src/path.c
@@ -451,6 +451,7 @@
             /* inherit module of the previous node */
             *mod = prev_ctx_node->module;
             break;
+        case LY_VALUE_CANON:
         case LY_VALUE_XML:
             /* not really defined */
             LOGINT_RET(ctx);
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 320ae7a..98c4b15 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -137,6 +137,7 @@
     case LY_VALUE_XML:
         mod = ly_xml_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
         break;
+    case LY_VALUE_CANON:
     case LY_VALUE_JSON:
         mod = ly_json_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
         break;
@@ -157,6 +158,7 @@
         case LY_VALUE_SCHEMA_RESOLVED:
             /* use context node module, handles augments */
             return ctx_node->module;
+        case LY_VALUE_CANON:
         case LY_VALUE_JSON:
             /* use context node module (as specified) */
             return ctx_node->module;
@@ -250,6 +252,7 @@
     case LY_VALUE_XML:
         prefix = ly_xml_get_prefix(mod, prefix_data);
         break;
+    case LY_VALUE_CANON:
     case LY_VALUE_JSON:
         prefix = ly_json_get_prefix(mod, prefix_data);
         break;
@@ -279,11 +282,16 @@
 }
 
 API const char *
-lyplg_type_print_simple(const struct lyd_value *value, LY_VALUE_FORMAT UNUSED(format),
-        void *UNUSED(prefix_data), ly_bool *dynamic)
+lyplg_type_print_simple(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT UNUSED(format),
+        void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
 {
-    *dynamic = 0;
-    return (char *)value->canonical;
+    if (dynamic) {
+        *dynamic = 0;
+    }
+    if (value_len) {
+        *value_len = strlen(value->canonical);
+    }
+    return value->canonical;
 }
 
 API LY_ERR
@@ -686,6 +694,9 @@
     *err = NULL;
 
     switch (format) {
+    case LY_VALUE_CANON:
+        LOGARG(ctx, format);
+        return LY_EINVAL;
     case LY_VALUE_SCHEMA:
     case LY_VALUE_SCHEMA_RESOLVED:
     case LY_VALUE_XML:
@@ -769,7 +780,6 @@
     LY_ERR ret;
     struct lyxp_set set = {0};
     const char *val_str;
-    ly_bool dynamic;
     uint32_t i;
     int rc;
 
@@ -778,14 +788,11 @@
             &set, 0);
     if (ret) {
         ret = LY_ENOTFOUND;
-        val_str = lref->plugin->print(value, LY_VALUE_JSON, NULL, &dynamic);
+        val_str = lref->plugin->print(lref->cur_mod->ctx, value, LY_VALUE_CANON, NULL, NULL, NULL);
         if (asprintf(errmsg, "Invalid leafref value \"%s\" - XPath evaluation error.", val_str) == -1) {
             *errmsg = NULL;
             ret = LY_EMEM;
         }
-        if (dynamic) {
-            free((char *)val_str);
-        }
         goto error;
     }
 
@@ -801,7 +808,7 @@
     }
     if (i == set.used) {
         ret = LY_ENOTFOUND;
-        val_str = lref->plugin->print(value, LY_VALUE_JSON, NULL, &dynamic);
+        val_str = lref->plugin->print(lref->cur_mod->ctx, value, LY_VALUE_CANON, NULL, NULL, NULL);
         if (set.used) {
             rc = asprintf(errmsg, LY_ERRMSG_NOLREF_VAL, val_str, lref->path->expr);
         } else {
@@ -811,9 +818,6 @@
             *errmsg = NULL;
             ret = LY_EMEM;
         }
-        if (dynamic) {
-            free((char *)val_str);
-        }
         goto error;
     }
 
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 9bc539d..dc661b2 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -349,26 +349,28 @@
 /** @} plugintypestoreopts */
 
 /**
- * @brief Callback to store and canonize the given @p value according to the given @p type.
+ * @brief Callback to store the given @p value according to the given @p type.
  *
  * Value must always be correctly stored meaning all the other type callbacks (such as print or compare)
- * must function as expected.
+ * must function as expected. However, ::lyd_value.canonical can be left NULL and will be generated
+ * and stored on-demand. But if @p format is ::LY_VALUE_CANON (or another, which must be equal to the canonical
+ * value), the canonical value should be stored so that it does not have to be generated later.
  *
- * Note that the \p value string is not necessarily zero-terminated. The provided \p value_len is always correct.
- * All store function have to free dynamically allocated value in case they dont used it.
- * Also, in case of any error store function have to call free on dynamically allocated value.
+ * Note that the @p value is not necessarily zero-terminated. The provided @p value_len is always correct.
+ * All store functions have to free a dynamically allocated @p value in all cases (even error).
  *
  * @param[in] ctx libyang Context
  * @param[in] type Type of the value being stored.
  * @param[in] value Lexical representation of the value to be stored.
  *            It is never NULL, empty string is represented as "" with zero @p value_len.
- * @param[in] value_len Length (number of bytes) of the given \p value.
+ * @param[in] value_len Length (number of bytes) of the given @p value.
  * @param[in] options [Type plugin store options](@ref plugintypestoreopts).
- * @param[in] format Input format of the value.
+ * @param[in] format Input format of the value, see the description for details.
  * @param[in] prefix_data Format-specific data for resolving any prefixes (see ly_resolve_prefix()).
  * @param[in] hints Bitmap of [value hints](@ref lydvalhints) of all the allowed value types.
- * @param[in] ctx_node The @p value schema context node.
- * @param[out] storage Storage for the value in the type's specific encoding. All the members should be filled by the plugin.
+ * @param[in] ctx_node Schema context node of @p value.
+ * @param[out] storage Storage for the value in the type's specific encoding. Except for canonical, all the members
+ * should be filled by the plugin (if it fills them at all).
  * @param[in,out] unres Global unres structure for newly implemented modules.
  * @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic
  *             error message is prepared instead. The error structure can be created by ::ly_err_new().
@@ -404,7 +406,7 @@
  * @brief Callback for comparing 2 values of the same type.
  *
  * In case the value types (::lyd_value.realtype) are different, ::LY_ENOT must always be returned.
- * It can be assumed that the same libyang context (dictionary) was used for storing both values.
+ * It can be assumed that the same context (dictionary) was used for storing both values.
  *
  * @param[in] val1 First value to compare.
  * @param[in] val2 Second value to compare.
@@ -414,26 +416,30 @@
 typedef LY_ERR (*lyplg_type_compare_clb)(const struct lyd_value *val1, const struct lyd_value *val2);
 
 /**
- * @brief Callback to getting the canonical value of the data stored in @p value.
+ * @brief Callback for getting the value of the data stored in @p value.
  *
+ * Canonical value (@p format of ::LY_VALUE_CANON) must always be a zero-terminated const string stored in
+ * the dictionary. The ::lyd_value._canonical member should be used for storing (caching) it.
+ *
+ * @param[in] ctx libyang context for storing the canonical value.
  * @param[in] value Value to print.
- * @param[in] format Format in which the data are supposed to be printed.
- *            Only 2 formats are currently implemented: ::LY_VALUE_XML and ::LY_VALUE_JSON.
+ * @param[in] format Format in which the data are supposed to be printed. Formats ::LY_VALUE_SCHEMA and
+ * ::LY_VALUE_SCHEMA_RESOLVED are not supported and should not be implemented.
  * @param[in] prefix_data Format-specific data for processing prefixes. In case of using one of the built-in's print
  * callback (or ::lyplg_type_print_simple()), the argument is just simply passed in. If you need to handle prefixes
  * in the value on your own, there is ::lyplg_type_get_prefix() function to help.
- * @param[out] dynamic Flag if the returned string is dynamically allocated. In such a case the caller is responsible
- *            for freeing it.
- * @return String with the value of @p value in specified @p format. According to the returned @p dynamic flag, caller
- *         can be responsible for freeing allocated memory.
+ * @param[out] dynamic Flag if the returned value is dynamically allocated. In such a case the caller is responsible
+ * for freeing it. Will not be set and should be ignored for @p format ::LY_VALUE_CANON.
+ * @param[out] value_len Optional returned value length in bytes. For strings it EXCLUDES the terminating zero.
+ * @return Pointer to @p value in the specified @p format. According to the returned @p dynamic flag, caller
+ * can be responsible for freeing allocated memory.
  * @return NULL in case of error.
  */
-typedef const char *(*lyplg_type_print_clb)(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data,
-        ly_bool *dynamic);
+typedef const char *(*lyplg_type_print_clb)(const struct ly_ctx *ctx, const struct lyd_value *value,
+        LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
 
 /**
- * @brief Callback to duplicate data in data structure. Note that callback is even responsible for
- * duplicating ::lyd_value.canonical.
+ * @brief Callback to duplicate data in data structure.
  *
  * @param[in] ctx libyang context of the @p dup. Note that the context of @p original and @p dup might not be the same.
  * @param[in] original Original data structure to be duplicated.
@@ -505,7 +511,8 @@
  * @brief Generic simple printer callback of the canonized value.
  * Implementation of the ::lyplg_type_print_clb.
  */
-const char *lyplg_type_print_simple(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic);
+const char *lyplg_type_print_simple(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *prefix_data, ly_bool *dynamic, size_t *value_len);
 
 /**
  * @brief Generic simple duplication callback.
@@ -673,8 +680,8 @@
  * @brief Printer callback printing identityref value.
  * Implementation of the ::lyplg_type_print_clb.
  */
-const char *lyplg_type_print_identityref(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data,
-        ly_bool *dynamic);
+const char *lyplg_type_print_identityref(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data,
+        ly_bool *dynamic, size_t *value_len);
 
 /** @} pluginsTypesIdentityref */
 
@@ -704,8 +711,8 @@
  * @brief Printer callback printing the instance-identifier value.
  * Implementation of the ::lyplg_type_print_clb.
  */
-const char *lyplg_type_print_instanceid(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data,
-        ly_bool *dynamic);
+const char *lyplg_type_print_instanceid(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data,
+        ly_bool *dynamic, size_t *value_len);
 
 /**
  * @brief Duplication callback of the instance-identifier values.
@@ -780,8 +787,8 @@
  * @brief Printer callback printing the leafref value.
  * Implementation of the ::lyplg_type_print_clb.
  */
-const char *lyplg_type_print_leafref(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data,
-        ly_bool *dynamic);
+const char *lyplg_type_print_leafref(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data,
+        ly_bool *dynamic, size_t *value_len);
 
 /**
  * @brief Duplication callback of the leafref values.
@@ -848,8 +855,8 @@
  * @brief Printer callback printing the union value.
  * Implementation of the ::lyplg_type_print_clb.
  */
-const char *lyplg_type_print_union(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data,
-        ly_bool *dynamic);
+const char *lyplg_type_print_union(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data,
+        ly_bool *dynamic, size_t *value_len);
 
 /**
  * @brief Duplication callback of the union values.
@@ -898,8 +905,8 @@
  * @brief Printer callback printing the xpath1.0 value.
  * Implementation of the ::lyplg_type_print_clb.
  */
-const char *lyplg_type_print_xpath10(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data,
-        ly_bool *dynamic);
+const char *lyplg_type_print_xpath10(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *prefix_data, ly_bool *dynamic, size_t *value_len);
 
 /**
  * @brief Duplication callback of the xpath1.0 values.
diff --git a/src/plugins_types/identityref.c b/src/plugins_types/identityref.c
index 14e4e92..a26ed18 100644
--- a/src/plugins_types/identityref.c
+++ b/src/plugins_types/identityref.c
@@ -29,13 +29,13 @@
 #include "compat.h"
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
-API const char *
-lyplg_type_print_identityref(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic)
+static char *
+identityref_print(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data)
 {
     char *result = NULL;
 
-    *dynamic = 1;
-    if (asprintf(&result, "%s:%s", lyplg_type_get_prefix(value->ident->module, format, prefix_data), value->ident->name) == -1) {
+    if (asprintf(&result, "%s:%s", lyplg_type_get_prefix(value->ident->module, format, prefix_data),
+            value->ident->name) == -1) {
         return NULL;
     } else {
         return result;
@@ -55,7 +55,6 @@
     const struct lys_module *mod = NULL;
     LY_ARRAY_COUNT_TYPE u;
     struct lysc_ident *ident = NULL, *identities, *base;
-    ly_bool dyn;
 
     *err = NULL;
 
@@ -147,8 +146,7 @@
     storage->ident = ident;
 
     /* get JSON form since there is no canonical */
-    str = (char *)lyplg_type_print_identityref(storage, LY_VALUE_JSON, NULL, &dyn);
-    assert(str && dyn);
+    str = identityref_print(storage, LY_VALUE_JSON, NULL);
     ret = lydict_insert_zc(ctx, str, &storage->canonical);
     LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
     storage->realtype = type;
@@ -161,6 +159,30 @@
     return ret;
 }
 
+API const char *
+lyplg_type_print_identityref(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *prefix_data, ly_bool *dynamic, size_t *value_len)
+{
+    char *result;
+
+    if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON)) {
+        if (dynamic) {
+            *dynamic = 0;
+        }
+        if (value_len) {
+            *value_len = strlen(value->canonical);
+        }
+        return value->canonical;
+    }
+
+    result = identityref_print(value, format, prefix_data);
+    *dynamic = 1;
+    if (value_len) {
+        *value_len = strlen(result);
+    }
+    return result;
+}
+
 API LY_ERR
 lyplg_type_compare_identityref(const struct lyd_value *val1, const struct lyd_value *val2)
 {
diff --git a/src/plugins_types/instanceid.c b/src/plugins_types/instanceid.c
index 53e8015..789afb9 100644
--- a/src/plugins_types/instanceid.c
+++ b/src/plugins_types/instanceid.c
@@ -32,7 +32,8 @@
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
 API const char *
-lyplg_type_print_instanceid(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic)
+lyplg_type_print_instanceid(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *prefix_data, ly_bool *dynamic, size_t *value_len)
 {
     LY_ARRAY_COUNT_TYPE u, v;
     char *result = NULL;
@@ -60,7 +61,7 @@
                 case LY_PATH_PREDTYPE_LIST: {
                     /* key-predicate */
                     ly_bool d = 0;
-                    const char *str = pred->value.realtype->plugin->print(&pred->value, format, prefix_data, &d);
+                    const char *str = pred->value.realtype->plugin->print(ctx, &pred->value, format, prefix_data, &d, NULL);
                     char quot = '\'';
                     if (strchr(str, quot)) {
                         quot = '"';
@@ -75,7 +76,7 @@
                 case LY_PATH_PREDTYPE_LEAFLIST: {
                     /* leaf-list-predicate */
                     ly_bool d = 0;
-                    const char *str = pred->value.realtype->plugin->print(&pred->value, format, prefix_data, &d);
+                    const char *str = pred->value.realtype->plugin->print(ctx, &pred->value, format, prefix_data, &d, NULL);
                     char quot = '\'';
                     if (strchr(str, quot)) {
                         quot = '"';
@@ -89,64 +90,76 @@
                 }
             }
         }
-    } else if (format == LY_VALUE_JSON) {
-        /* only the first node or the node changing module is prefixed */
-        struct lys_module *mod = NULL;
-        LY_ARRAY_FOR(value->target, u) {
-            if (mod != value->target[u].node->module) {
-                mod = value->target[u].node->module;
-                ly_strcat(&result, "/%s:%s", lyplg_type_get_prefix(mod, format, prefix_data), value->target[u].node->name);
-            } else {
-                ly_strcat(&result, "/%s", value->target[u].node->name);
-            }
-            LY_ARRAY_FOR(value->target[u].predicates, v) {
-                struct ly_path_predicate *pred = &value->target[u].predicates[v];
+        *dynamic = 1;
+    } else if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON)) {
+        /* generate canonical, only the first node or the node changing module is prefixed */
+        if (!value->canonical) {
+            struct lys_module *mod = NULL;
+            LY_ARRAY_FOR(value->target, u) {
+                if (mod != value->target[u].node->module) {
+                    mod = value->target[u].node->module;
+                    ly_strcat(&result, "/%s:%s", lyplg_type_get_prefix(mod, format, prefix_data), value->target[u].node->name);
+                } else {
+                    ly_strcat(&result, "/%s", value->target[u].node->name);
+                }
+                LY_ARRAY_FOR(value->target[u].predicates, v) {
+                    struct ly_path_predicate *pred = &value->target[u].predicates[v];
 
-                switch (value->target[u].pred_type) {
-                case LY_PATH_PREDTYPE_NONE:
-                    break;
-                case LY_PATH_PREDTYPE_POSITION:
-                    /* position predicate */
-                    ly_strcat(&result, "[%" PRIu64 "]", pred->position);
-                    break;
-                case LY_PATH_PREDTYPE_LIST: {
-                    /* key-predicate */
-                    ly_bool d = 0;
-                    const char *str = pred->value.realtype->plugin->print(&pred->value, format, prefix_data, &d);
-                    char quot = '\'';
-                    if (strchr(str, quot)) {
-                        quot = '"';
+                    switch (value->target[u].pred_type) {
+                    case LY_PATH_PREDTYPE_NONE:
+                        break;
+                    case LY_PATH_PREDTYPE_POSITION:
+                        /* position predicate */
+                        ly_strcat(&result, "[%" PRIu64 "]", pred->position);
+                        break;
+                    case LY_PATH_PREDTYPE_LIST: {
+                        /* key-predicate */
+                        ly_bool d = 0;
+                        const char *str = pred->value.realtype->plugin->print(ctx, &pred->value, format, prefix_data, &d, NULL);
+                        char quot = '\'';
+                        if (strchr(str, quot)) {
+                            quot = '"';
+                        }
+                        ly_strcat(&result, "[%s=%c%s%c]", pred->key->name, quot, str, quot);
+                        if (d) {
+                            free((char *)str);
+                        }
+                        break;
                     }
-                    ly_strcat(&result, "[%s=%c%s%c]", pred->key->name, quot, str, quot);
-                    if (d) {
-                        free((char *)str);
+                    case LY_PATH_PREDTYPE_LEAFLIST: {
+                        /* leaf-list-predicate */
+                        ly_bool d = 0;
+                        const char *str = pred->value.realtype->plugin->print(ctx, &pred->value, format, prefix_data, &d, NULL);
+                        char quot = '\'';
+                        if (strchr(str, quot)) {
+                            quot = '"';
+                        }
+                        ly_strcat(&result, "[.=%c%s%c]", quot, str, quot);
+                        if (d) {
+                            free((char *)str);
+                        }
+                        break;
                     }
-                    break;
-                }
-                case LY_PATH_PREDTYPE_LEAFLIST: {
-                    /* leaf-list-predicate */
-                    ly_bool d = 0;
-                    const char *str = pred->value.realtype->plugin->print(&pred->value, format, prefix_data, &d);
-                    char quot = '\'';
-                    if (strchr(str, quot)) {
-                        quot = '"';
                     }
-                    ly_strcat(&result, "[.=%c%s%c]", quot, str, quot);
-                    if (d) {
-                        free((char *)str);
-                    }
-                    break;
-                }
                 }
             }
+
+            lydict_insert_zc(ctx, result, (const char **)&value->canonical);
+        }
+
+        /* use canonical */
+        result = (char *)value->canonical;
+        if (dynamic) {
+            *dynamic = 0;
         }
     } else {
         /* not supported format */
-        free(result);
         return NULL;
     }
 
-    *dynamic = 1;
+    if (value_len) {
+        *value_len = strlen(result);
+    }
     return result;
 }
 
@@ -157,9 +170,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lysc_type_instanceid *type_inst = (struct lysc_type_instanceid *)type;
-    char *str;
     struct ly_path *path = NULL;
-    ly_bool dyn;
 
     /* init */
     *err = NULL;
@@ -174,13 +185,11 @@
     /* store resolved schema path */
     storage->target = path;
     path = NULL;
-
-    /* store JSON string value */
-    str = (char *)lyplg_type_print_instanceid(storage, LY_VALUE_JSON, NULL, &dyn);
-    assert(str && dyn);
-    LY_CHECK_GOTO(ret = lydict_insert_zc(ctx, str, &storage->canonical), cleanup);
     storage->realtype = type;
 
+    /* generate canonical value */
+    lyplg_type_print_instanceid(ctx, storage, LY_VALUE_CANON, NULL, NULL, NULL);
+
     /* cleanup */
 cleanup:
     lyplg_type_lypath_free(ctx, path);
@@ -199,7 +208,7 @@
 }
 
 API LY_ERR
-lyplg_type_validate_instanceid(const struct ly_ctx *UNUSED(ctx), const struct lysc_type *UNUSED(type),
+lyplg_type_validate_instanceid(const struct ly_ctx *ctx, const struct lysc_type *UNUSED(type),
         const struct lyd_node *UNUSED(ctx_node), const struct lyd_node *tree, struct lyd_value *storage,
         struct ly_err_item **err)
 {
@@ -214,8 +223,14 @@
     }
 
     /* find the target in data */
-    ret = ly_path_eval(storage->target, tree, NULL);
-    return ly_err_new(err, ret, LYVE_DATA, NULL, NULL, LY_ERRMSG_NOINST, storage->canonical);
+    if (!(ret = ly_path_eval(storage->target, tree, NULL))) {
+        return LY_SUCCESS;
+    }
+
+    /* error */
+    const char *value = storage->realtype->plugin->print(ctx, storage, LY_VALUE_CANON, NULL, NULL, NULL);
+
+    return ly_err_new(err, ret, LYVE_DATA, NULL, NULL, LY_ERRMSG_NOINST, value);
 }
 
 API LY_ERR
diff --git a/src/plugins_types/leafref.c b/src/plugins_types/leafref.c
index 58508d3..a704687 100644
--- a/src/plugins_types/leafref.c
+++ b/src/plugins_types/leafref.c
@@ -90,9 +90,10 @@
 }
 
 API const char *
-lyplg_type_print_leafref(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic)
+lyplg_type_print_leafref(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *prefix_data, ly_bool *dynamic, size_t *value_len)
 {
-    return value->realtype->plugin->print(value, format, prefix_data, dynamic);
+    return value->realtype->plugin->print(ctx, value, format, prefix_data, dynamic, value_len);
 }
 
 API LY_ERR
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
index bd11ec3..bbe8987 100644
--- a/src/plugins_types/union.c
+++ b/src/plugins_types/union.c
@@ -192,9 +192,18 @@
 }
 
 API const char *
-lyplg_type_print_union(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic)
+lyplg_type_print_union(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *prefix_data, ly_bool *dynamic, size_t *value_len)
 {
-    return value->subvalue->value.realtype->plugin->print(&value->subvalue->value, format, prefix_data, dynamic);
+    const void *ret;
+
+    ret = value->subvalue->value.realtype->plugin->print(ctx, &value->subvalue->value, format, prefix_data, dynamic, value_len);
+    if (!value->canonical && (format == LY_VALUE_CANON)) {
+        /* the canonical value is supposed to be stored now */
+        lydict_insert(ctx, value->subvalue->value.canonical, 0, (const char **)&value->canonical);
+    }
+
+    return ret;
 }
 
 API LY_ERR
diff --git a/src/plugins_types/xpath1.0.c b/src/plugins_types/xpath1.0.c
index f71e9cb..d08b1f5 100644
--- a/src/plugins_types/xpath1.0.c
+++ b/src/plugins_types/xpath1.0.c
@@ -244,14 +244,20 @@
 }
 
 API const char *
-lyplg_type_print_xpath10(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic)
+lyplg_type_print_xpath10(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *prefix_data, ly_bool *dynamic, size_t *value_len)
 {
     char *str_value;
     struct ly_err_item *err = NULL;
 
-    if (format == LY_VALUE_JSON) {
+    if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON)) {
         /* canonical */
-        *dynamic = 0;
+        if (dynamic) {
+            *dynamic = 0;
+        }
+        if (value_len) {
+            *value_len = strlen(value->canonical);
+        }
         return value->canonical;
     }
 
diff --git a/src/printer_json.c b/src/printer_json.c
index 3ac61c8..44ebe59 100644
--- a/src/printer_json.c
+++ b/src/printer_json.c
@@ -332,7 +332,7 @@
 json_print_value(struct jsonpr_ctx *ctx, const struct lyd_value *val)
 {
     ly_bool dynamic = 0;
-    const char *value = val->realtype->plugin->print(val, LY_VALUE_JSON, NULL, &dynamic);
+    const char *value = val->realtype->plugin->print(ctx->ctx, val, LY_VALUE_JSON, NULL, &dynamic, NULL);
 
     /* leafref is not supported */
     switch (val->realtype->basetype) {
diff --git a/src/printer_xml.c b/src/printer_xml.c
index af60e7a..aa4b35b 100644
--- a/src/printer_xml.c
+++ b/src/printer_xml.c
@@ -196,7 +196,8 @@
     }
 #endif
     for (meta = node->meta; meta; meta = meta->next) {
-        const char *value = meta->value.realtype->plugin->print(&meta->value, LY_VALUE_XML, &ns_list, &dynamic);
+        const char *value = meta->value.realtype->plugin->print(LYD_CTX(node), &meta->value, LY_VALUE_XML, &ns_list,
+                &dynamic, NULL);
 
         /* print namespaces connected with the value's prefixes */
         for (uint32_t u = 0; u < ns_list.count; ++u) {
@@ -323,7 +324,8 @@
     const char *value;
 
     xml_print_node_open(ctx, &node->node);
-    value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(&node->value, LY_VALUE_XML, &ns_list, &dynamic);
+    value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(LYD_CTX(node), &node->value, LY_VALUE_XML,
+            &ns_list, &dynamic, NULL);
 
     /* print namespaces connected with the values's prefixes */
     for (uint32_t u = 0; u < ns_list.count; ++u) {
diff --git a/src/printer_yang.c b/src/printer_yang.c
index 520e6de..29df721 100644
--- a/src/printer_yang.c
+++ b/src/printer_yang.c
@@ -824,12 +824,13 @@
 }
 
 static void
-yprc_dflt_value(struct lys_ypr_ctx *ctx, const struct lyd_value *value, struct lysc_ext_instance *exts)
+yprc_dflt_value(struct lys_ypr_ctx *ctx, const struct ly_ctx *ly_ctx, const struct lyd_value *value,
+        struct lysc_ext_instance *exts)
 {
     ly_bool dynamic;
     const char *str;
 
-    str = value->realtype->plugin->print(value, LY_VALUE_JSON, NULL, &dynamic);
+    str = value->realtype->plugin->print(ly_ctx, value, LY_VALUE_JSON, NULL, &dynamic, NULL);
     ypr_substmt(ctx, LY_STMT_DEFAULT, 0, str, exts);
     if (dynamic) {
         free((void *)str);
@@ -1487,7 +1488,7 @@
     }
 
     if (leaf->dflt) {
-        yprc_dflt_value(ctx, leaf->dflt, leaf->exts);
+        yprc_dflt_value(ctx, node->module->ctx, leaf->dflt, leaf->exts);
     }
 
     yprc_node_common2(ctx, node, NULL);
@@ -1552,7 +1553,7 @@
         yprc_must(ctx, &llist->musts[u], NULL);
     }
     LY_ARRAY_FOR(llist->dflts, u) {
-        yprc_dflt_value(ctx, llist->dflts[u], llist->exts);
+        yprc_dflt_value(ctx, node->module->ctx, llist->dflts[u], llist->exts);
     }
 
     ypr_config(ctx, node->flags, node->exts, NULL);
diff --git a/src/schema_compile_node.c b/src/schema_compile_node.c
index ed9d42c..1c33c60 100644
--- a/src/schema_compile_node.c
+++ b/src/schema_compile_node.c
@@ -2876,6 +2876,7 @@
                 /* inherit the module of the previous context node */
                 mod = ctx_node->module;
                 break;
+            case LY_VALUE_CANON:
             case LY_VALUE_XML:
                 /* not really defined */
                 LOGINT_RET(ctx->ctx);
diff --git a/src/tree.h b/src/tree.h
index f573504..eb0fd71 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -233,6 +233,7 @@
  * @brief All kinds of supported value formats and prefix mappings to modules.
  */
 typedef enum {
+    LY_VALUE_CANON,           /**< canonical value, prefix mapping is type-specific */
     LY_VALUE_SCHEMA,          /**< YANG schema value, prefixes map to YANG import prefixes */
     LY_VALUE_SCHEMA_RESOLVED, /**< resolved YANG schema value, prefixes map to module structures directly */
     LY_VALUE_XML,             /**< XML data value, prefixes map to XML namespace prefixes */
diff --git a/src/tree_data.c b/src/tree_data.c
index f7404ef..2e33fe1 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -663,7 +663,7 @@
     struct lysc_type *type;
 
     assert(schema->nodetype & LYD_NODE_TERM);
-    assert(val && val->canonical && val->realtype);
+    assert(val && val->realtype);
 
     term = calloc(1, sizeof *term);
     LY_CHECK_ERR_RET(!term, LOGMEM(schema->module->ctx), LY_EMEM);
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index 73a5750..76539a5 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -420,6 +420,7 @@
         }
         LY_ARRAY_FREE(prefixes);
         break;
+    case LY_VALUE_CANON:
     case LY_VALUE_SCHEMA:
     case LY_VALUE_JSON:
         break;
@@ -477,6 +478,7 @@
             LY_CHECK_ERR_GOTO(!ns->uri, LOGMEM(ctx); ret = LY_EMEM, cleanup);
         }
         break;
+    case LY_VALUE_CANON:
     case LY_VALUE_JSON:
         assert(!prefix_data);
         *prefix_data_p = NULL;
@@ -579,6 +581,7 @@
             }
         }
         break;
+    case LY_VALUE_CANON:
     case LY_VALUE_SCHEMA_RESOLVED:
     case LY_VALUE_JSON:
         if (!*prefix_data_p) {
@@ -601,6 +604,8 @@
 ly_format2str(LY_VALUE_FORMAT format)
 {
     switch (format) {
+    case LY_VALUE_CANON:
+        return "canonical";
     case LY_VALUE_SCHEMA:
         return "schema imports";
     case LY_VALUE_SCHEMA_RESOLVED:
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index ed05847..0956dc2 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -782,6 +782,7 @@
  * @param[in] mod Module whose prefix to get.
  * @param[in] format Format of the prefix.
  * @param[in] prefix_data Format-specific data based on @p format:
+ *      LY_VALUE_CANON           - NULL
  *      LY_VALUE_SCHEMA          - const struct lysp_module * (module used for resolving imports to prefixes)
  *      LY_VALUE_SCHEMA_RESOLVED - struct lyd_value_prefix * (sized array of pairs: prefix - module)
  *      LY_VALUE_XML             - struct ly_set * (set of all returned modules as ::struct lys_module)
diff --git a/src/validation.c b/src/validation.c
index 78feeb5..2177dc6 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -1105,7 +1105,7 @@
                     }
 
                     /* get canonical string value */
-                    str = val->realtype->plugin->print(val, LY_VALUE_JSON, NULL, &dynamic);
+                    str = val->realtype->plugin->print(ctx, val, LY_VALUE_JSON, NULL, &dynamic, NULL);
                     hash = dict_hash_multi(hash, str, strlen(str));
                     if (dynamic) {
                         free((char *)str);
diff --git a/src/xpath.c b/src/xpath.c
index 450968b..91f3d51 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -5319,6 +5319,7 @@
             /* current module */
             mod = set->cur_mod;
             break;
+        case LY_VALUE_CANON:
         case LY_VALUE_JSON:
             /* inherit parent (context node) module */
             if (ctx_scnode) {
@@ -5394,6 +5395,7 @@
                 moveto_mod = ctx_node->schema->module;
             }
             break;
+        case LY_VALUE_CANON:
         case LY_VALUE_XML:
             /* not defined */
             LOGINT(set->ctx);
@@ -5455,6 +5457,7 @@
                 moveto_mod = ctx_scnode->module;
             }
             break;
+        case LY_VALUE_CANON:
         case LY_VALUE_XML:
             /* not defined */
             LOGINT(set->ctx);
diff --git a/tests/utests/data/test_types.c b/tests/utests/data/test_types.c
index a06445b..9671a57 100644
--- a/tests/utests/data/test_types.c
+++ b/tests/utests/data/test_types.c
@@ -129,7 +129,7 @@
     { \
         const char *_str; \
         uint8_t _dynamic; \
-        assert_non_null(_str = (VALUE)->realtype->plugin->print(VALUE, FORMAT, (void *)PREFIX_DATA, &_dynamic)); \
+        assert_non_null(_str = (VALUE)->realtype->plugin->print(UTEST_LYCTX, VALUE, FORMAT, (void *)PREFIX_DATA, &_dynamic, NULL)); \
         assert_string_equal(EXPECTED, _str); \
         if (_dynamic) { \
             free((char *)_str); \
diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c
index 3a95fa4..b6932b6 100644
--- a/tests/utests/schema/test_tree_schema_compile.c
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -278,16 +278,16 @@
     assert_non_null(ll->dflts);
     assert_int_equal(6, ll->type->refcount); /* 3x type's reference, 3x default value's reference (typedef's default does not reference own type) */
     assert_int_equal(2, LY_ARRAY_COUNT(ll->dflts));
-    assert_string_equal("1", dflt = ll->dflts[0]->realtype->plugin->print(ll->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("1", dflt = ll->dflts[0]->realtype->plugin->print(UTEST_LYCTX, ll->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
-    assert_string_equal("1", dflt = ll->dflts[1]->realtype->plugin->print(ll->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("1", dflt = ll->dflts[1]->realtype->plugin->print(UTEST_LYCTX, ll->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_DFLT | LYS_SET_CONFIG, ll->flags);
     assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data->next));
     assert_non_null(ll->dflts);
     assert_int_equal(6, ll->type->refcount); /* 3x type's reference, 3x default value's reference */
     assert_int_equal(1, LY_ARRAY_COUNT(ll->dflts));
-    assert_string_equal("10", dflt = ll->dflts[0]->realtype->plugin->print(ll->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("10", dflt = ll->dflts[0]->realtype->plugin->print(UTEST_LYCTX, ll->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, ll->flags);
 
@@ -1948,7 +1948,7 @@
     assert_int_equal(3, type->refcount);     /* 2x type reference, 1x default value's reference (typedf's default does not reference own type)*/
     assert_int_equal(LY_TYPE_STRING, type->basetype);
     assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data);
-    assert_string_equal("hello", leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_string_equal("xxx", leaf->units);
 
@@ -1959,7 +1959,7 @@
     assert_int_equal(3, type->refcount);     /* 2x type reference, 1x default value's reference */
     assert_int_equal(LY_TYPE_STRING, type->basetype);
     leaf = (struct lysc_node_leaf *)mod->compiled->data;
-    assert_string_equal("goodbye", leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("goodbye", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_string_equal("yyy", leaf->units);
 
@@ -1971,7 +1971,7 @@
     assert_int_equal(6, type->refcount);     /* 4x type reference, 2x default value's reference (1 shared compiled type of typedefs which default does not reference own type) */
     assert_int_equal(LY_TYPE_STRING, type->basetype);
     leaf = (struct lysc_node_leaf *)mod->compiled->data;
-    assert_string_equal("goodbye", leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("goodbye", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_string_equal("yyy", leaf->units);
     type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type;
@@ -1979,7 +1979,7 @@
     assert_int_equal(6, type->refcount);     /* 4x type reference, 2x default value's reference (1 shared compiled type of typedefs which default does not reference own type) */
     assert_int_equal(LY_TYPE_STRING, type->basetype);
     leaf = (struct lysc_node_leaf *)mod->compiled->data->next;
-    assert_string_equal("hello", leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_string_equal("xxx", leaf->units);
 
@@ -1990,7 +1990,7 @@
     assert_int_equal(4, type->refcount);     /* 3x type reference, 1x default value's reference (typedef's default does not reference own type) */
     assert_int_equal(LY_TYPE_STRING, type->basetype);
     leaf = (struct lysc_node_leaf *)mod->compiled->data;
-    assert_string_equal("hello", leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_string_equal("xxx", leaf->units);
 
@@ -2265,16 +2265,16 @@
     assert_non_null((leaf = (struct lysc_node_leaf *)((struct lysc_node_container *)parent)->child));
     assert_int_equal(LYS_LEAF, leaf->nodetype);
     assert_string_equal("l", leaf->name);
-    assert_string_equal("hello", leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_int_equal(LYS_CONFIG_R, leaf->flags & LYS_CONFIG_MASK);
     assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
     assert_int_equal(LYS_LEAFLIST, llist->nodetype);
     assert_string_equal("ll", llist->name);
     assert_int_equal(2, LY_ARRAY_COUNT(llist->dflts));
-    assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
-    assert_string_equal("world", llist->dflts[1]->realtype->plugin->print(llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("world", llist->dflts[1]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_int_equal(5, llist->max);
     assert_non_null(child = llist->next);
@@ -2287,7 +2287,7 @@
     assert_int_equal(LYS_LEAF, leaf->nodetype);
     assert_string_equal("x", leaf->name);
     assert_false(LYS_MAND_TRUE & leaf->flags);
-    assert_string_equal("cheers!", leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("cheers!", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_non_null(leaf->musts);
     assert_int_equal(2, LY_ARRAY_COUNT(leaf->musts));
@@ -2314,7 +2314,7 @@
     assert_int_equal(LYS_LEAF, leaf->nodetype);
     assert_string_equal("x", leaf->name);
     assert_false(LYS_MAND_TRUE & leaf->flags);
-    assert_string_equal("hello", leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
 
     /* invalid */
@@ -2647,15 +2647,15 @@
     assert_null(leaf->dflt);
     assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
     assert_int_equal(1, LY_ARRAY_COUNT(llist->dflts));
-    assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_non_null(leaf = (struct lysc_node_leaf *)llist->next);
-    assert_string_equal("nothing", leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("nothing", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_int_equal(5, leaf->dflt->realtype->refcount);     /* 3x type reference, 2x default value reference (typedef's default does not reference own type) */
     assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
     assert_int_equal(1, LY_ARRAY_COUNT(llist->dflts));
-    assert_string_equal("nothing", llist->dflts[0]->realtype->plugin->print(llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("nothing", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
 
     assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {yang-version 1.1; namespace urn:g;prefix g;import e {prefix x;}"
@@ -2672,19 +2672,19 @@
     assert_string_equal("ba", ((struct lysc_node_choice *)node)->dflt->name);
     assert_non_null(leaf = (struct lysc_node_leaf *)node->next);
     assert_non_null(leaf->dflt);
-    assert_string_equal("bye", leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("bye", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
     assert_int_equal(3, LY_ARRAY_COUNT(llist->dflts));
-    assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
-    assert_string_equal("all", llist->dflts[1]->realtype->plugin->print(llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("all", llist->dflts[1]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
-    assert_string_equal("people", llist->dflts[2]->realtype->plugin->print(llist->dflts[2], LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("people", llist->dflts[2]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[2], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_non_null(leaf = (struct lysc_node_leaf *)llist->next);
     assert_non_null(leaf->dflt);
-    assert_string_equal("hi", leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("hi", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_int_equal(6, leaf->dflt->realtype->refcount);     /* 3x type reference, 3x default value reference
     - previous type's default values were replaced by node's default values where d2 now has 2 default values */
@@ -2692,9 +2692,9 @@
     assert_int_equal(0, LY_ARRAY_COUNT(leaf->musts[0].prefixes));
     assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
     assert_int_equal(2, LY_ARRAY_COUNT(llist->dflts));
-    assert_string_equal("hi", llist->dflts[0]->realtype->plugin->print(llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("hi", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
-    assert_string_equal("all", llist->dflts[1]->realtype->plugin->print(llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("all", llist->dflts[1]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
 
     assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {yang-version 1.1; namespace urn:h;prefix h;import e {prefix x;}"
@@ -2708,7 +2708,7 @@
     assert_string_equal("ba", ((struct lysc_node_choice *)node)->dflt->name);
     assert_non_null(leaf = (struct lysc_node_leaf *)node->next);
     assert_non_null(leaf->dflt);
-    assert_string_equal("hello", leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
 
     ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module i {namespace urn:i;prefix i;"
@@ -2825,14 +2825,14 @@
     assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data);
     assert_string_equal("a", leaf->name);
     assert_int_equal(LY_TYPE_INT8, leaf->type->basetype);
-    assert_string_equal("10", leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("10", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_int_equal(10, leaf->dflt->uint8);
     assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next);
     assert_string_equal("b", llist->name);
     assert_int_equal(LY_TYPE_INT8, llist->type->basetype);
     assert_int_equal(1, LY_ARRAY_COUNT(llist->dflts));
-    assert_string_equal("1", llist->dflts[0]->realtype->plugin->print(llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic));
+    assert_string_equal("1", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL));
     assert_int_equal(0, dynamic);
     assert_int_equal(1, llist->dflts[0]->uint8);
 
@@ -2873,7 +2873,7 @@
     assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data);
     assert_string_equal("s", leaf->name);
     assert_non_null(leaf->dflt);
-    assert_non_null(str = leaf->dflt->realtype->plugin->print(leaf->dflt, LY_VALUE_SCHEMA, mod->parsed, &dynamic));
+    assert_non_null(str = leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, mod->parsed, &dynamic, NULL));
     assert_string_equal("/s:y", str);
     if (dynamic) {
         free((char *)str);
diff --git a/tests/utests/types/bits.c b/tests/utests/types/bits.c
index 43eeac3..437c9ab 100644
--- a/tests/utests/types/bits.c
+++ b/tests/utests/types/bits.c
@@ -960,12 +960,12 @@
     /* print value */
     ly_bool dynamic = 0;
 
-    assert_string_equal("", type->print(&(values[0]), LY_VALUE_XML, NULL, &dynamic));
-    assert_string_equal("zero two", type->print(&(values[1]), LY_VALUE_XML, NULL, &dynamic));
-    assert_string_equal("three", type->print(&(values[2]), LY_VALUE_XML, NULL, &dynamic));
-    assert_string_equal("zero two", type->print(&(values[3]), LY_VALUE_XML, NULL, &dynamic));
-    assert_string_equal("zero", type->print(&(values[4]), LY_VALUE_XML, NULL, &dynamic));
-    assert_string_equal("three", type->print(&(values[5]), LY_VALUE_XML, NULL, &dynamic));
+    assert_string_equal("", type->print(UTEST_LYCTX, &(values[0]), LY_VALUE_XML, NULL, &dynamic, NULL));
+    assert_string_equal("zero two", type->print(UTEST_LYCTX, &(values[1]), LY_VALUE_XML, NULL, &dynamic, NULL));
+    assert_string_equal("three", type->print(UTEST_LYCTX, &(values[2]), LY_VALUE_XML, NULL, &dynamic, NULL));
+    assert_string_equal("zero two", type->print(UTEST_LYCTX, &(values[3]), LY_VALUE_XML, NULL, &dynamic, NULL));
+    assert_string_equal("zero", type->print(UTEST_LYCTX, &(values[4]), LY_VALUE_XML, NULL, &dynamic, NULL));
+    assert_string_equal("three", type->print(UTEST_LYCTX, &(values[5]), LY_VALUE_XML, NULL, &dynamic, NULL));
 
     for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
         type->free(UTEST_LYCTX, &(values[it]));
diff --git a/tests/utests/types/int8.c b/tests/utests/types/int8.c
index 05f2009..2b66618 100644
--- a/tests/utests/types/int8.c
+++ b/tests/utests/types/int8.c
@@ -1621,12 +1621,12 @@
     /* print value */
     ly_bool dynamic = 0;
 
-    assert_string_equal("32", type->print(&(values[0]), LY_VALUE_XML, NULL, &dynamic));
-    assert_string_equal("74", type->print(&(values[1]), LY_VALUE_XML, NULL, &dynamic));
-    assert_string_equal("-15", type->print(&(values[2]), LY_VALUE_XML, NULL, &dynamic));
-    assert_string_equal("0", type->print(&(values[3]), LY_VALUE_XML, NULL, &dynamic));
-    assert_string_equal("0", type->print(&(values[4]), LY_VALUE_XML, NULL, &dynamic));
-    assert_string_equal("-32", type->print(&(values[5]), LY_VALUE_XML, NULL, &dynamic));
+    assert_string_equal("32", type->print(UTEST_LYCTX, &(values[0]), LY_VALUE_XML, NULL, &dynamic, NULL));
+    assert_string_equal("74", type->print(UTEST_LYCTX, &(values[1]), LY_VALUE_XML, NULL, &dynamic, NULL));
+    assert_string_equal("-15", type->print(UTEST_LYCTX, &(values[2]), LY_VALUE_XML, NULL, &dynamic, NULL));
+    assert_string_equal("0", type->print(UTEST_LYCTX, &(values[3]), LY_VALUE_XML, NULL, &dynamic, NULL));
+    assert_string_equal("0", type->print(UTEST_LYCTX, &(values[4]), LY_VALUE_XML, NULL, &dynamic, NULL));
+    assert_string_equal("-32", type->print(UTEST_LYCTX, &(values[5]), LY_VALUE_XML, NULL, &dynamic, NULL));
 
     for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
         type->free(UTEST_LYCTX, &(values[it]));
diff --git a/tests/utests/types/string.c b/tests/utests/types/string.c
index 5ddb872..12f7194 100644
--- a/tests/utests/types/string.c
+++ b/tests/utests/types/string.c
@@ -1249,10 +1249,10 @@
     /* print value */
     ly_bool dynamic = 0;
 
-    assert_string_equal("20", type->print(&(values[0]), LY_VALUE_XML, NULL, &dynamic));
-    assert_string_equal("0x4A", type->print(&(values[1]), LY_VALUE_XML, NULL, &dynamic));
-    assert_string_equal("<|>", type->print(&(values[2]), LY_VALUE_XML, NULL, &dynamic));
-    assert_string_equal("\"", type->print(&(values[3]), LY_VALUE_XML, NULL, &dynamic));
+    assert_string_equal("20", type->print(UTEST_LYCTX, &(values[0]), LY_VALUE_XML, NULL, &dynamic, NULL));
+    assert_string_equal("0x4A", type->print(UTEST_LYCTX, &(values[1]), LY_VALUE_XML, NULL, &dynamic, NULL));
+    assert_string_equal("<|>", type->print(UTEST_LYCTX, &(values[2]), LY_VALUE_XML, NULL, &dynamic, NULL));
+    assert_string_equal("\"", type->print(UTEST_LYCTX, &(values[3]), LY_VALUE_XML, NULL, &dynamic, NULL));
 
     for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
         type->free(UTEST_LYCTX, &(values[it]));