Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/parser_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,24 @@ LIBYANG_API_DECL LY_ERR lyd_parse_data_path(const struct ly_ctx *ctx, const char
LIBYANG_API_DECL LY_ERR lyd_parse_ext_data(const struct lysc_ext_instance *ext, struct lyd_node *parent, struct ly_in *in,
LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree);

/**
* @brief Parse data from the input handler as a bare JSON value and connect it to the node parsed from the path.
*
* @param[in] ctx Context to connect with the tree being built here.
* @param[in] path Path to the node so the value in the input handler can be connected to it.
* @param[in] in Input handler with the value in the JSON format.
* @param[in] format Currently only LYD_JSON is supported.
* @param[in] new_val_options Options for new values of the parent node (node to attach the @p in to), see @ref newvaloptions.
* @param[in] parse_options Options for parser, see @ref dataparseroptions.
* @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
* @param[out] tree Full parsed data tree, note that NULL can be a valid tree.
* @return LY_SUCCESS in case of successful parsing.
* @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
*/
LIBYANG_API_DECL LY_ERR lyd_parse_value_fragment(const struct ly_ctx *ctx, const char *path, struct ly_in *in,
LYD_FORMAT format, uint32_t new_val_options, uint32_t parse_options, uint32_t validate_options,
struct lyd_node **tree);

/**
* @ingroup datatree
* @defgroup datatype Data operation type
Expand Down
8 changes: 5 additions & 3 deletions src/parser_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,19 +286,21 @@ LY_ERR lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_ins
* @param[in] ctx libyang context.
* @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance
* @param[in] parent Parent to connect the parsed nodes to, if any.
* @param[in] schema Optional schema node of the parsed node (mandatory when parsing JSON value fragment).
* @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
* @param[in] in Input structure.
* @param[in] parse_opts Options for parser, see @ref dataparseroptions.
* @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
* @param[in] int_opts Internal data parser options.
* @param[out] parsed Set to add all the parsed siblings into.
* @param[out] subtree_sibling Set if ::LYD_PARSE_SUBTREE is used and another subtree is following in @p in.
* @param[out] lydctx_p Data parser context to finish validation.
* @param[out] lydctx_p Optional data parser context to finish validation.
* @return LY_ERR value.
*/
LY_ERR lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p);
const struct lysc_node *schema, struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts,
uint32_t val_opts, uint32_t int_opts, struct ly_set *parsed, ly_bool *subtree_sibling,
struct lyd_ctx **lydctx_p);

