xml parser & printer FEATURE support for special filter attrs
Fixes #1619
diff --git a/src/parser_xml.c b/src/parser_xml.c
index a827ab7..fd2f856 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -67,30 +67,60 @@
free(ctx);
}
+/**
+ * @brief Parse and create XML metadata.
+ *
+ * @param[in] lydctx XML data parser context.
+ * @param[in] parent_exts Extension instances of the parent node.
+ * @param[out] meta List of created metadata instances.
+ * @return LY_ERR value.
+ */
static LY_ERR
-lydxml_metadata(struct lyd_xml_ctx *lydctx, struct lyd_meta **meta)
+lydxml_metadata(struct lyd_xml_ctx *lydctx, struct lysc_ext_instance *parent_exts, struct lyd_meta **meta)
{
LY_ERR ret = LY_SUCCESS;
const struct lyxml_ns *ns;
struct lys_module *mod;
const char *name;
size_t name_len;
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool filter_attrs = 0;
struct lyxml_ctx *xmlctx = lydctx->xmlctx;
*meta = NULL;
+ /* check for NETCONF filter unqualified attributes */
+ LY_ARRAY_FOR(parent_exts, u) {
+ if (!strcmp(parent_exts[u].def->name, "get-filter-element-attributes") &&
+ !strcmp(parent_exts[u].def->module->name, "ietf-netconf")) {
+ filter_attrs = 1;
+ break;
+ }
+ }
+
while (xmlctx->status == LYXML_ATTRIBUTE) {
if (!xmlctx->prefix_len) {
- /* in XML, all attributes must be prefixed
- * TODO exception for NETCONF filters which are supposed to map to the ietf-netconf without prefix */
+ /* in XML all attributes must be prefixed except NETCONF filter ones marked by an extension */
+ if (filter_attrs && (!ly_strncmp("type", xmlctx->name, xmlctx->name_len) ||
+ !ly_strncmp("select", xmlctx->name, xmlctx->name_len))) {
+ mod = ly_ctx_get_module_implemented(xmlctx->ctx, "ietf-netconf");
+ if (!mod) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE,
+ "Missing (or not implemented) YANG module \"ietf-netconf\" for special filter attributes.");
+ ret = LY_ENOTFOUND;
+ goto cleanup;
+ }
+ goto create_meta;
+ }
+
if (lydctx->parse_opts & LYD_PARSE_STRICT) {
- ret = LY_EVALID;
LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Missing mandatory prefix for XML metadata \"%.*s\".",
(int)xmlctx->name_len, xmlctx->name);
+ ret = LY_EVALID;
goto cleanup;
}
-skip_attr:
+ /* skip attr */
LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
assert(xmlctx->status == LYXML_ATTR_CONTENT);
LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
@@ -100,25 +130,32 @@
/* get namespace of the attribute to find its annotation definition */
ns = lyxml_ns_get(&xmlctx->ns, xmlctx->prefix, xmlctx->prefix_len);
if (!ns) {
- ret = LY_ENOTFOUND;
/* unknown namespace, XML error */
LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", (int)xmlctx->prefix_len, xmlctx->prefix);
+ ret = LY_ENOTFOUND;
goto cleanup;
}
+
+ /* get the module with metadata definition */
mod = ly_ctx_get_module_implemented_ns(xmlctx->ctx, ns->uri);
if (!mod) {
- /* module is not implemented or not present in the schema */
if (lydctx->parse_opts & LYD_PARSE_STRICT) {
- ret = LY_ENOTFOUND;
LOGVAL(xmlctx->ctx, LYVE_REFERENCE,
"Unknown (or not implemented) YANG module with namespace \"%s\" for metadata \"%.*s%s%.*s\".",
ns->uri, (int)xmlctx->prefix_len, xmlctx->prefix, xmlctx->prefix_len ? ":" : "",
(int)xmlctx->name_len, xmlctx->name);
+ ret = LY_ENOTFOUND;
goto cleanup;
}
- goto skip_attr;
+
+ /* skip attr */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ assert(xmlctx->status == LYXML_ATTR_CONTENT);
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ continue;
}
+create_meta:
/* remember meta name and get its content */
name = xmlctx->name;
name_len = xmlctx->name_len;
@@ -489,7 +526,7 @@
/* create metadata/attributes */
if (xmlctx->status == LYXML_ATTRIBUTE) {
if (snode) {
- ret = lydxml_metadata(lydctx, &meta);
+ ret = lydxml_metadata(lydctx, snode->exts, &meta);
LY_CHECK_GOTO(ret, error);
} else {
assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
diff --git a/src/printer_xml.c b/src/printer_xml.c
index 32114f5..c6f5341 100644
--- a/src/printer_xml.c
+++ b/src/printer_xml.c
@@ -166,16 +166,8 @@
struct lyd_meta *meta;
const struct lys_module *mod;
struct ly_set ns_list = {0};
-
-#if 0
- const char **prefs, **nss;
- const char *xml_expr = NULL, *mod_name;
- uint32_t ns_count, i;
- ly_bool rpc_filter = 0;
- char *p;
- size_t len;
-#endif
- ly_bool dynamic;
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool dynamic, filter_attrs = 0;
/* with-defaults */
if (node->schema->nodetype & LYD_NODE_TERM) {
@@ -188,50 +180,36 @@
}
}
}
-#if 0
- /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
- if (!strcmp(node->schema->name, "filter") &&
- (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
- rpc_filter = 1;
+
+ /* check for NETCONF filter unqualified attributes */
+ LY_ARRAY_FOR(node->schema->exts, u) {
+ if (!strcmp(node->schema->exts[u].def->name, "get-filter-element-attributes") &&
+ !strcmp(node->schema->exts[u].def->module->name, "ietf-netconf")) {
+ filter_attrs = 1;
+ break;
+ }
}
-#endif
+
for (meta = node->meta; meta; meta = meta->next) {
const char *value = meta->value.realtype->plugin->print(LYD_CTX(node), &meta->value, LY_VALUE_XML, &ns_list,
&dynamic, NULL);
/* print namespaces connected with the value's prefixes */
- for (uint32_t u = 0; u < ns_list.count; ++u) {
- mod = (const struct lys_module *)ns_list.objs[u];
+ for (uint32_t i = 0; i < ns_list.count; ++i) {
+ mod = ns_list.objs[i];
xml_print_ns(ctx, mod->ns, mod->prefix, 1);
}
ly_set_erase(&ns_list, NULL);
-#if 0
- if (rpc_filter) {
- /* exception for NETCONF's filter's attributes */
- if (!strcmp(meta->name, "select")) {
- /* xpath content, we have to convert the JSON format into XML first */
- xml_expr = transform_json2xml(node->schema->module, meta->value_str, 0, &prefs, &nss, &ns_count);
- if (!xml_expr) {
- /* error */
- return EXIT_FAILURE;
- }
-
- for (i = 0; i < ns_count; ++i) {
- ly_print_(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
- }
- free(prefs);
- free(nss);
- }
- ly_print_(out, " %s=\"", meta->name);
- } else {
-#endif
- /* print the metadata with its namespace */
mod = meta->annotation->module;
- ly_print_(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
-#if 0
- }
-#endif
+ if (filter_attrs && !strcmp(mod->name, "ietf-netconf") && (!strcmp(meta->name, "type") ||
+ !strcmp(meta->name, "select"))) {
+ /* print special NETCONF filter unqualified attributes */
+ ly_print_(ctx->out, " %s=\"", meta->name);
+ } else {
+ /* print the metadata with its namespace */
+ ly_print_(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
+ }
/* print metadata value */
if (value && value[0]) {
diff --git a/tests/utests/data/test_parser_xml.c b/tests/utests/data/test_parser_xml.c
index 1e74bb9..4183799 100644
--- a/tests/utests/data/test_parser_xml.c
+++ b/tests/utests/data/test_parser_xml.c
@@ -734,6 +734,44 @@
}
static void
+test_filter_attributes(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree;
+ const struct lyd_node *node;
+ const char *dsc;
+ const char *ref = "RFC 6241, Section 7.7";
+ const char *feats[] = {"writable-running", NULL};
+
+ assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", "2011-06-01", feats)));
+
+ data = "<get xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <filter type=\"xpath\" select=\"/*\"/>\n"
+ "</get>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, NULL));
+ ly_in_free(in, 0);
+ assert_non_null(tree);
+
+ node = tree;
+ dsc = "Retrieve running configuration and device state information.";
+ CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "get", LYS_RPC,
+ 1, 0, 0, 0, 0, ref, 0);
+ node = lyd_child(node);
+ dsc = "This parameter specifies the portion of the system\nconfiguration and state data to retrieve.";
+ CHECK_LYSC_NODE(node->schema, dsc, 1, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "filter", 0, LYS_ANYXML, 1, 0, NULL, 0);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS,
+ "<get xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <filter type=\"xpath\" select=\"/*\"/>\n"
+ "</get>\n");
+
+ lyd_free_all(tree);
+}
+
+static void
test_data_skip(void **state)
{
const char *data;
@@ -775,6 +813,7 @@
UTEST(test_netconf_rpc, setup),
UTEST(test_netconf_action, setup),
UTEST(test_netconf_reply_or_notification, setup),
+ UTEST(test_filter_attributes, setup),
UTEST(test_data_skip, setup),
};