libyang CHANGE reflect recent changes in data manipulation functions

Reflect extended support of canonical and LYB values by type plugins
the functions manipulating with the values of data nodes.
diff --git a/src/in.c b/src/in.c
index 1dda5d9..7d9d046 100644
--- a/src/in.c
+++ b/src/in.c
@@ -494,7 +494,7 @@
 }
 
 LY_ERR
-lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const char *value, size_t value_len,
+lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const void *value, size_t value_len,
         ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, struct lyd_node **node)
 {
     ly_bool incomplete;
diff --git a/src/parser_internal.h b/src/parser_internal.h
index 6d47489..9b8ca1e 100644
--- a/src/parser_internal.h
+++ b/src/parser_internal.h
@@ -207,7 +207,7 @@
  * @param[in] lydctx Data parser context.
  * @param[in] hints Data parser's hint for the value's type.
  */
-LY_ERR lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const char *value, size_t value_len,
+LY_ERR lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const void *value, size_t value_len,
         ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, struct lyd_node **node);
 
 /**
diff --git a/src/path.c b/src/path.c
index a4db50c..6a413a3 100644
--- a/src/path.c
+++ b/src/path.c
@@ -380,8 +380,8 @@
  *
  * @param[in] ctx libyang context.
  * @param[in] cur_node Optional current (original context) node.
- * @param[in] cur_mod Current module of the path (where the path is "instantiated"). Needed for ::LY_PREF_SCHEMA*.
- * @param[in] prev_ctx_node Previous context node. Needed for ::LY_PREF_JSON.
+ * @param[in] cur_mod Current module of the path (where the path is "instantiated"). Needed for ::LY_VALUE_SCHEMA*.
+ * @param[in] prev_ctx_node Previous context node. Needed for ::LY_VALUE_JSON.
  * @param[in] expr Parsed path.
  * @param[in] tok_idx Index in @p expr.
  * @param[in] lref Lref option.
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 22ffa5c..d176dc2 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -693,8 +693,8 @@
 
 API LY_ERR
 lyplg_type_lypath_new(const struct ly_ctx *ctx, const char *value, size_t value_len, uint32_t options,
-        LY_VALUE_FORMAT format, void *prefix_data, const struct lysc_node *ctx_node,
-        struct lys_glob_unres *unres, struct ly_path **path, struct ly_err_item **err)
+        LY_VALUE_FORMAT format, void *prefix_data, const struct lysc_node *ctx_node, struct lys_glob_unres *unres,
+        struct ly_path **path, struct ly_err_item **err)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyxp_expr *exp = NULL;
diff --git a/src/tree_data.c b/src/tree_data.c
index 919920f..bbe79ea 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -160,14 +160,18 @@
     struct ly_err_item *err = NULL;
     struct lysc_type *type;
     struct lyd_value val = {0};
-    ly_bool stored = 0;
+    ly_bool stored = 0, log = 1;
 
     LY_CHECK_ARG_RET(ctx, schema, value, LY_EINVAL);
 
+    if (!ctx) {
+        ctx = schema->module->ctx;
+        log = 0;
+    }
     type = ((struct lysc_node_leaf *)schema)->type;
 
     /* store */
