parser json UPDATE optional JSON 'null' value parsed and ignored (#2216)

This patch introduces parsing flag, which allows user to use JSON 'null'
value with leaf, leaf-list and anydata
diff --git a/src/parser_data.h b/src/parser_data.h
index d13fd57..9d79556 100644
--- a/src/parser_data.h
+++ b/src/parser_data.h
@@ -176,6 +176,8 @@
                                                  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 95d689a..6facc07 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1492,7 +1492,10 @@
     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 @@
             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 bebab7b..f731161 100644
--- a/tests/utests/data/test_parser_json.c
+++ b/tests/utests/data/test_parser_json.c
@@ -163,6 +163,11 @@
     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 @@
             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 7572248..9bdb8d9 100644
--- a/tools/lint/main_ni.c
+++ b/tools/lint/main_ni.c
@@ -211,6 +211,9 @@
     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 @@
         {"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 @@
     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 @@
             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;