Skip to content

Commit 18493da

Browse files
committed
JSON parser ADD parse JSON value fragment
New functionality allowing parsing only JSON values and adding them to the nodes specified by path.
1 parent 17265ec commit 18493da

File tree

7 files changed

+354
-76
lines changed

7 files changed

+354
-76
lines changed

src/parser_data.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,24 @@ LIBYANG_API_DECL LY_ERR lyd_parse_data_path(const struct ly_ctx *ctx, const char
336336
LIBYANG_API_DECL LY_ERR lyd_parse_ext_data(const struct lysc_ext_instance *ext, struct lyd_node *parent, struct ly_in *in,
337337
LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree);
338338

339+
/**
340+
* @brief Parse data from the input handler as a bare JSON value and connect it to the node parsed from the path.
341+
*
342+
* @param[in] ctx Context to connect with the tree being built here.
343+
* @param[in] path Path to the node so the value in the input handler can be connected to it.
344+
* @param[in] in Input handler with the value in the JSON format.
345+
* @param[in] format Currently only LYD_JSON is supported.
346+
* @param[in] new_val_options Options for new values, see @ref newvaloptions.
347+
* @param[in] parse_options Options for parser, see @ref dataparseroptions.
348+
* @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
349+
* @param[out] tree Full parsed data tree, note that NULL can be a valid tree.
350+
* @return LY_SUCCESS in case of successful parsing.
351+
* @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
352+
*/
353+
LIBYANG_API_DECL LY_ERR lyd_parse_value_fragment(const struct ly_ctx *ctx, const char *path, struct ly_in *in,
354+
LYD_FORMAT format, uint32_t new_val_options, uint32_t parse_options, uint32_t validate_options,
355+
struct lyd_node **tree);
356+
339357
/**
340358
* @ingroup datatree
341359
* @defgroup datatype Data operation type

src/parser_internal.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,19 +286,21 @@ LY_ERR lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_ins
286286
* @param[in] ctx libyang context.
287287
* @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance
288288
* @param[in] parent Parent to connect the parsed nodes to, if any.
289+
* @param[in] schema Optional schema node of the parsed node (mandatory when parsing JSON value fragment).
289290
* @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
290291
* @param[in] in Input structure.
291292
* @param[in] parse_opts Options for parser, see @ref dataparseroptions.
292293
* @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
293294
* @param[in] int_opts Internal data parser options.
294295
* @param[out] parsed Set to add all the parsed siblings into.
295296
* @param[out] subtree_sibling Set if ::LYD_PARSE_SUBTREE is used and another subtree is following in @p in.
296-
* @param[out] lydctx_p Data parser context to finish validation.
297+
* @param[out] lydctx_p Optional data parser context to finish validation.
297298
* @return LY_ERR value.
298299
*/
299300
LY_ERR lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
300-
struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
301-
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p);
301+
const struct lysc_node *schema, struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts,
302+
uint32_t val_opts, uint32_t int_opts, struct ly_set *parsed, ly_bool *subtree_sibling,
303+
struct lyd_ctx **lydctx_p);
302304

303305
/**
304306
* @brief Parse JSON string as a RESTCONF message.

src/parser_json.c

Lines changed: 123 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,7 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st
935935
*/
936936
static void
937937
lydjson_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last,
938-
struct lysc_ext_instance *ext)
938+
const struct lysc_ext_instance *ext)
939939
{
940940
if (!*node_p) {
941941
return;
@@ -1822,15 +1822,17 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l
18221822
* @brief Common start of JSON parser processing different types of the input data.
18231823
*
18241824
* @param[in] ctx libyang context
1825+
* @param[in] schema Schema node of the potential bare value to check.
18251826
* @param[in] in Input structure.
18261827
* @param[in] parse_opts Options for parser, see @ref dataparseroptions.
18271828
* @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
1829+
* @param[out] status_p Initial status of the input structure.
18281830
* @param[out] lydctx_p Data parser context to finish validation.
18291831
* @return LY_ERR value.
18301832
*/
18311833
static LY_ERR
1832-
lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts,
1833-
struct lyd_json_ctx **lydctx_p)
1834+
lyd_parse_json_init(const struct ly_ctx *ctx, const struct lysc_node *schema, struct ly_in *in, uint32_t parse_opts,
1835+
uint32_t val_opts, enum LYJSON_PARSER_STATUS *status_p, struct lyd_json_ctx **lydctx_p)
18341836
{
18351837
LY_ERR ret = LY_SUCCESS;
18361838
struct lyd_json_ctx *lydctx;
@@ -1849,28 +1851,44 @@ lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_o
18491851
status = lyjson_ctx_status(lydctx->jsonctx);
18501852

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

18601872
*lydctx_p = lydctx;
1873+
if (status_p) {
1874+
*status_p = status;
1875+
}
18611876
return LY_SUCCESS;
18621877
}
18631878

18641879
LY_ERR
18651880
lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
1866-
struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
1867-
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p)
1881+
const struct lysc_node *schema, struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts,
1882+
uint32_t val_opts, uint32_t int_opts, struct ly_set *parsed, ly_bool *subtree_sibling,
1883+
struct lyd_ctx **lydctx_p)
18681884
{
18691885
LY_ERR r, rc = LY_SUCCESS;
18701886
struct lyd_json_ctx *lydctx = NULL;
1871-
enum LYJSON_PARSER_STATUS status;
1887+
enum LYJSON_PARSER_STATUS status = LYJSON_ERROR;
1888+
struct lyd_node *node = NULL;
1889+
const char *expected = NULL;
18721890

1873-
rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx);
1891+
rc = lyd_parse_json_init(ctx, schema, in, parse_opts, val_opts, &status, &lydctx);
18741892
LY_CHECK_GOTO(rc, cleanup);
18751893