-    rc = type->plugin->store(ctx ? ctx : schema->module->ctx, type, value, value_len, 0, LY_VALUE_JSON, NULL,
+    rc = type->plugin->store(ctx, type, value, value_len, 0, LY_VALUE_JSON, NULL,
             LYD_HINT_DATA, schema, &val, NULL, &err);
     if (!rc || (rc == LY_EINCOMPLETE)) {
         stored = 1;
@@ -175,11 +179,11 @@
 
     if (ctx_node && (rc == LY_EINCOMPLETE)) {
         /* resolve */
-        rc = type->plugin->validate(ctx ? ctx : schema->module->ctx, type, ctx_node, ctx_node, &val, &err);
+        rc = type->plugin->validate(ctx, type, ctx_node, ctx_node, &val, &err);
     }
 
     if (rc && (rc != LY_EINCOMPLETE) && err) {
-        if (ctx) {
+        if (log) {
             /* log error */
             if (err->path) {
                 LOG_LOCSET(NULL, NULL, err->path, NULL);
@@ -1046,14 +1050,28 @@
     return LY_SUCCESS;
 }
 
-API LY_ERR
-lyd_new_term(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *val_str,
-        ly_bool output, struct lyd_node **node)
+/**
+ * @brief Create a new term node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node can be ::LYS_LEAF or ::LYS_LEAFLIST.
+ * @param[in] value Value of the node being created.
+ * @param[in] value_len Length of @p value.
+ * @param[in] format Format of @p value.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+_lyd_new_term(struct lyd_node *parent, const struct lys_module *module, const char *name, const void *value,
+        size_t value_len, LY_VALUE_FORMAT format, ly_bool output, struct lyd_node **node)
 {
     LY_ERR rc;
     struct lyd_node *ret = NULL;
     const struct lysc_node *schema;
-    struct ly_ctx *ctx = parent ? parent->schema->module->ctx : (module ? module->ctx : NULL);
+    const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
 
     LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
 
@@ -1064,7 +1082,7 @@
     schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYD_NODE_TERM, output ? LYS_GETNEXT_OUTPUT : 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, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, &ret);
+    rc = lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA, NULL, &ret);
     LY_CHECK_RET(rc);
     if (parent) {
         lyd_insert_node(parent, NULL, ret);
@@ -1077,6 +1095,27 @@
 }
 
 API LY_ERR
+lyd_new_term(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *val_str,
+        ly_bool output, struct lyd_node **node)
+{
+    return _lyd_new_term(parent, module, name, val_str, val_str ? strlen(val_str) : 0, LY_VALUE_JSON, output, node);
+}
+
+API LY_ERR
+lyd_new_term_bin(struct lyd_node *parent, const struct lys_module *module, const char *name, const void *value,
+        size_t value_len, ly_bool output, struct lyd_node **node)
+{
+    return _lyd_new_term(parent, module, name, value, value_len, LY_VALUE_LYB, output, node);
+}
+
+API LY_ERR
+lyd_new_term_canon(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *val_str,
+        ly_bool output, struct lyd_node **node)
+{
+    return _lyd_new_term(parent, module, name, val_str, val_str ? strlen(val_str) : 0, LY_VALUE_CANON, output, node);
+}
+
+API LY_ERR
 lyd_new_ext_term(const struct lysc_ext_instance *ext, const char *name, const char *val_str, struct lyd_node **node)
 {
     LY_ERR rc;
@@ -1159,86 +1198,6 @@
     return LY_SUCCESS;
 }
 
-/**
- * @brief Update node value.
- *
- * @param[in] node Node to update.
- * @param[in] value New value to set.
- * @param[in] value_type Type of @p value for any node.
- * @param[out] new_parent Set to @p node if the value was updated, otherwise set to NULL.
- * @param[out] new_node Set to @p node if the value was updated, otherwise set to NULL.
- * @return LY_ERR value.
- */
-static LY_ERR
-lyd_new_path_update(struct lyd_node *node, const void *value, LYD_ANYDATA_VALUETYPE value_type,
-        struct lyd_node **new_parent, struct lyd_node **new_node)
-{
-    LY_ERR ret = LY_SUCCESS;
-    struct lyd_node *new_any;
-
-    switch (node->schema->nodetype) {
-    case LYS_CONTAINER:
-    case LYS_NOTIF:
-    case LYS_RPC:
-    case LYS_ACTION:
-    case LYS_LIST:
-        /* if it exists, there is nothing to update */
-        *new_parent = NULL;
-        *new_node = NULL;
-        break;
-    case LYS_LEAFLIST:
-        if (!lysc_is_dup_inst_list(node->schema)) {
-            /* if it exists, there is nothing to update */
-            *new_parent = NULL;
-            *new_node = NULL;
-            break;
-        }
-    /* fallthrough */
-    case LYS_LEAF:
-        ret = lyd_change_term(node, value);
-        if ((ret == LY_SUCCESS) || (ret == LY_EEXIST)) {
-            /* there was an actual change (at least of the default flag) */
-            *new_parent = node;
-            *new_node = node;
-            ret = LY_SUCCESS;
-        } else if (ret == LY_ENOT) {
-            /* no change */
-            *new_parent = NULL;
-            *new_node = NULL;
-            ret = LY_SUCCESS;
-        } /* else error */
-        break;
-    case LYS_ANYDATA:
-    case LYS_ANYXML:
-        /* create a new any node */
-        LY_CHECK_RET(lyd_create_any(node->schema, value, value_type, 0, &new_any));
-
-        /* compare with the existing one */
-        if (lyd_compare_single(node, new_any, 0)) {
-            /* not equal, switch values (so that we can use generic node free) */
-            ((struct lyd_node_any *)new_any)->value = ((struct lyd_node_any *)node)->value;
-            ((struct lyd_node_any *)new_any)->value_type = ((struct lyd_node_any *)node)->value_type;
-            ((struct lyd_node_any *)node)->value.str = value;
-            ((struct lyd_node_any *)node)->value_type = value_type;
-
-            *new_parent = node;
-            *new_node = node;
-        } else {
-            /* they are equal */
-            *new_parent = NULL;
-            *new_node = NULL;
-        }
-        lyd_free_tree(new_any);
-        break;
-    default:
-        LOGINT(LYD_CTX(node));
-        ret = LY_EINT;
-        break;
-    }
-
-    return ret;
-}
-
 API LY_ERR
 lyd_new_meta(const struct ly_ctx *ctx, struct lyd_node *parent, const struct lys_module *module, const char *name,
         const char *val_str, ly_bool clear_dflt, struct lyd_meta **meta)
@@ -1450,8 +1409,23 @@
     return LY_SUCCESS;
 }
 
-API LY_ERR
-lyd_change_term(struct lyd_node *term, const char *val_str)
+/**
+ * @brief Change the value of a term (leaf or leaf-list) node.
+ *
+ * Node changed this way is always considered explicitly set, meaning its default flag
+ * is always cleared.
+ *
+ * @param[in] term Term node to change.
+ * @param[in] value New value to set.
+ * @param[in] value_len Length of @p value.
+ * @param[in] format Format of @p value.
+ * @return LY_SUCCESS if value was changed,
+ * @return LY_EEXIST if value was the same and only the default flag was cleared,
+ * @return LY_ENOT if the values were equal and no change occured,
+ * @return LY_ERR value on other errors.
+ */
+static LY_ERR
+_lyd_change_term(struct lyd_node *term, const void *value, size_t value_len, LY_VALUE_FORMAT format)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lysc_type *type;
@@ -1460,18 +1434,14 @@
     struct lyd_value val = {0};
     ly_bool dflt_change, val_change;
 
-    LY_CHECK_ARG_RET(NULL, term, term->schema, term->schema->nodetype & LYD_NODE_TERM, LY_EINVAL);
+    assert(term && term->schema && (term->schema->nodetype & LYD_NODE_TERM));
 
-    if (!val_str) {
-        val_str = "";
-    }
     t = (struct lyd_node_term *)term;
     type = ((struct lysc_node_leaf *)term->schema)->type;
 
     /* parse the new value */
     LOG_LOCSET(term->schema, term, NULL, NULL);
-    ret = lyd_value_store(LYD_CTX(term), &val, type, val_str, strlen(val_str), NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA,
-            term->schema, NULL);
+    ret = lyd_value_store(LYD_CTX(term), &val, type, value, value_len, NULL, format, NULL, LYD_HINT_DATA, term->schema, NULL);
     LOG_LOCBACK(term->schema ? 1 : 0, 1, 0, 0);
     LY_CHECK_GOTO(ret, cleanup);
 
@@ -1535,6 +1505,30 @@
 }
 
 API LY_ERR
+lyd_change_term(struct lyd_node *term, const char *val_str)
+{
+    LY_CHECK_ARG_RET(NULL, term, term->schema, term->schema->nodetype & LYD_NODE_TERM, LY_EINVAL);
+
+    return _lyd_change_term(term, val_str, val_str ? strlen(val_str) : 0, LY_VALUE_JSON);
+}
+
+API LY_ERR
+lyd_change_term_bin(struct lyd_node *term, const void *value, size_t value_len)
+{
+    LY_CHECK_ARG_RET(NULL, term, term->schema, term->schema->nodetype & LYD_NODE_TERM, LY_EINVAL);
+
+    return _lyd_change_term(term, value, value_len, LY_VALUE_LYB);
+}
+
+API LY_ERR
+lyd_change_term_canon(struct lyd_node *term, const char *val_str)
+{
+    LY_CHECK_ARG_RET(NULL, term, term->schema, term->schema->nodetype & LYD_NODE_TERM, LY_EINVAL);
+
+    return _lyd_change_term(term, val_str, val_str ? strlen(val_str) : 0, LY_VALUE_CANON);
+}
+
+API LY_ERR
 lyd_change_meta(struct lyd_meta *meta, const char *val_str)
 {
     LY_ERR ret = LY_SUCCESS;
@@ -1574,8 +1568,91 @@
     return ret;
 }
 
+/**
+ * @brief Update node value.
+ *
+ * @param[in] node Node to update.
+ * @param[in] value New value to set.
+ * @param[in] value_len Length of @p value.
+ * @param[in] value_type Type of @p value for anydata/anyxml node.
+ * @param[in] format Format of @p value.
+ * @param[out] new_parent Set to @p node if the value was updated, otherwise set to NULL.
+ * @param[out] new_node Set to @p node if the value was updated, otherwise set to NULL.
+ * @return LY_ERR value.
+ */
 static LY_ERR
-lyd_new_path_check_find_lypath(struct ly_path *path, const char *str_path, const char *value, uint32_t options)
+lyd_new_path_update(struct lyd_node *node, const void *value, size_t value_len, LYD_ANYDATA_VALUETYPE value_type,
+        LY_VALUE_FORMAT format, struct lyd_node **new_parent, struct lyd_node **new_node)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_node *new_any;
+
+    switch (node->schema->nodetype) {
+    case LYS_CONTAINER:
+    case LYS_NOTIF:
+    case LYS_RPC:
+    case LYS_ACTION:
+    case LYS_LIST:
+        /* if it exists, there is nothing to update */
+        *new_parent = NULL;
+        *new_node = NULL;
+        break;
+    case LYS_LEAFLIST:
+        if (!lysc_is_dup_inst_list(node->schema)) {
+            /* if it exists, there is nothing to update */
+            *new_parent = NULL;
+            *new_node = NULL;
+            break;
+        }
+    /* fallthrough */
+    case LYS_LEAF:
+        ret = _lyd_change_term(node, value, value_len, format);
+        if ((ret == LY_SUCCESS) || (ret == LY_EEXIST)) {
+            /* there was an actual change (at least of the default flag) */
+            *new_parent = node;
+            *new_node = node;
+            ret = LY_SUCCESS;
+        } else if (ret == LY_ENOT) {
+            /* no change */
+            *new_parent = NULL;
+            *new_node = NULL;
+            ret = LY_SUCCESS;
+        } /* else error */
+        break;
+    case LYS_ANYDATA:
+    case LYS_ANYXML:
+        /* create a new any node */
+        LY_CHECK_RET(lyd_create_any(node->schema, value, value_type, 0, &new_any));
+
+        /* compare with the existing one */
+        if (lyd_compare_single(node, new_any, 0)) {
+            /* not equal, switch values (so that we can use generic node free) */
+            ((struct lyd_node_any *)new_any)->value = ((struct lyd_node_any *)node)->value;
+            ((struct lyd_node_any *)new_any)->value_type = ((struct lyd_node_any *)node)->value_type;
+            ((struct lyd_node_any *)node)->value.str = value;
+            ((struct lyd_node_any *)node)->value_type = value_type;
+
+            *new_parent = node;
+            *new_node = node;
+        } else {
+            /* they are equal */
+            *new_parent = NULL;
+            *new_node = NULL;
+        }
+        lyd_free_tree(new_any);
+        break;
+    default:
+        LOGINT(LYD_CTX(node));
+        ret = LY_EINT;
+        break;
+    }
+
+    return ret;
+}
+
+static LY_ERR
+lyd_new_path_check_find_lypath(struct ly_path *path, const char *str_path, const char *value, size_t value_len,
+        LY_VALUE_FORMAT format, uint32_t options)
 {
     LY_ERR r;
     struct ly_path_predicate *pred;
@@ -1610,13 +1687,9 @@
                 return LY_EINVAL;
             } /* else creating an opaque list */
         } else if ((schema->nodetype == LYS_LEAFLIST) && (path[u].pred_type != LY_PATH_PREDTYPE_LEAFLIST)) {
-            if (!value) {
-                value = "";
-            }
-
             r = LY_SUCCESS;
             if (options & LYD_NEW_PATH_OPAQ) {
-                r = lyd_value_validate(NULL, schema, value, strlen(value), NULL, NULL, NULL);
+                r = lyd_value_validate(NULL, schema, value, value_len, NULL, NULL, NULL);
             }
             if (!r) {
                 /* store the new predicate so that it is used when searching for this instance */
@@ -1624,7 +1697,7 @@
                 LY_ARRAY_NEW_RET(schema->module->ctx, path[u].predicates, pred, LY_EMEM);
 
                 LY_CHECK_RET(lyd_value_store(schema->module->ctx, &pred->value,
-                        ((struct lysc_node_leaflist *)schema)->type, value, strlen(value), NULL, LY_VALUE_JSON, NULL,
+                        ((struct lysc_node_leaflist *)schema)->type, value, value_len, NULL, format, NULL,
                         LYD_HINT_DATA, schema, NULL));
                 ++((struct lysc_type *)pred->value.realtype)->refcount;
             } /* else we have opaq flag and the value is not valid, leave no predicate and then create an opaque node */
@@ -1659,8 +1732,10 @@
  * path or when the relative paths touches document root (top-level). In such cases the present extension instance replaces
  * searching for the appropriate module.
  * @param[in] path [Path](@ref howtoXPath) to create.
- * @param[in] value Value of the new leaf/leaf-list (const char *) or anyxml/anydata (expected type depends on @p value_type).
- * For other node types, it is ignored.
+ * @param[in] value Value of the new leaf/leaf-list (const char *) in ::LY_VALUE_JSON format. If creating an
+ * anyxml/anydata node, the expected type depends on @p value_type. For other node types, it should be NULL.
+ * @param[in] value_len Length of @p value in bytes. May be 0 if @p value is a zero-terminated string. Ignored when
+ * creating anyxml/anydata nodes.
  * @param[in] value_type Anyxml/anydata node @p value type.
  * @param[in] options Bitmask of options, see @ref pathoptions.
  * @param[out] new_parent Optional first parent node created. If only one node was created, equals to @p new_node.
@@ -1669,7 +1744,7 @@
  */
 static LY_ERR
 lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, const char *path,
-        const void *value, LYD_ANYDATA_VALUETYPE value_type, uint32_t options,
+        const void *value, size_t value_len, LYD_ANYDATA_VALUETYPE value_type, uint32_t options,
         struct lyd_node **new_parent, struct lyd_node **new_node)
 {
     LY_ERR ret = LY_SUCCESS, r;
@@ -1679,14 +1754,25 @@
     const struct lysc_node *schema;
     const struct lyd_value *val = NULL;
     LY_ARRAY_COUNT_TYPE path_idx = 0, orig_count = 0;
+    LY_VALUE_FORMAT format;
 
     assert(parent || ctx);
-    assert(path);
-    assert((path[0] == '/') || parent);
+    assert(path && ((path[0] == '/') || parent));
+    assert(!(options & LYD_NEW_PATH_BIN_VALUE) || !(options & LYD_NEW_PATH_CANON_VALUE));
 
     if (!ctx) {
         ctx = LYD_CTX(parent);
     }
+    if (value && !value_len) {
+        value_len = strlen(value);
+    }
+    if (options & LYD_NEW_PATH_BIN_VALUE) {
+        format = LY_VALUE_LYB;
+    } else if (options & LYD_NEW_PATH_CANON_VALUE) {
+        format = LY_VALUE_CANON;
+    } else {
+        format = LY_VALUE_JSON;
+    }
 
     /* parse path */
     LY_CHECK_GOTO(ret = ly_path_parse(ctx, NULL, path, strlen(path), LY_PATH_BEGIN_EITHER, LY_PATH_LREF_FALSE,
@@ -1699,7 +1785,7 @@
 
     /* check the compiled path before searching existing nodes, it may be shortened */
     orig_count = LY_ARRAY_COUNT(p);
-    LY_CHECK_GOTO(ret = lyd_new_path_check_find_lypath(p, path, value, options), cleanup);
+    LY_CHECK_GOTO(ret = lyd_new_path_check_find_lypath(p, path, value, value_len, format, options), cleanup);
 
     /* try to find any existing nodes in the path */
     if (parent) {
@@ -1716,7 +1802,7 @@
                 }
 
                 /* update the existing node */
-                ret = lyd_new_path_update(node, value, value_type, &nparent, &nnode);
+                ret = lyd_new_path_update(node, value, value_len, value_type, format, &nparent, &nnode);
                 goto cleanup;
             } /* else we were not searching for the whole path */
         } else if (ret == LY_EINCOMPLETE) {
@@ -1771,13 +1857,13 @@
                 r = LY_EVALID;
                 if (lysc_is_dup_inst_list(schema)) {
                     /* validate value */
-                    r = lyd_value_validate(NULL, schema, value ? value : "", value ? strlen(value) : 0, NULL, NULL, NULL);
+                    r = lyd_value_validate(NULL, schema, value ? value : "", value_len, NULL, NULL, NULL);
                 }
                 if (r && (r != LY_EINCOMPLETE)) {
                     /* creating opaque leaf-list */
-                    LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), value,
-                            value ? strlen(value) : 0, schema->module->name, strlen(schema->module->name), NULL, 0, NULL,
-                            LY_VALUE_JSON, NULL, LYD_NODEHINT_LEAFLIST, &node), cleanup);
+                    LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), value, value_len,
+                            schema->module->name, strlen(schema->module->name), NULL, 0, NULL, format, NULL,
+                            LYD_NODEHINT_LEAFLIST, &node), cleanup);
                     break;
                 }
             }
