diff --git a/src/parser_data.h b/src/parser_data.h index d13fd57b0..9d79556da 100644 --- a/src/parser_data.h +++ b/src/parser_data.h @@ -176,6 +176,8 @@ struct ly_in; be checked (length, range, pattern, ...) and if a value can be stored, it is. Calling separate validation on these data always checks all the restrictions as well. */ +#define LYD_PARSE_JSON_NULL 0x4000000 /**< Allow using JSON empty value 'null' within JSON input. By default such value + is not supported and according to RFC 7951 '[null]' shall be used instead. */ #define LYD_PARSE_OPTS_MASK 0xFFFF0000 /**< Mask for all the LYD_PARSE_ options. */ diff --git a/src/parser_json.c b/src/parser_json.c index 95d689a76..6facc07ab 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -1492,7 +1492,10 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str if (r == LY_SUCCESS) { assert(snode->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER | LYD_NODE_ANY)); if (snode->nodetype & LYD_NODE_TERM) { - if ((*status != LYJSON_ARRAY) && (*status != LYJSON_NUMBER) && (*status != LYJSON_STRING) && + if ((lydctx->parse_opts & LYD_PARSE_JSON_NULL) && (*status == LYJSON_NULL)) { + /* do not do anything if value is JSON 'null' */ + goto cleanup; + } else if ((*status != LYJSON_ARRAY) && (*status != LYJSON_NUMBER) && (*status != LYJSON_STRING) && (*status != LYJSON_FALSE) && (*status != LYJSON_TRUE) && (*status != LYJSON_NULL)) { rc = LY_ENOT; goto cleanup; @@ -1519,6 +1522,10 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str r = lydjson_parse_instance_inner(lydctx, snode, ext, status, node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } else { + if ((lydctx->parse_opts & LYD_PARSE_JSON_NULL) && (*status == LYJSON_NULL)) { + /* do not do anything if value is JSON 'null' */ + goto cleanup; + } /* create any node */ r = lydjson_parse_any(lydctx, snode, ext, status, node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c index bebab7b3e..f7311617f 100644 --- a/tests/utests/data/test_parser_json.c +++ b/tests/utests/data/test_parser_json.c @@ -163,6 +163,11 @@ test_leaf(void **state) data = "{\"@a:foo\":{\"a:hi\\nt\":1},\"a:foo\":\"xxx\"}"; assert_int_equal(LY_EINVAL, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree)); CHECK_LOG_CTX("Annotation definition for attribute \"a:hi\nt\" not found.", "/@a:foo/@a:hi\nt", 1); + + data = "{\"a:foo\": null}"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "Invalid non-string-encoded string value \"\".", "/a:foo", 1); + CHECK_PARSE_LYD(data, LYD_PARSE_JSON_NULL, LYD_VALIDATE_PRESENT, tree); + assert_null(tree); } static void @@ -291,6 +296,11 @@ test_anydata(void **state) 1, LYS_ANYDATA, 0, 0, NULL, 0); CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); lyd_free_all(tree); + + data = "{\"a:any\": null}"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "Expecting JSON name/object but anydata \"any\" is represented in input data as name/null.", NULL, 1); + CHECK_PARSE_LYD(data, LYD_PARSE_JSON_NULL, LYD_VALIDATE_PRESENT, tree); + assert_null(tree); } static void diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c index 7572248be..9bdb8d96e 100644 --- a/tools/lint/main_ni.c +++ b/tools/lint/main_ni.c @@ -211,6 +211,9 @@ help(int shortout) printf(" -X, --extended-leafref\n" " Allow usage of deref() XPath function within leafref\n\n"); + printf(" -J, --json-null\n" + " Allow usage of JSON empty values ('null') within input data\n\n"); + printf(" -G GROUPS, --debug=GROUPS\n" #ifndef NDEBUG " Enable printing of specific debugging message group\n" @@ -463,6 +466,7 @@ fill_context(int argc, char *argv[], struct yl_opt *yo, struct ly_ctx **ctx) {"yang-library", no_argument, NULL, 'y'}, {"yang-library-file", required_argument, NULL, 'Y'}, {"extended-leafref", no_argument, NULL, 'X'}, + {"json-null", no_argument, NULL, 'J'}, {"debug", required_argument, NULL, 'G'}, {NULL, 0, NULL, 0} }; @@ -474,7 +478,7 @@ fill_context(int argc, char *argv[], struct yl_opt *yo, struct ly_ctx **ctx) yo->line_length = 0; opterr = 0; - while ((opt = getopt_long(argc, argv, "hvVQf:I:p:DF:iP:qs:neE:t:d:lL:o:O:R:myY:Xx:G:", options, &opt_index)) != -1) { + while ((opt = getopt_long(argc, argv, "hvVQf:I:p:DF:iP:qs:neE:t:d:lL:o:O:R:myY:XJx:G:", options, &opt_index)) != -1) { switch (opt) { case 'h': /* --help */ help(0); @@ -654,6 +658,10 @@ fill_context(int argc, char *argv[], struct yl_opt *yo, struct ly_ctx **ctx) yo->ctx_options |= LY_CTX_LEAFREF_EXTENDED; break; + case 'J': /* --json-null */ + yo->data_parse_options |= LYD_PARSE_JSON_NULL; + break; + case 'G': /* --debug */ if (set_debug_groups(optarg, yo)) { return -1;