diff --git a/src/parser_data.h b/src/parser_data.h
index 2d0c1aa36..797fdb389 100644
--- a/src/parser_data.h
+++ b/src/parser_data.h
@@ -195,6 +195,10 @@ struct ly_in;
format according to RFC 7951 based on their type. Using this
option the validation can be softened to accept boolean and
number type values enclosed in quotes. */
+#define LYD_PARSE_ANYDATA_STRICT 0x10000000 /**< Apply strict parsing (::LYD_PARSE_STRICT) also to anydata
+ content. By default, unknown elements in anydata are parsed
+ as opaque nodes. With this flag, an error is raised for any unknown
+ elements within anydata/anyxml subtrees. */
#define LYD_PARSE_OPTS_MASK 0xFFFF0000 /**< Mask for all the LYD_PARSE_ options. */
/** @} dataparseroptions */
diff --git a/src/parser_json.c b/src/parser_json.c
index 689609a37..5f8a3e043 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1310,7 +1310,11 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st
/* parse any data tree with correct options, first backup the current options and then make the parser
* process data as opaq nodes */
- lydctx->parse_opts &= ~LYD_PARSE_STRICT;
+ if (lydctx->parse_opts & LYD_PARSE_ANYDATA_STRICT) {
+ lydctx->parse_opts |= LYD_PARSE_STRICT;
+ } else {
+ lydctx->parse_opts &= ~LYD_PARSE_STRICT;
+ }
lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
lydctx->any_schema = snode;
diff --git a/src/parser_xml.c b/src/parser_xml.c
index dd704680d..f33cf631e 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -954,8 +954,12 @@ lydxml_subtree_any(struct lyd_xml_ctx *lydctx, const struct lysc_node *snode, co
r = lyxml_ctx_next(xmlctx);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
- /* update options so that generic data can be parsed */
- lydctx->parse_opts &= ~LYD_PARSE_STRICT;
+ if (lydctx->parse_opts & LYD_PARSE_ANYDATA_STRICT) {
+ lydctx->parse_opts |= LYD_PARSE_STRICT;
+ } else {
+ /* update options so that generic data can be parsed */
+ lydctx->parse_opts &= ~LYD_PARSE_STRICT;
+ }
lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c
index bfec8f3ed..ea432c610 100644
--- a/tests/utests/data/test_parser_json.c
+++ b/tests/utests/data/test_parser_json.c
@@ -376,6 +376,35 @@ test_anydata(void **state)
assert_null(tree);
}
+static void
+test_anydata_strict_validation(void **state)
+{
+ const char *data_without_schema;
+ const char *data_invalid;
+ const char *data_valid;
+ struct lyd_node *tree;
+
+ // no shcema defiend for "x" in the parsing context
+ data_without_schema = "{\"a:any\":{\"x:element1\":{\"element2\":\"/a:some/a:path\",\"list\":[{},{\"key\":\"a\"}]}}}";
+
+ PARSER_CHECK_ERROR(data_without_schema, LYD_PARSE_ANYDATA_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "No module named \"x\" in the context.", "/a:any", 1);
+
+ data_invalid = "{\"a:any\":{\"a:fooA\":{\"element2\":\"/a:some/a:path\",\"list\":[{},{\"key\":\"a\"}]}}}";
+
+ PARSER_CHECK_ERROR(data_invalid, LYD_PARSE_ANYDATA_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Node \"fooA\" not found in the \"a\" module.", "/a:any", 1);
+
+ data_valid = "{\"a:any\":{\"foo\":\"default-val\"}}";
+ CHECK_PARSE_LYD(data_valid, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_R | LYS_SET_CONFIG, 1, "any",
+ 1, LYS_ANYDATA, 0, 0, NULL, 0);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_SIBLINGS, data_valid);
+ lyd_free_all(tree);
+}
+
static void
test_anyxml(void **state)
{
@@ -1031,6 +1060,7 @@ main(void)
UTEST(test_leaf, setup),
UTEST(test_leaflist, setup),
UTEST(test_anydata, setup),
+ UTEST(test_anydata_strict_validation, setup),
UTEST(test_anyxml, setup),
UTEST(test_list, setup),
UTEST(test_container, setup),
diff --git a/tests/utests/data/test_parser_xml.c b/tests/utests/data/test_parser_xml.c
index 3f40b6d0b..b6ce7edf8 100644
--- a/tests/utests/data/test_parser_xml.c
+++ b/tests/utests/data/test_parser_xml.c
@@ -159,6 +159,61 @@ test_anydata(void **state)
lyd_free_all(tree);
}
+static void
+test_anydata_strict_validation(void **state)
+{
+ const char *data_without_schema;
+ const char *data_invalid;
+ const char *data_valid;
+ char *str;
+ struct lyd_node *tree;
+
+ // no shcema defiend for "urn:tests:no:schema" in the parsing context
+ data_without_schema = "\n"
+ " \n"
+ " default-val\n"
+ " \n"
+ "\n";
+
+ PARSER_CHECK_ERROR(data_without_schema, LYD_PARSE_ANYDATA_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "No module with namespace \"urn:tests:no:schema\" in the context.", "/a:any", 3);
+
+ // anydata value are based on "module a" defined in the setup function and loaded into the parsing context.
+ // However, the value passed in the anydata subtree is not defined in "module a".
+ data_invalid = "\n"
+ " default-val\n"
+ "\n";
+
+ PARSER_CHECK_ERROR(data_invalid, LYD_PARSE_ANYDATA_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
+ "Node \"element1\" not found in the \"a\" module.", "/a:any", 2);
+
+ // anydata value are based on "module a" defined in the setup function and loaded into the parsing context
+ data_valid = "\n"
+ " default-val\n"
+ "\n";
+
+ CHECK_PARSE_LYD(data_valid, LYD_PARSE_ANYDATA_STRICT, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+ CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_SET_CONFIG, 1, "any",
+ 1, LYS_ANYDATA, 0, 0, NULL, 0);
+
+ const char *data_expected =
+ "\n"
+ " default-val\n"
+ "\n";
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_SIBLINGS, data_expected);
+
+ assert_int_equal(LY_SUCCESS, lyd_any_value_str(tree, &str));
+ lyd_free_all(tree);
+
+ assert_int_equal(LY_SUCCESS, lyd_new_path2(NULL, UTEST_LYCTX, "/a:any", str, strlen(str), LYD_ANYDATA_XML, 0, &tree, NULL));
+ free(str);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SIBLINGS, data_expected);
+ lyd_free_all(tree);
+}
+
static void
test_anyxml(void **state)
{
@@ -1042,6 +1097,7 @@ main(void)
const struct CMUnitTest tests[] = {
UTEST(test_leaf, setup),
UTEST(test_anydata, setup),
+ UTEST(test_anydata_strict_validation, setup),
UTEST(test_anyxml, setup),
UTEST(test_list, setup),
UTEST(test_container, setup),
diff --git a/tools/lint/cmd_data.c b/tools/lint/cmd_data.c
index 69b4dd3c9..e3208fb0a 100644
--- a/tools/lint/cmd_data.c
+++ b/tools/lint/cmd_data.c
@@ -146,7 +146,9 @@ cmd_data_help(void)
" is supposed to contain the source 'nc-rpc' operation of the reply.\n"
" -k, --ext-inst \n"
" Name of extension instance in format:\n"
- " ::\n");
+ " ::\n"
+ " -A, --anydata-strict\n"
+ " Enable strict parsing of anydata content\n");
cmd_data_help_format();
cmd_data_help_in_format();
printf(" -o OUTFILE, --output=OUTFILE\n"
@@ -161,19 +163,20 @@ cmd_data_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
- {"defaults", required_argument, NULL, 'd'},
- {"present", no_argument, NULL, 'e'},
- {"format", required_argument, NULL, 'f'},
- {"in-format", required_argument, NULL, 'F'},
- {"help", no_argument, NULL, 'h'},
- {"merge", no_argument, NULL, 'm'},
- {"output", required_argument, NULL, 'o'},
- {"operational", required_argument, NULL, 'O'},
- {"reply-rpc", required_argument, NULL, 'R'},
- {"not-strict", no_argument, NULL, 'n'},
- {"type", required_argument, NULL, 't'},
- {"xpath", required_argument, NULL, 'x'},
- {"ext-inst", required_argument, NULL, 'k'},
+ {"defaults", required_argument, NULL, 'd'},
+ {"present", no_argument, NULL, 'e'},
+ {"format", required_argument, NULL, 'f'},
+ {"in-format", required_argument, NULL, 'F'},
+ {"help", no_argument, NULL, 'h'},
+ {"merge", no_argument, NULL, 'm'},
+ {"output", required_argument, NULL, 'o'},
+ {"operational", required_argument, NULL, 'O'},
+ {"reply-rpc", required_argument, NULL, 'R'},
+ {"not-strict", no_argument, NULL, 'n'},
+ {"anydata-strict", no_argument, NULL, 'A'},
+ {"type", required_argument, NULL, 't'},
+ {"xpath", required_argument, NULL, 'x'},
+ {"ext-inst", required_argument, NULL, 'k'},
{NULL, 0, NULL, 0}
};
@@ -242,6 +245,10 @@ cmd_data_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
case 'n': /* --not-strict */
yo->data_parse_options &= ~LYD_PARSE_STRICT;
break;
+ case 'A': /* --anydata-strict */
+ yo->data_parse_options |= LYD_PARSE_ANYDATA_STRICT;
+ break;
+
case 't': /* --type */
if (data_type_set) {
YLMSG_E("The data type (-t) cannot be set multiple times.");
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
index 0725455c4..74c6b3d1c 100644
--- a/tools/lint/main_ni.c
+++ b/tools/lint/main_ni.c
@@ -128,6 +128,9 @@ help(int shortout)
" Do not require strict data parsing (silently skip unknown data),\n"
" has no effect for schemas.\n\n");
+ printf(" -A, --anydata-strict\n"
+ " Enable strict parsing of anydata content.\n\n");
+
printf(" -e, --present Validate only with the schema modules whose data actually\n"
" exist in the provided input data files. Takes effect only\n"
" with the 'data' or 'config' TYPEs. Used to avoid requiring\n"
@@ -486,6 +489,7 @@ process_args(int argc, char *argv[], struct yl_opt *yo, struct ly_ctx **ctx)
{"submodule", required_argument, NULL, 's'},
{"ext-data", required_argument, NULL, 'x'},
{"not-strict", no_argument, NULL, 'n'},
+ {"anydata-strict", no_argument, NULL, 'A'},
{"present", no_argument, NULL, 'e'},
{"type", required_argument, NULL, 't'},
{"default", required_argument, NULL, 'd'},
@@ -511,7 +515,7 @@ process_args(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:XJx:G:k:", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, argv, "hvVQf:I:p:DF:iP:qs:neEA:t:d:lL:o:O:R:myY:XJx:G:k:", options, &opt_index)) != -1) {
switch (opt) {
case 'h': /* --help */
help(0);
@@ -604,6 +608,10 @@ process_args(int argc, char *argv[], struct yl_opt *yo, struct ly_ctx **ctx)
yo->data_parse_options &= ~LYD_PARSE_STRICT;
break;
+ case 'A': /* --anydata-strict */
+ yo->data_parse_options |= LYD_PARSE_ANYDATA_STRICT;
+ break;
+
case 'e': /* --present */
yo->data_validate_options |= LYD_VALIDATE_PRESENT;
break;