@@ -1785,16 +1871,14 @@
             /* get value to set */
             if (p[path_idx].pred_type == LY_PATH_PREDTYPE_LEAFLIST) {
                 val = &p[path_idx].predicates[0].value;
-            } else if (!value) {
-                value = "";
             }
 
             /* create a leaf-list instance */
             if (val) {
                 LY_CHECK_GOTO(ret = lyd_create_term2(schema, &p[path_idx].predicates[0].value, &node), cleanup);
             } else {
-                LY_CHECK_GOTO(ret = lyd_create_term(schema, value, strlen(value), NULL, LY_VALUE_JSON, NULL,
-                        LYD_HINT_DATA, NULL, &node), cleanup);
+                LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA,
+                        NULL, &node), cleanup);
             }
             break;
         case LYS_LEAF:
@@ -1808,24 +1892,19 @@
 
             if (options & LYD_NEW_PATH_OPAQ) {
                 /* validate value */
-                r = lyd_value_validate(NULL, schema, value ? value : "", value ? strlen(value) : 0, NULL, NULL, NULL);
+                r = lyd_value_validate(NULL, schema, value ? value : "", value_len, NULL, NULL, NULL);
                 if (r && (r != LY_EINCOMPLETE)) {
                     /* creating opaque leaf */
-                    LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), value,
-                            value ? strlen(value) : 0, schema->module->name, strlen(schema->module->name), NULL, 0, NULL,
-                            LY_VALUE_JSON, NULL, 0, &node), cleanup);
+                    LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), value, value_len,
+                            schema->module->name, strlen(schema->module->name), NULL, 0, NULL, format, NULL, 0, &node),
+                            cleanup);
                     break;
                 }
             }
 