18761894
lydctx->int_opts = int_opts;
@@ -1879,17 +1897,88 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
18791897
/* find the operation node if it exists already */
18801898
LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
18811899

1882-
/* read subtree(s) */
1883-
do {
1884-
r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
1885-
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
1900+
if (status != LYJSON_OBJECT) {
1901+
/* parse bare JSON value */
1902+
assert(schema);
18861903

1887-
status = lyjson_ctx_status(lydctx->jsonctx);
1904+
/* this branch partly copies the behavior of lydjson_subtree_r() */
18881905

1889-
if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
1906+
/* set expected representation */
1907+
switch (schema->nodetype) {
1908+
case LYS_LEAFLIST:
1909+
expected = "array of values";
1910+
break;
1911+
case LYS_LEAF:
1912+
if (status == LYJSON_ARRAY) {
1913+
expected = "[null]";
1914+
} else {
1915+
expected = "value";
1916+
}
18901917
break;
18911918
}
1892-
} while (status == LYJSON_OBJECT_NEXT);
1919+
1920+
/* check the representation according to the nodetype and then continue with the content */
1921+
/* for now object values are not supported (anydata) */
1922+
/* for now extensions not supported */
1923+
switch (schema->nodetype) {
1924+
case LYS_LEAFLIST:
1925+
LY_CHECK_GOTO(status != LYJSON_ARRAY, representation_error);
1926+
1927+
/* process all the values/objects */
1928+
do {
1929+
/* move into array/next value */
1930+
r = lyjson_ctx_next(lydctx->jsonctx, &status);
1931+
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
1932+
if (status == LYJSON_ARRAY_CLOSED) {
1933+
/* empty array, fine... */
1934+
break;
1935+
}
1936+
1937+
r = lydjson_parse_instance(lydctx, parent, first_p, schema, NULL, schema->name, strlen(schema->name),
1938+
NULL, 0, &status, &node);
1939+
if (r == LY_ENOT) {
1940+
goto representation_error;
1941+
}
1942+
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
1943+
1944+
lydjson_maintain_children(parent, first_p, &node,
1945+
lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL);
1946+
1947+
/* move after the item(s) */
1948+
r = lyjson_ctx_next(lydctx->jsonctx, &status);
1949+
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
1950+
} while (status == LYJSON_ARRAY_NEXT);
1951+
1952+
break;
1953+
case LYS_LEAF:
1954+
/* process the value/object */
1955+
r = lydjson_parse_instance(lydctx, parent, first_p, schema, NULL, schema->name, strlen(schema->name),
1956+
NULL, 0, &status, &node);
1957+
if (r == LY_ENOT) {
1958+
goto representation_error;
1959+
}
1960+
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
1961+
1962+
/* finally connect the parsed node, is zeroed */
1963+
lydjson_maintain_children(parent, first_p, &node,
1964+
lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL);
1965+
break;
1966+
}
1967+
} else {
1968+
/* parse JSON object */
1969+
1970+
/* read subtree(s) */
1971+
do {
1972+
r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
1973+
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
1974+
1975+
status = lyjson_ctx_status(lydctx->jsonctx);
1976+
1977+
if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
1978+
break;
1979+
}
1980+
} while (status == LYJSON_OBJECT_NEXT);
1981+
}
18931982

18941983
if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] && (status != LYJSON_OBJECT_CLOSED)) {
18951984
LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
@@ -1919,6 +2008,13 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
19192008
}
19202009
}
19212010

2011+
goto cleanup;
2012+
2013+
representation_error:
2014+
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s \"%s\" is represented in input data as %s.",
2015+
expected, lys_nodetype2str(schema->nodetype), schema->name, lyjson_token2str(status));
2016+
rc = LY_EVALID;
2017+
19222018
cleanup:
19232019
/* there should be no unresolved types stored */
19242020
assert(!(parse_opts & LYD_PARSE_ONLY) || !lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count &&
@@ -1927,12 +2023,19 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
19272023
if (rc && (!lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
19282024
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
19292025
} else {
1930-
*lydctx_p = (struct lyd_ctx *)lydctx;
1931-
19322026
/* the JSON context is no more needed, freeing it also stops logging line numbers which would be confusing now */
19332027
lyjson_ctx_free(lydctx->jsonctx);
19342028
lydctx->jsonctx = NULL;
2029+
2030+
/* set optional lydctx pointer, otherwise free */
2031+
if (lydctx_p) {
2032+
*lydctx_p = (struct lyd_ctx *)lydctx;
2033+
} else {
2034+
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
2035+
}
19352036
}
2037+
2038+
lyd_free_tree(node);
19362039
return rc;
19372040
}
19382041

@@ -2020,7 +2123,7 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance
20202123
assert(!(parse_opts & LYD_PARSE_SUBTREE));
20212124

20222125
/* init context */
2023-
rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx);
2126+
rc = lyd_parse_json_init(ctx, NULL, in, parse_opts, val_opts, NULL, &lydctx);
20242127
LY_CHECK_GOTO(rc, cleanup);
20252128
lydctx->ext = ext;
20262129

