Skip to content

Commit f677166

Browse files
Humblesawmichalvasko
authored andcommitted
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 3d07c3a commit f677166

File tree

7 files changed

+431
-78
lines changed

7 files changed

+431
-78
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 of the parent node (node to attach the @p in to), 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: 148 additions & 22 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,139 @@ 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

1879+
/**
1880+
* @brief Parse a bare JSON value.
1881+
*
1882+
* @param[in] lydctx Data parser context.
1883+
* @param[in,out] status Current status of the data parser context.
1884+
* @param[in] parent Parent to connect the parsed nodes to, if any.
1885+
* @param[in] schema Optional schema node of the parsed node (mandatory when parsing JSON value fragment).
1886+
* @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
1887+
* @return LY_ERR value.
1888+
*/
1889+
static LY_ERR
1890+
lyd_parse_json_bare_value(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status, struct lyd_node *parent,
1891+
const struct lysc_node *schema, struct lyd_node **first_p)
1892+
{
1893+
LY_ERR r, rc = LY_SUCCESS;
1894+
const struct ly_ctx *ctx = lydctx->jsonctx->ctx;
1895+
struct lyd_node *node = NULL;
1896+
const char *expected = NULL;
1897+
1898+
assert(schema);
1899+
1900+
/* this branch partly copies the behavior of lydjson_subtree_r() */
1901+
1902+
/* set expected representation */
1903+
switch (schema->nodetype) {
1904+
case LYS_LEAFLIST:
1905+
expected = "array of values";
1906+
break;
1907+
case LYS_LEAF:
1908+
if (*status == LYJSON_ARRAY) {
1909+
expected = "[null]";
1910+
} else {
1911+
expected = "value";
1912+
}
1913+
break;
1914+
}
1915+
1916+
/* check the representation according to the nodetype and then continue with the content */
1917+
/* for now object values are not supported (anydata) */
1918+
/* for now extensions not supported */
1919+
switch (schema->nodetype) {
1920+
case LYS_LEAFLIST:
1921+
LY_CHECK_GOTO(*status != LYJSON_ARRAY, representation_error);
1922+
1923+
/* process all the values/objects */
1924+
do {
1925+
/* move into array/next value */
1926+
r = lyjson_ctx_next(lydctx->jsonctx, status);
1927+
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
1928+
if (*status == LYJSON_ARRAY_CLOSED) {
1929+
/* empty array, fine... */
1930+
break;
1931+
}
1932+
1933+
r = lydjson_parse_instance(lydctx, parent, first_p, schema, NULL, schema->name, strlen(schema->name),
1934+
NULL, 0, status, &node);
1935+
if (r == LY_ENOT) {
1936+
goto representation_error;
1937+
}
1938+
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
1939+
1940+
lydjson_maintain_children(parent, first_p, &node,
1941+
lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL);
1942+
1943+
/* move after the item(s) */
1944+
r = lyjson_ctx_next(lydctx->jsonctx, status);
1945+
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
1946+
} while (*status == LYJSON_ARRAY_NEXT);
1947+
1948+
break;
1949+
case LYS_LEAF:
1950+
/* process the value/object */
1951+
r = lydjson_parse_instance(lydctx, parent, first_p, schema, NULL, schema->name, strlen(schema->name),
1952+
NULL, 0, status, &node);
1953+
if (r == LY_ENOT) {
1954+
goto representation_error;
1955+
}
1956+
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
1957+
1958+
/* finally connect the parsed node, is zeroed */
1959+
lydjson_maintain_children(parent, first_p, &node,
1960+
lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL);
1961+
break;
1962+
}
1963+
1964+
goto cleanup;
1965+
1966+
representation_error:
1967+
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s \"%s\" is represented in input data as %s.",
1968+
expected, lys_nodetype2str(schema->nodetype), schema->name, lyjson_token2str(*status));
1969+
rc = LY_EVALID;
1970+
1971+
cleanup:
1972+
lyd_free_tree(node);
1973+
return rc;
1974+
}
1975+
18641976
LY_ERR
18651977
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)
1978+
const struct lysc_node *schema, struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts,
1979+
uint32_t val_opts, uint32_t int_opts, struct ly_set *parsed, ly_bool *subtree_sibling,
1980+
struct lyd_ctx **lydctx_p)
18681981
{
18691982
LY_ERR r, rc = LY_SUCCESS;
18701983
struct lyd_json_ctx *lydctx = NULL;
1871-
enum LYJSON_PARSER_STATUS status;
1984+
enum LYJSON_PARSER_STATUS status = LYJSON_ERROR;
18721985

1873-
rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx);
1986+
rc = lyd_parse_json_init(ctx, schema, in, parse_opts, val_opts, &status, &lydctx);
18741987
LY_CHECK_GOTO(rc, cleanup);
18751988

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

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);
1995+
if (status != LYJSON_OBJECT) {
1996+
/* parse bare JSON value */
1997+
r = lyd_parse_json_bare_value(lydctx, &status, parent, schema, first_p);
1998+
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
1999+
} else {
2000+
/* parse JSON object */
18862001

1887-
status = lyjson_ctx_status(lydctx->jsonctx);
2002+
/* read subtree(s) */
2003+
do {
2004+
r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
2005+
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
18882006

1889-
if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
1890-
break;
1891-
}
1892-
} while (status == LYJSON_OBJECT_NEXT);
2007+
status = lyjson_ctx_status(lydctx->jsonctx);
2008+
2009+
if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
2010+
break;
2011+
}
2012+
} while (status == LYJSON_OBJECT_NEXT);
2013+
}
18932014

18942015
if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] && (status != LYJSON_OBJECT_CLOSED)) {
18952016
LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
@@ -1927,11 +2048,16 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
19272048
if (rc && (!lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
19282049
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
19292050
} else {
1930-
*lydctx_p = (struct lyd_ctx *)lydctx;
1931-
19322051
/* the JSON context is no more needed, freeing it also stops logging line numbers which would be confusing now */
19332052
lyjson_ctx_free(lydctx->jsonctx);
19342053
lydctx->jsonctx = NULL;
2054+
2055+
/* set optional lydctx pointer, otherwise free */
2056+
if (lydctx_p) {
2057+
*lydctx_p = (struct lyd_ctx *)lydctx;
2058+
} else {
2059+
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
2060+
}
19352061
}
19362062
return rc;
19372063
}
@@ -2020,7 +2146,7 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance
20202146
assert(!(parse_opts & LYD_PARSE_SUBTREE));
20212147

20222148
/* init context */
2023-
rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx);
2149+
rc = lyd_parse_json_init(ctx, NULL, in, parse_opts, val_opts, NULL, &lydctx);
20242150
LY_CHECK_GOTO(rc, cleanup);
20252151
lydctx->ext = ext;
20262152

0 commit comments

Comments
 (0)