-            /* get value to set */
-            if (!value) {
-                value = "";
-            }
-
             /* create a leaf instance */
-            LY_CHECK_GOTO(ret = lyd_create_term(schema, value, strlen(value), NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA,
-                    NULL, &node), cleanup);
+            LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA, NULL,
+                    &node), cleanup);
             break;
         case LYS_ANYDATA:
         case LYS_ANYXML:
@@ -1879,16 +1958,20 @@
 lyd_new_path(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const char *value, uint32_t options,
         struct lyd_node **node)
 {
-    LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent, LY_EINVAL);
-    return lyd_new_path_(parent, ctx, NULL, path, value, LYD_ANYDATA_STRING, options, node, NULL);
+    LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent,
+            !(options & LYD_NEW_PATH_BIN_VALUE) || !(options & LYD_NEW_PATH_CANON_VALUE), LY_EINVAL);
+
+    return lyd_new_path_(parent, ctx, NULL, path, value, 0, LYD_ANYDATA_STRING, options, node, NULL);
 }
 
 API LY_ERR
 lyd_new_path2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
-        LYD_ANYDATA_VALUETYPE value_type, uint32_t options, struct lyd_node **new_parent, struct lyd_node **new_node)
+        size_t value_len, LYD_ANYDATA_VALUETYPE value_type, uint32_t options, struct lyd_node **new_parent,
+        struct lyd_node **new_node)
 {
-    LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent, LY_EINVAL);
-    return lyd_new_path_(parent, ctx, NULL, path, value, value_type, options, new_parent, new_node);
+    LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent,
+            !(options & LYD_NEW_PATH_BIN_VALUE) || !(options & LYD_NEW_PATH_CANON_VALUE), LY_EINVAL);
+    return lyd_new_path_(parent, ctx, NULL, path, value, value_len, value_type, options, new_parent, new_node);
 }
 
 API LY_ERR
