Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for printing and parsing bare json values for sysrepo-gnmi #2154

Open
wants to merge 2 commits into
base: devel
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions src/parser_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ struct ly_in;
when parsing validated data to skip some validation tasks and modify
some validation behavior (auto-deletion of cases). */

#define LYD_PARSE_BARETOPLEAF 0x2000000 /**< Fragments for top-level leaf values are expected to be bare and not
have a top-level object or node name. */

#define LYD_PARSE_OPTS_MASK 0xFFFF0000 /**< Mask for all the LYD_PARSE_ options. */

/** @} dataparseroptions */
Expand Down
63 changes: 51 additions & 12 deletions src/parser_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -1817,7 +1817,7 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l
* @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,
lyd_parse_json_init(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts,
struct lyd_json_ctx **lydctx_p)
{
LY_ERR ret = LY_SUCCESS;
Expand All @@ -1837,7 +1837,14 @@ 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) {
if (parse_opts & LYD_PARSE_BARETOPLEAF && parent && (parent->schema->nodetype == LYS_LEAF)) {
if ((status != LYJSON_STRING) && (status != LYJSON_OBJECT)) {
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected top-level JSON object or bare value, but %s found.", lyjson_token2str(status));
*lydctx_p = NULL;
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
return LY_EVALID;
}
} else if (status != LYJSON_OBJECT) {
/* expecting top-level object */
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected top-level JSON object, but %s found.", lyjson_token2str(status));
*lydctx_p = NULL;
Expand All @@ -1857,8 +1864,9 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
LY_ERR r, rc = LY_SUCCESS;
struct lyd_json_ctx *lydctx = NULL;
enum LYJSON_PARSER_STATUS status;
uint32_t log_location_items = 0;

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

lydctx->int_opts = int_opts;
Expand All @@ -1867,17 +1875,46 @@ 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);
if (lyjson_ctx_status(lydctx->jsonctx) == LYJSON_OBJECT) {
/* read subtree(s) */
do {
r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);

status = lyjson_ctx_status(lydctx->jsonctx);

if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
break;
}
} while (status == LYJSON_OBJECT_NEXT);
assert((status == LYJSON_END) || (status == LYJSON_OBJECT_CLOSED));
} else {
struct lyd_node_term *t;
struct lysc_type *type;
struct lyd_value val;

t = (struct lyd_node_term *)parent;
type = ((struct lysc_node_leaf *)parent->schema)->type;

LOG_LOCSET(parent->schema, parent, NULL, NULL);
log_location_items++;

r = lyd_value_store(LYD_CTX(parent), &val, type, lydctx->jsonctx->value, lydctx->jsonctx->value_len, 0, NULL,
LY_VALUE_JSON, NULL, LYD_HINT_DATA, parent->schema, NULL);
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);

status = lyjson_ctx_status(lydctx->jsonctx);
type->plugin->free(LYD_CTX(parent), &t->value);
t->value = val;

if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
break;
r = lyjson_ctx_next(lydctx->jsonctx, &status);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);

if (status != LYJSON_END) {
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected end-of-input, but %s found.", lyjson_token2str(status));
rc = LY_EVALID;
goto cleanup;
}
} 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 All @@ -1891,7 +1928,7 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
}

/* finish linking metadata */
r = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
r = lydjson_metadata_finish(lydctx, parent ? &parent : first_p);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);

if (parse_opts & LYD_PARSE_SUBTREE) {
Expand Down Expand Up @@ -1921,6 +1958,8 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
lyjson_ctx_free(lydctx->jsonctx);
lydctx->jsonctx = NULL;
}

LOG_LOCBACK(log_location_items, log_location_items, 0, 0);
return rc;
}

Expand Down Expand Up @@ -2008,7 +2047,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, parent, in, parse_opts, val_opts, &lydctx);
LY_CHECK_GOTO(rc, cleanup);
lydctx->ext = ext;