src/tree_data.c

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ lyd_parse(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct
130130
&subtree_sibling, &lydctx);
131131
break;
132132
case LYD_JSON:
133-
r = lyd_parse_json(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
133+
r = lyd_parse_json(ctx, ext, parent, NULL, first_p, in, parse_opts, val_opts, int_opts, &parsed,
134134
&subtree_sibling, &lydctx);
135135
break;
136136
case LYD_LYB:
@@ -279,6 +279,75 @@ lyd_parse_data_path(const struct ly_ctx *ctx, const char *path, LYD_FORMAT forma
279279
return ret;
280280
}
281281

282+
LIBYANG_API_DEF LY_ERR
283+
lyd_parse_value_fragment(const struct ly_ctx *ctx, const char *path, struct ly_in *in, LYD_FORMAT format,
284+
uint32_t new_val_options, uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree)
285+
{
286+
LY_ERR ret = LY_SUCCESS;
287+
struct lyxp_expr *exp = NULL;
288+
struct ly_path *p = NULL;
289+
struct lyd_node *new_parent = NULL, *top_level = NULL;
290+
const struct lysc_node *new_node_schema = NULL;
291+
struct ly_set parsed = {0};
292+
ly_bool p_decremented = 0;
293+
294+
assert(ctx);
295+
assert(path && (path[0] == '/'));
296+
297+
/* other formats are not supported for now */
298+
assert(format == LYD_JSON);
299+
300+
/* parse path */
301+
LY_CHECK_GOTO(ret = ly_path_parse(ctx, NULL, path, 0, 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST,
302+
LY_PATH_PRED_SIMPLE, &exp), cleanup);
303+
304+
/* compile path */
305+
LY_CHECK_GOTO(ret = ly_path_compile(ctx, NULL, NULL, NULL, exp, new_val_options & LYD_NEW_VAL_OUTPUT ?
306+
LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_MANY, 0, LY_VALUE_JSON, NULL, &p), cleanup);
307+
308+
/* has to have a schema */
309+
new_node_schema = p[LY_ARRAY_COUNT(p) - 1].node;
310+
311+
/* you cannot set a key */
312+
if (lysc_is_key(new_node_schema)) {
313+
LOGERR(ctx, LY_EINVAL, "Invalid argument: path: %s ends with a key (%s())", path, __func__);
314+
goto cleanup;
315+
}
316+
317+
/* only the term nodes get their path shortened */
318+
if (new_node_schema->nodetype & LYD_NODE_TERM) {
319+
/* shorten the ly_path by one element (to avoid a leaflist without predicate at the end) */
320+
LY_ARRAY_DECREMENT(p);
321+
p_decremented = 1;
322+
}
323+
324+
if (LY_ARRAY_COUNT(p)) {
325+
/* create nodes */
326+
LY_CHECK_GOTO(ret = lyd_new_path_create(NULL, ctx, NULL, p, path, NULL, 0, 0, new_val_options, &top_level,
327+
&new_parent), cleanup);
328+
}
329+
330+
/* parse the json value */
331+
LY_CHECK_GOTO(ret = lyd_parse_json(ctx, NULL, new_parent, new_node_schema, new_parent ? NULL : &top_level, in,
332+
parse_options, validate_options, 0, &parsed, NULL, NULL), cleanup);
333+
334+
/* set output tree */
335+
if (tree) {
336+
*tree = top_level;
337+
}
338+
339+
cleanup:
340+
lyxp_expr_free(exp);
341+
if (p_decremented) {
342+
LY_ARRAY_INCREMENT(p);
343+
}
344+
ly_path_free(p);
345+
if (ret) {
346+
lyd_free_all(top_level);
347+
}
348+
return ret;
349+
}
350+
282351
/**
283352
* @brief Parse YANG data into an operation data tree, in case the extension instance is specified, keep the searching
284353
* for schema nodes locked inside the extension instance.
@@ -401,7 +470,7 @@ lyd_parse_op_(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str
401470
rc = lyd_parse_xml(ctx, ext, parent, &first, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx);
402471
break;
403472
case LYD_JSON:
404-
rc = lyd_parse_json(ctx, ext, parent, &first, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx);
473+
rc = lyd_parse_json(ctx, ext, parent, NULL, &first, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx);
405474
break;
406475
case LYD_LYB:
407476
rc = lyd_parse_lyb(ctx, ext, parent, &first, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx);

0 commit comments

Comments
 (0)