@@ -1897,8 +1980,9 @@
 {
     const struct ly_ctx *ctx = ext ? ext->module->ctx : NULL;
 
-    LY_CHECK_ARG_RET(ctx, ext, path, (path[0] == '/') || parent, LY_EINVAL);
-    return lyd_new_path_(parent, ctx, ext, path, value, LYD_ANYDATA_STRING, options, node, NULL);
+    LY_CHECK_ARG_RET(ctx, ext, path, (path[0] == '/') || parent,
+            !(options & LYD_NEW_PATH_BIN_VALUE) || !(options & LYD_NEW_PATH_CANON_VALUE), LY_EINVAL);
+    return lyd_new_path_(parent, ctx, ext, path, value, 0, LYD_ANYDATA_STRING, options, node, NULL);
 }
 
 LY_ERR
@@ -2947,7 +3031,8 @@
         }
         switch (opaq1->format) {
         case LY_VALUE_XML:
-            if (lyxml_value_compare(LYD_CTX(node1), opaq1->value, opaq1->val_prefix_data, LYD_CTX(node2), opaq2->value, opaq2->val_prefix_data)) {
+            if (lyxml_value_compare(LYD_CTX(node1), opaq1->value, opaq1->val_prefix_data, LYD_CTX(node2), opaq2->value,
+                    opaq2->val_prefix_data)) {
                 return LY_ENOT;
             }
             break;
diff --git a/src/tree_data.h b/src/tree_data.h
index 4992b02..a9e2f02 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -243,6 +243,8 @@
  * --------------
  * - ::lyd_new_inner()
  * - ::lyd_new_term()
+ * - ::lyd_new_term_bin()
+ * - ::lyd_new_term_canon()
  * - ::lyd_new_list()
  * - ::lyd_new_list2()
  * - ::lyd_new_any()
@@ -273,6 +275,8 @@
  * - ::lyd_value_validate()
  *
  * - ::lyd_change_term()
+ * - ::lyd_change_term_bin()
+ * - ::lyd_change_term_canon()
  * - ::lyd_change_meta()
  *
  * - ::lyd_compare_single()
@@ -365,6 +369,8 @@
  * in its new binary format specification according to its type. Following is the format for all types with explicit
  * support out-of-the-box (meaning that have a special type plugin). Any derived types inherit the format of its
  * closest type with explicit support (up to a built-in type).
+ *
+ * @subpage lybformat_ipv6_address
  */
 
 /**
@@ -1097,8 +1103,7 @@
  * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
  * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
  * @param[in] name Schema node name of the new data node. The node can be #LYS_LEAF or #LYS_LEAFLIST.
- * @param[in] val_str String form of the value of the node being created. In case of an instance-identifier or identityref
- * value, the JSON format is expected (module names instead of prefixes).
+ * @param[in] val_str String value of the node. If it varies based on the format, ::LY_VALUE_JSON is expected.
  * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
  * taken into consideration. Otherwise, the output's data node is going to be created.
  * @param[out] node Optional created node.
@@ -1108,6 +1113,37 @@
         ly_bool output, struct lyd_node **node);
 
 /**
+ * @brief Create a new term node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node can be #LYS_LEAF or #LYS_LEAFLIST.
+ * @param[in] value Binary value of the node. To learn what exactly is expected see @ref howtoDataLYB.
+ * @param[in] value_len Length of @p value.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_new_term_bin(struct lyd_node *parent, const struct lys_module *module, const char *name, const void *value,
+        size_t value_len, ly_bool output, struct lyd_node **node);
+
+/**
+ * @brief Create a new term node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node can be #LYS_LEAF or #LYS_LEAFLIST.
+ * @param[in] val_str Canonical string value of the node. If it is not, it may lead to unexpected behavior.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_new_term_canon(struct lyd_node *parent, const struct lys_module *module, const char *name,
+        const char *val_str, ly_bool output, struct lyd_node **node);
+
+/**
  * @brief Create a new top-level term node defined in the given extension instance.
  *
  * To create a term node with parent (no matter if defined inside extension instance or a standard tree) or a top-level
@@ -1259,15 +1295,20 @@
  * @{
  */
 
-#define LYD_NEW_PATH_UPDATE 0x01 /**< If the target node exists, is a leaf, and it is updated with a new value or its
-                                      default flag is changed, it is returned. If the target node exists and is not
-                                      a leaf or generally no change occurs in the @p parent tree, NULL is returned and
-                                      no error set. */
-#define LYD_NEW_PATH_OUTPUT 0x02 /**< Changes the behavior to ignoring RPC/action input schema nodes and using only
-                                      output ones. */
-#define LYD_NEW_PATH_OPAQ   0x04 /**< Enables the creation of opaque nodes with some specific rules. If the __last node__
-                                      in the path is not uniquely defined ((leaf-)list without a predicate) or has an
-                                      invalid value (leaf/leaf-list), it is created as opaque. */
+#define LYD_NEW_PATH_UPDATE 0x01    /**< If the target node exists, is a leaf, and it is updated with a new value or its
+                                        default flag is changed, it is returned. If the target node exists and is not
+                                        a leaf or generally no change occurs in the @p parent tree, NULL is returned and
+                                        no error set. */
+#define LYD_NEW_PATH_OUTPUT 0x02    /**< Changes the behavior to ignoring RPC/action input schema nodes and using only
+                                        output ones. */
+#define LYD_NEW_PATH_OPAQ   0x04    /**< Enables the creation of opaque nodes with some specific rules. If the __last node__
+                                        in the path is not uniquely defined ((leaf-)list without a predicate) or has an
+                                        invalid value (leaf/leaf-list), it is created as opaque. */
+#define LYD_NEW_PATH_BIN_VALUE 0x08 /**< Interpret the provided leaf/leaf-list @p value as being in the binary
+                                        ::LY_VALUE_LYB format, to learn what exactly is expected see @ref howtoDataLYB. */
+#define LYD_NEW_PATH_CANON_VALUE 0x10   /**< Interpret the provided leaf/leaf-list @p value as being in the canonical
+                                            (or JSON if no defined) ::LY_VALUE_CANON format. If it is not, it may lead
+                                            to unexpected behavior. */
 
 /** @} pathoptions */
 
@@ -1289,7 +1330,8 @@
  * before @p parent. Use ::lyd_first_sibling() to adjust @p parent in these cases.
  * @param[in] ctx libyang context, must be set if @p parent is NULL.
  * @param[in] path [Path](@ref howtoXPath) to create.
- * @param[in] value Value of the new leaf/leaf-list. For other node types, it is ignored.
+ * @param[in] value String value of the new leaf/leaf-list. If it varies based on the format, ::LY_VALUE_JSON is expected.
+ * For other node types, it should be NULL.
  * @param[in] options Bitmask of options, see @ref pathoptions.
  * @param[out] node Optional first created node.
  * @return LY_ERR value.
@@ -1307,8 +1349,10 @@
  * before @p parent. Use ::lyd_first_sibling() to adjust @p parent in these cases.
  * @param[in] ctx libyang context, must be set if @p parent is NULL.
  * @param[in] path [Path](@ref howtoXPath) to create.
- * @param[in] value Value of the new leaf/leaf-list (const char *) or anyxml/anydata (expected type depends on @p value_type).
- * For other node types, it is ignored.
+ * @param[in] value Value of the new leaf/leaf-list (const char *) in ::LY_VALUE_JSON format. If creating an
+ * anyxml/anydata node, the expected type depends on @p value_type. For other node types, it should be NULL.
+ * @param[in] value_len Length of @p value in bytes. May be 0 if @p value is a zero-terminated string. Ignored when
+ * creating anyxml/anydata nodes.
  * @param[in] value_type Anyxml/anydata node @p value type.
  * @param[in] options Bitmask of options, see @ref pathoptions.
  * @param[out] new_parent Optional first parent node created. If only one node was created, equals to @p new_node.
@@ -1316,7 +1360,8 @@
  * @return LY_ERR value.
  */
 LY_ERR lyd_new_path2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
-        LYD_ANYDATA_VALUETYPE value_type, uint32_t options, struct lyd_node **new_parent, struct lyd_node **new_node);
+        size_t value_len, LYD_ANYDATA_VALUETYPE value_type, uint32_t options, struct lyd_node **new_parent,
+        struct lyd_node **new_node);
 
 /**
  * @brief Create a new node defined in the given extension instance. In case of anyxml/anydata nodes, this function expects
@@ -1331,7 +1376,7 @@
  * before @p parent. Use ::lyd_first_sibling() to adjust @p parent in these cases.
  * @param[in] ext Extension instance where the node being created is defined.
  * @param[in] path [Path](@ref howtoXPath) to create.
- * @param[in] value Value of the new leaf/leaf-list. For other node types, it is ignored.
+ * @param[in] value Value of the new leaf/leaf-list. For other node types, it should be NULL.
  * @param[in] options Bitmask of options, see @ref pathoptions.
  * @param[out] node Optional first created node.
  * @return LY_ERR value.
@@ -1398,7 +1443,7 @@
         struct lyd_node **diff);
 
 /**
- * @brief Change the value of a term (leaf or leaf-list) node.
+ * @brief Change the value of a term (leaf or leaf-list) node to a string value.
  *
  * Node changed this way is always considered explicitly set, meaning its default flag
  * is always cleared.
@@ -1413,6 +1458,38 @@
 LY_ERR lyd_change_term(struct lyd_node *term, const char *val_str);
 
 /**
+ * @brief Change the value of a term (leaf or leaf-list) node to a binary value.
+ *
+ * Node changed this way is always considered explicitly set, meaning its default flag
+ * is always cleared.
+ *
+ * @param[in] term Term node to change.
+ * @param[in] value New value to set in binary format, see @ref howtoDataLYB.
+ * @param[in] value_len Length of @p value.
+ * @return LY_SUCCESS if value was changed,
+ * @return LY_EEXIST if value was the same and only the default flag was cleared,
+ * @return LY_ENOT if the values were equal and no change occured,
+ * @return LY_ERR value on other errors.
+ */
+LY_ERR lyd_change_term_bin(struct lyd_node *term, const void *value, size_t value_len);
+
+/**
+ * @brief Change the value of a term (leaf or leaf-list) node to a canonical string value.
+ *
+ * Node changed this way is always considered explicitly set, meaning its default flag
+ * is always cleared.
+ *
+ * @param[in] term Term node to change.
+ * @param[in] val_str New value to set in canonical (or JSON if no defined) format. If the value is not
+ * canonical, it may lead to unexpected behavior.
+ * @return LY_SUCCESS if value was changed,
+ * @return LY_EEXIST if value was the same and only the default flag was cleared,
+ * @return LY_ENOT if the values were equal and no change occured,
+ * @return LY_ERR value on other errors.
+ */
+LY_ERR lyd_change_term_canon(struct lyd_node *term, const char *val_str);
+
+/**
  * @brief Change the value of a metadata instance.
  *
  * @param[in] meta Metadata to change.