Expand Down
5 changes: 5 additions & 0 deletions src/printer_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ 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_FRAGMENT 0x100 /**< Print the data tree as a fragment. For
JSON this means that apparent
top-level nodes do not have their
namespace added. */
#define LYD_PRINT_BARETOPLEAF 0x200 /**< Print top-level leaf node as a bare value (JSON-only). */
/**
* @}
*/
Expand Down
26 changes: 23 additions & 3 deletions src/printer_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,13 @@ json_print_string(struct ly_out *out, const char *text)
static LY_ERR
json_print_member(struct jsonpr_ctx *pctx, const struct lyd_node *node, ly_bool is_attr)
{
if ((LEVEL == 1) && pctx->options & LYD_PRINT_BARETOPLEAF) {
return LY_SUCCESS;
}

PRINT_COMMA;
if ((LEVEL == 1) || json_nscmp(node, pctx->parent)) {
if (((LEVEL == 1) || json_nscmp(node, pctx->parent)) &&
!(pctx->options & LYD_PRINT_FRAGMENT)) {
/* print "namespace" */
ly_print_(pctx->out, "%*s\"%s%s:%s\":%s", INDENT, is_attr ? "@" : "",
node_prefix(node), node->schema->name, DO_FORMAT ? " " : "");
Expand All @@ -298,6 +303,10 @@ json_print_member2(struct jsonpr_ctx *pctx, const struct lyd_node *parent, LY_VA
{
const char *module_name = NULL, *name_str;

if ((LEVEL == 1) && pctx->options & LYD_PRINT_BARETOPLEAF) {
return LY_SUCCESS;
}

PRINT_COMMA;

/* determine prefix string */
Expand Down Expand Up @@ -999,13 +1008,20 @@ json_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t option
const struct lyd_node *node;
struct jsonpr_ctx pctx = {0};
const char *delimiter = (options & LYD_PRINT_SHRINK) ? "" : "\n";
int is_obj = 1;

if (!root) {
ly_print_(out, "{}%s", delimiter);
ly_print_flush(out);
return LY_SUCCESS;
}

if (!(options & LYD_PRINT_WITHSIBLINGS) &&
options & LYD_PRINT_BARETOPLEAF &&
(root->schema->nodetype == LYS_LEAF)) {
is_obj = 0;
}

pctx.out = out;
pctx.parent = NULL;
pctx.level = 1;
Expand All @@ -1014,7 +1030,9 @@ json_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t option
pctx.ctx = LYD_CTX(root);

/* start */
ly_print_(pctx.out, "{%s", delimiter);
if (is_obj) {
ly_print_(pctx.out, "{%s", delimiter);
}

/* content */
LY_LIST_FOR(root, node) {
Expand All @@ -1026,7 +1044,9 @@ json_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t option
}

/* end */
ly_print_(out, "%s}%s", delimiter, delimiter);
if (is_obj) {
ly_print_(pctx.out, "%s}%s", delimiter, delimiter);
}

assert(!pctx.open.count);
ly_set_erase(&pctx.open, NULL);
Expand Down
33 changes: 33 additions & 0 deletions tests/utests/data/test_parser_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,38 @@ test_metadata(void **state)
CHECK_LOG_CTX("Invalid non-number-encoded int8 value \"value\".", "Path \"/a:c/x/@a:hint\", line number 1.");
}

static void
test_baretopleaf(void **state)
{
struct lyd_node *root;
struct lyd_node *node;
struct lyd_node *tree;
struct lyd_node_term *leaf;
const char *data;
struct ly_in *in = NULL;
int ret;

ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:foo", NULL, 0, 0, 0, &root, &node);
if (ret) {
fail_msg("Print err 0x%d; MSG: %s", ret, ly_err_last(UTEST_LYCTX)->msg);
}

data = "\"foo value\"";
if ((ret = ly_in_new_memory(data, &in))) {
fail_msg("Print err 0x%d; MSG: %s", ret, ly_err_last(UTEST_LYCTX)->msg);
}

if ((ret = lyd_parse_data(UTEST_LYCTX, node, in, LYD_JSON, LYD_PARSE_BARETOPLEAF | LYD_PARSE_ONLY, 0, &tree))) {
fail_msg("Print err 0x%d; MSG: %s", ret, ly_err_last(UTEST_LYCTX)->msg);
}
leaf = (struct lyd_node_term *)node;
CHECK_LYD_VALUE(leaf->value, STRING, "foo value");

CHECK_LYD_STRING(node, LYD_PRINT_SHRINK | LYD_PRINT_BARETOPLEAF, data);
lyd_free_all(root);
ly_in_free(in, 0);
}

int
main(void)
{
Expand All @@ -933,6 +965,7 @@ main(void)
UTEST(test_restconf_notification, setup),
UTEST(test_restconf_reply, setup),
UTEST(test_metadata, setup),
UTEST(test_baretopleaf, setup),
};

return cmocka_run_group_tests(tests, NULL, NULL);
Expand Down
Loading