/**
* @brief Parse JSON string as a RESTCONF message.
Expand Down
170 changes: 148 additions & 22 deletions src/parser_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,7 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st
*/
static void
lydjson_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last,
struct lysc_ext_instance *ext)
const struct lysc_ext_instance *ext)
{
if (!*node_p) {
return;
Expand Down Expand Up @@ -1822,15 +1822,17 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l
* @brief Common start of JSON parser processing different types of the input data.
*
* @param[in] ctx libyang context
* @param[in] schema Schema node of the potential bare value to check.
* @param[in] in Input structure.
* @param[in] parse_opts Options for parser, see @ref dataparseroptions.
* @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
* @param[out] status_p Initial status of the input structure.
* @param[out] lydctx_p Data parser context to finish validation.
* @return LY_ERR value.
*/
static LY_ERR
lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts,
struct lyd_json_ctx **lydctx_p)
lyd_parse_json_init(const struct ly_ctx *ctx, const struct lysc_node *schema, struct ly_in *in, uint32_t parse_opts,
uint32_t val_opts, enum LYJSON_PARSER_STATUS *status_p, struct lyd_json_ctx **lydctx_p)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_json_ctx *lydctx;
Expand All @@ -1849,28 +1851,139 @@ lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_o
status = lyjson_ctx_status(lydctx->jsonctx);

/* parse_opts & LYD_PARSE_SUBTREE not implemented */
if (status != LYJSON_OBJECT) {
/* expecting top-level object */
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected top-level JSON object, but %s found.", lyjson_token2str(status));
/* there are two options: either we want to parse a bare JSON value or a JSON object
* node of the bare JSON value has to have a schema, otherwise we do not know where to put the value
* the only types of nodes that can take on a value are a leaf (number, string or bool) and a leaf-list (array) */
if (schema &&
(((status == LYJSON_ARRAY) && (schema->nodetype & LYS_LEAFLIST)) ||
(((status == LYJSON_NUMBER) || (status == LYJSON_STRING) || (status == LYJSON_FALSE) ||
(status == LYJSON_TRUE) || (status == LYJSON_NULL) || (status == LYJSON_ARRAY)) && (schema->nodetype & LYS_LEAF)))) {
/* bare value (bare anydata 'value = object' is not supported) */
} else if (status == LYJSON_OBJECT) {
/* JSON object */
} else {
/* expecting top-level object or bare value */
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected top-level JSON object or correct bare value, but %s found.", lyjson_token2str(status));
*lydctx_p = NULL;
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
return LY_EVALID;
}

*lydctx_p = lydctx;
if (status_p) {
*status_p = status;
}
return LY_SUCCESS;
}

/**
* @brief Parse a bare JSON value.
*
* @param[in] lydctx Data parser context.
* @param[in,out] status Current status of the data parser context.
* @param[in] parent Parent to connect the parsed nodes to, if any.
* @param[in] schema Optional schema node of the parsed node (mandatory when parsing JSON value fragment).
* @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
* @return LY_ERR value.
*/
static LY_ERR
lyd_parse_json_bare_value(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status, struct lyd_node *parent,
const struct lysc_node *schema, struct lyd_node **first_p)
{
LY_ERR r, rc = LY_SUCCESS;
const struct ly_ctx *ctx = lydctx->jsonctx->ctx;
struct lyd_node *node = NULL;
const char *expected = NULL;

assert(schema);

/* this branch partly copies the behavior of lydjson_subtree_r() */

/* set expected representation */
switch (schema->nodetype) {
case LYS_LEAFLIST:
expected = "array of values";
break;
case LYS_LEAF:
if (*status == LYJSON_ARRAY) {
expected = "[null]";
} else {
expected = "value";
}
break;
}

/* check the representation according to the nodetype and then continue with the content */
/* for now object values are not supported (anydata) */
/* for now extensions not supported */
switch (schema->nodetype) {
case LYS_LEAFLIST:
LY_CHECK_GOTO(*status != LYJSON_ARRAY, representation_error);

/* process all the values/objects */
do {
/* move into array/next value */
r = lyjson_ctx_next(lydctx->jsonctx, status);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (*status == LYJSON_ARRAY_CLOSED) {
/* empty array, fine... */
break;
}

r = lydjson_parse_instance(lydctx, parent, first_p, schema, NULL, schema->name, strlen(schema->name),
NULL, 0, status, &node);
if (r == LY_ENOT) {
goto representation_error;
}
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);

lydjson_maintain_children(parent, first_p, &node,
lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL);

/* move after the item(s) */
r = lyjson_ctx_next(lydctx->jsonctx, status);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
} while (*status == LYJSON_ARRAY_NEXT);

break;
case LYS_LEAF:
/* process the value/object */
r = lydjson_parse_instance(lydctx, parent, first_p, schema, NULL, schema->name, strlen(schema->name),
NULL, 0, status, &node);
if (r == LY_ENOT) {
goto representation_error;
}
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);

/* finally connect the parsed node, is zeroed */
lydjson_maintain_children(parent, first_p, &node,
lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL);
break;
}

goto cleanup;

representation_error:
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s \"%s\" is represented in input data as %s.",
expected, lys_nodetype2str(schema->nodetype), schema->name, lyjson_token2str(*status));
rc = LY_EVALID;

cleanup:
lyd_free_tree(node);
return rc;
}

LY_ERR
lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p)
const struct lysc_node *schema, struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts,
uint32_t val_opts, uint32_t int_opts, struct ly_set *parsed, ly_bool *subtree_sibling,
struct lyd_ctx **lydctx_p)
{
LY_ERR r, rc = LY_SUCCESS;
struct lyd_json_ctx *lydctx = NULL;
enum LYJSON_PARSER_STATUS status;
enum LYJSON_PARSER_STATUS status = LYJSON_ERROR;

rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx);
rc = lyd_parse_json_init(ctx, schema, in, parse_opts, val_opts, &status, &lydctx);
LY_CHECK_GOTO(rc, cleanup);

lydctx->int_opts = int_opts;
Expand All @@ -1879,17 +1992,25 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
/* find the operation node if it exists already */
LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);

/* read subtree(s) */
do {
r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
if (status != LYJSON_OBJECT) {
/* parse bare JSON value */
r = lyd_parse_json_bare_value(lydctx, &status, parent, schema, first_p);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
} else {
/* parse JSON object */

status = lyjson_ctx_status(lydctx->jsonctx);
/* read subtree(s) */
do {
r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);

if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
break;
}
} while (status == LYJSON_OBJECT_NEXT);
status = lyjson_ctx_status(lydctx->jsonctx);

if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
break;
}
} while (status == LYJSON_OBJECT_NEXT);
}

if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] && (status != LYJSON_OBJECT_CLOSED)) {
LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
Expand Down Expand Up @@ -1927,11 +2048,16 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
if (rc && (!lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
} else {
*lydctx_p = (struct lyd_ctx *)lydctx;

/* the JSON context is no more needed, freeing it also stops logging line numbers which would be confusing now */
lyjson_ctx_free(lydctx->jsonctx);
lydctx->jsonctx = NULL;

/* set optional lydctx pointer, otherwise free */
if (lydctx_p) {
*lydctx_p = (struct lyd_ctx *)lydctx;
} else {
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
}
}
return rc;
}
Expand Down Expand Up @@ -2020,7 +2146,7 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance
assert(!(parse_opts & LYD_PARSE_SUBTREE));

/* init context */
rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx);
rc = lyd_parse_json_init(ctx, NULL, in, parse_opts, val_opts, NULL, &lydctx);
LY_CHECK_GOTO(rc, cleanup);
lydctx->ext = ext;

Expand Down
7 changes: 7 additions & 0 deletions src/printer_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ struct ly_out;
are not explicitly present in the original data tree despite their
value is equal to their default value. There is the same limitation regarding
the presence of ietf-netconf-with-defaults module in libyang context. */
#define LYD_PRINT_JSON_NO_NESTED_PREFIX 0x0100 /**< Do not print the prefix in JSON data for nested nodes
(non-top-level). By nested we mean any node that does not appear
in the top-level of a corresponding schema. The printed data do
not have the information about the module they belong to. When
parsing such data it is important to include this information
elsewhere (e.g. for lyd_parse_value_fragment() the module name
should be part of the path parameter). */
/**
* @}
*/
Expand Down
6 changes: 5 additions & 1 deletion src/printer_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ json_print_member(struct jsonpr_ctx *pctx, const struct lyd_node *node, const st
}

PRINT_COMMA;
if ((LEVEL == 1) || json_nscmp(node, snode, pctx->parent)) {
if (json_nscmp(node, snode, pctx->parent)) {
/* print "namespace" */
node_prefix(node, snode, &pref, NULL);
ly_print_(pctx->out, "%*s\"%s%s:%s\":%s", INDENT, is_attr ? "@" : "", pref, name, DO_FORMAT ? " " : "");
Expand Down Expand Up @@ -1174,6 +1174,10 @@ json_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t option
/* content */
LY_LIST_FOR(root, node) {
pctx.root = node;
if (options & LYD_PRINT_JSON_NO_NESTED_PREFIX) {
/* update parent, if it has the same namespace it will not be printed */
pctx.parent = (const struct lyd_node *)(node->parent);
}
LY_CHECK_RET(json_print_node(&pctx, node));
if (!(options & LYD_PRINT_SIBLINGS)) {
break;
Expand Down
Loading