* Added nsc parameter to xml2xpath() and ensured the xpath uses prefixes.
* Old code: add `NULL` as second parameter
This commit is contained in:
parent
82a0670031
commit
614c927343
6 changed files with 157 additions and 13 deletions
|
|
@ -136,6 +136,8 @@ Users may have to change how they access the system
|
||||||
|
|
||||||
Developers may need to change their code
|
Developers may need to change their code
|
||||||
|
|
||||||
|
* Added `nsc` parameter to `xml2xpath()` and ensured the xpath uses prefixes.
|
||||||
|
* Old code: add `NULL` as second parameter
|
||||||
* Added `eof` parameter to `clicon_rpc()` and `clicon_rpc1()` and error handling modified
|
* Added `eof` parameter to `clicon_rpc()` and `clicon_rpc1()` and error handling modified
|
||||||
|
|
||||||
## 5.6.0
|
## 5.6.0
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ int xml_nopresence_default(cxobj *xt);
|
||||||
int xml_nopresence_default_mark(cxobj *x, void *arg);
|
int xml_nopresence_default_mark(cxobj *x, void *arg);
|
||||||
int xml_sanity(cxobj *x, void *arg);
|
int xml_sanity(cxobj *x, void *arg);
|
||||||
int xml_non_config_data(cxobj *xt, cxobj **xerr);
|
int xml_non_config_data(cxobj *xt, cxobj **xerr);
|
||||||
int xml2xpath(cxobj *x, char **xpath);
|
int xml2xpath(cxobj *x, cvec *nsc, char **xpath);
|
||||||
int assign_namespace_element(cxobj *x0, cxobj *x1, cxobj *x1p);
|
int assign_namespace_element(cxobj *x0, cxobj *x1, cxobj *x1p);
|
||||||
int assign_namespace_body(cxobj *x0, cxobj *x1);
|
int assign_namespace_body(cxobj *x0, cxobj *x1);
|
||||||
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);
|
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);
|
||||||
|
|
|
||||||
|
|
@ -1375,7 +1375,7 @@ netconf_minmax_elements_xml(cxobj **xret,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (xml_parent(xp)){ /* Dont include root, eg <config> */
|
if (xml_parent(xp)){ /* Dont include root, eg <config> */
|
||||||
if (xml2xpath(xp, &path) < 0)
|
if (xml2xpath(xp, NULL, &path) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (path)
|
if (path)
|
||||||
cprintf(cb, "%s", path);
|
cprintf(cb, "%s", path);
|
||||||
|
|
|
||||||
|
|
@ -1291,12 +1291,16 @@ xml_non_config_data(cxobj *xt,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Given an XML node, build an xpath to root, internal function
|
/*! Given an XML node, build an xpath recursively to root, internal function
|
||||||
|
* @param[in] x XML object
|
||||||
|
* @param[in] nsc Namespace context
|
||||||
|
* @param[out] cb XPath string as cbuf.
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error. eg XML malformed
|
* @retval -1 Error. eg XML malformed
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
xml2xpath1(cxobj *x,
|
xml2xpath1(cxobj *x,
|
||||||
|
cvec *nsc,
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -1309,12 +1313,30 @@ xml2xpath1(cxobj *x,
|
||||||
cxobj *xb;
|
cxobj *xb;
|
||||||
char *b;
|
char *b;
|
||||||
enum rfc_6020 keyword;
|
enum rfc_6020 keyword;
|
||||||
|
char *prefix = NULL;
|
||||||
|
char *namespace;
|
||||||
|
|
||||||
if ((xp = xml_parent(x)) == NULL)
|
if ((xp = xml_parent(x)) == NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
xml2xpath1(xp, cb);
|
if (xml2xpath1(xp, nsc, cb) < 0)
|
||||||
|
goto done;
|
||||||
|
if (nsc){
|
||||||
|
if (xml2ns(x, xml_prefix(x), &namespace) < 0)
|
||||||
|
goto done;
|
||||||
|
if (namespace){
|
||||||
|
if (xml_nsctx_get_prefix(nsc, namespace, &prefix) == 0)
|
||||||
|
; /* maybe NULL? */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
prefix = xml_prefix(x); /* maybe NULL? */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
prefix = xml_prefix(x);
|
||||||
/* XXX: sometimes there should be a /, sometimes not */
|
/* XXX: sometimes there should be a /, sometimes not */
|
||||||
cprintf(cb, "/%s", xml_name(x));
|
cprintf(cb, "/");
|
||||||
|
if (prefix)
|
||||||
|
cprintf(cb, "%s:", prefix);
|
||||||
|
cprintf(cb, "%s", xml_name(x));
|
||||||
if ((y = xml_spec(x)) != NULL){
|
if ((y = xml_spec(x)) != NULL){
|
||||||
keyword = yang_keyword_get(y);
|
keyword = yang_keyword_get(y);
|
||||||
switch (keyword){
|
switch (keyword){
|
||||||
|
|
@ -1334,7 +1356,10 @@ xml2xpath1(cxobj *x,
|
||||||
if ((xb = xml_find(x, keyname)) == NULL)
|
if ((xb = xml_find(x, keyname)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
b = xml_body(xb);
|
b = xml_body(xb);
|
||||||
cprintf(cb, "[%s=\"%s\"]", keyname, b?b:"");
|
cprintf(cb, "[");
|
||||||
|
if (prefix)
|
||||||
|
cprintf(cb, "%s:", prefix);
|
||||||
|
cprintf(cb, "%s=\"%s\"]", keyname, b?b:"");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -1348,8 +1373,14 @@ xml2xpath1(cxobj *x,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Given an XML node, build an xpath to root
|
/*! Given an XML node, build an xpath to root
|
||||||
* Builds only unqualified xpaths, ie no predicates []
|
*
|
||||||
|
* Creates an XPath from an XML node with some limitations, see notes below.
|
||||||
|
* The prefixes used are from the given namespace context if any, otherwise the native prefixes are used, if any.
|
||||||
|
* Note that this means that prefixes may be translated such as if the XML namespace mapping is different than the once used
|
||||||
|
* in the XML.
|
||||||
|
* Therefore, if nsc is "canonical", the returned xpath is also "canonical", even though the XML is not.
|
||||||
* @param[in] x XML object
|
* @param[in] x XML object
|
||||||
|
* @param[in] nsc Namespace context
|
||||||
* @param[out] xpath Malloced xpath string. Need to free() after use
|
* @param[out] xpath Malloced xpath string. Need to free() after use
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error. (eg XML malformed)
|
* @retval -1 Error. (eg XML malformed)
|
||||||
|
|
@ -1357,6 +1388,7 @@ xml2xpath1(cxobj *x,
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml2xpath(cxobj *x,
|
xml2xpath(cxobj *x,
|
||||||
|
cvec *nsc,
|
||||||
char **xpathp)
|
char **xpathp)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -1367,7 +1399,7 @@ xml2xpath(cxobj *x,
|
||||||
clicon_err(OE_XML, errno, "cbuf_new");
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (xml2xpath1(x, cb) < 0)
|
if (xml2xpath1(x, nsc, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* XXX: see xpath in test statement,.. */
|
/* XXX: see xpath in test statement,.. */
|
||||||
xpath = cbuf_get(cb);
|
xpath = cbuf_get(cb);
|
||||||
|
|
|
||||||
91
test/test_xpath_inverse.sh
Executable file
91
test/test_xpath_inverse.sh
Executable file
|
|
@ -0,0 +1,91 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Test xpath inverse function.
|
||||||
|
# That is, given an xml + xpath -> specific node x in xml -> xml2xpath(x)
|
||||||
|
|
||||||
|
# Magic line must be first in script (see README.md)
|
||||||
|
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
||||||
|
: ${clixon_util_xpath:=clixon_util_xpath}
|
||||||
|
|
||||||
|
ydir=$dir/yang
|
||||||
|
xml1=$dir/xml1.xml
|
||||||
|
xml2=$dir/xml2.xml
|
||||||
|
|
||||||
|
if [ ! -d $ydir ]; then
|
||||||
|
mkdir $ydir
|
||||||
|
fi
|
||||||
|
|
||||||
|
# canonical namespace xpath tests
|
||||||
|
# need yang modules
|
||||||
|
cat <<EOF > $ydir/a.yang
|
||||||
|
module a{
|
||||||
|
namespace "urn:example:a";
|
||||||
|
prefix a;
|
||||||
|
container x{
|
||||||
|
leaf y{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
list z {
|
||||||
|
key k;
|
||||||
|
leaf k{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Default prefix
|
||||||
|
cat <<EOF > $xml1
|
||||||
|
<x xmlns="urn:example:a">
|
||||||
|
<y>foo</y>
|
||||||
|
<z>
|
||||||
|
<k>1</k>
|
||||||
|
</z>
|
||||||
|
<z>
|
||||||
|
<k>2</k>
|
||||||
|
</z>
|
||||||
|
</x>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Explicit indexes
|
||||||
|
cat <<EOF > $xml2
|
||||||
|
<a:x xmlns:a="urn:example:a">
|
||||||
|
<a:y>foo</a:y>
|
||||||
|
<a:z>
|
||||||
|
<a:k>1</a:k>
|
||||||
|
</a:z>
|
||||||
|
<a:z>
|
||||||
|
<a:k>2</a:k>
|
||||||
|
</a:z>
|
||||||
|
</a:x>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
new "xpath leaf default ns"
|
||||||
|
expectpart "$($clixon_util_xpath -If $xml1 -y $ydir -p /x/y)" 0 'Inverse: /x/y'
|
||||||
|
|
||||||
|
new "xpath leaf explicit prefix"
|
||||||
|
expectpart "$($clixon_util_xpath -If $xml2 -y $ydir -p /a:x/a:y)" 0 'Inverse: /a:x/a:y'
|
||||||
|
|
||||||
|
new "xpath leaf explicit prefix"
|
||||||
|
expectpart "$($clixon_util_xpath -If $xml2 -y $ydir -p /a:x/a:y -n a:urn:example:a)" 0 'Inverse: /a:x/a:y'
|
||||||
|
|
||||||
|
new "xpath leaf no prefix"
|
||||||
|
expectpart "$($clixon_util_xpath -If $xml1 -y $ydir -p /x/y -n a:urn:example:a)" 0 'Inverse: /a:x/a:y'
|
||||||
|
|
||||||
|
new "xpath leaf other nsc"
|
||||||
|
expectpart "$($clixon_util_xpath -If $xml1 -y $ydir -p /a:x/a:y -n b:urn:example:a)" 0 'Inverse: /b:x/b:y' --not-- /a:x/a:y
|
||||||
|
|
||||||
|
new "xpath list same nsc"
|
||||||
|
expectpart "$($clixon_util_xpath -If $xml1 -y $ydir -p /a:x/a:z[a:k='2'] -n a:urn:example:a)" 0 'Inverse: /a:x/a:z\[a:k="2"\]'
|
||||||
|
|
||||||
|
new "xpath list same nsc"
|
||||||
|
expectpart "$($clixon_util_xpath -If $xml1 -y $ydir -p /a:x/a:z[a:k='2'] -n b:urn:example:a)" 0 'Inverse: /b:x/b:z\[b:k="2"\]' --not-- '/a:x/a:z'
|
||||||
|
|
||||||
|
rm -rf $dir
|
||||||
|
|
||||||
|
# unset conditional parameters
|
||||||
|
unset clixon_util_xpath
|
||||||
|
|
||||||
|
new "endtest"
|
||||||
|
endtest
|
||||||
|
|
@ -60,7 +60,7 @@ See https://www.w3.org/TR/xpath/
|
||||||
#include "clixon/clixon.h"
|
#include "clixon/clixon.h"
|
||||||
|
|
||||||
/* Command line options to be passed to getopt(3) */
|
/* Command line options to be passed to getopt(3) */
|
||||||
#define XPATH_OPTS "hD:f:p:i:n:cl:y:Y:"
|
#define XPATH_OPTS "hD:f:p:i:In:cl:y:Y:"
|
||||||
|
|
||||||
static int
|
static int
|
||||||
usage(char *argv0)
|
usage(char *argv0)
|
||||||
|
|
@ -72,6 +72,7 @@ usage(char *argv0)
|
||||||
"\t-f <file> \tXML file\n"
|
"\t-f <file> \tXML file\n"
|
||||||
"\t-p <xpath> \tPrimary XPATH string\n"
|
"\t-p <xpath> \tPrimary XPATH string\n"
|
||||||
"\t-i <xpath0>\t(optional) Initial XPATH string\n"
|
"\t-i <xpath0>\t(optional) Initial XPATH string\n"
|
||||||
|
"\t-I \t\tCheck inverse, map back xml result to xpath and check if equal\n"
|
||||||
"\t-n <pfx:id>\tNamespace binding (pfx=NULL for default)\n"
|
"\t-n <pfx:id>\tNamespace binding (pfx=NULL for default)\n"
|
||||||
"\t-c \t\tMap xpath to canonical form\n"
|
"\t-c \t\tMap xpath to canonical form\n"
|
||||||
"\t-l <s|e|o|f<file>> \tLog on (s)yslog, std(e)rr, std(o)ut or (f)ile (stderr is default)\n"
|
"\t-l <s|e|o|f<file>> \tLog on (s)yslog, std(e)rr, std(o)ut or (f)ile (stderr is default)\n"
|
||||||
|
|
@ -120,7 +121,6 @@ main(int argc,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *argv0 = argv[0];
|
char *argv0 = argv[0];
|
||||||
int i;
|
int i;
|
||||||
cxobj **xv = NULL;
|
|
||||||
cxobj *x0 = NULL;
|
cxobj *x0 = NULL;
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
int c;
|
int c;
|
||||||
|
|
@ -144,6 +144,7 @@ main(int argc,
|
||||||
cxobj *xerr = NULL; /* malloced must be freed */
|
cxobj *xerr = NULL; /* malloced must be freed */
|
||||||
int logdst = CLICON_LOG_STDERR;
|
int logdst = CLICON_LOG_STDERR;
|
||||||
int dbg = 0;
|
int dbg = 0;
|
||||||
|
int xpath_inverse = 0;
|
||||||
|
|
||||||
/* In the startup, logs to stderr & debug flag set later */
|
/* In the startup, logs to stderr & debug flag set later */
|
||||||
clicon_log_init("xpath", LOG_DEBUG, logdst);
|
clicon_log_init("xpath", LOG_DEBUG, logdst);
|
||||||
|
|
@ -180,6 +181,9 @@ main(int argc,
|
||||||
case 'i': /* Optional initial XPATH string */
|
case 'i': /* Optional initial XPATH string */
|
||||||
xpath0 = optarg;
|
xpath0 = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'I': /* Check inverse */
|
||||||
|
xpath_inverse++;
|
||||||
|
break;
|
||||||
case 'n':{ /* Namespace binding */
|
case 'n':{ /* Namespace binding */
|
||||||
char *prefix;
|
char *prefix;
|
||||||
char *id;
|
char *id;
|
||||||
|
|
@ -307,7 +311,7 @@ main(int argc,
|
||||||
*/
|
*/
|
||||||
if (clixon_xml_parse_file(fp, YB_NONE, NULL, &x0, NULL) < 0){
|
if (clixon_xml_parse_file(fp, YB_NONE, NULL, &x0, NULL) < 0){
|
||||||
fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason);
|
fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason);
|
||||||
return -1;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate XML as well */
|
/* Validate XML as well */
|
||||||
|
|
@ -361,6 +365,23 @@ main(int argc,
|
||||||
x = x0;
|
x = x0;
|
||||||
if (xpath_vec_ctx(x, nsc, xpath, 0, &xc) < 0)
|
if (xpath_vec_ctx(x, nsc, xpath, 0, &xc) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
/* Check inverse, eg XML back to xpath and compare with original, only if nodes */
|
||||||
|
if (xpath_inverse && xc->xc_type == XT_NODESET){
|
||||||
|
cxobj *xi;
|
||||||
|
char *xpathi = NULL;
|
||||||
|
for (i=0; i<xc->xc_size; i++){
|
||||||
|
xi = xc->xc_nodeset[i];
|
||||||
|
if (xml2xpath(xi, nsc, &xpathi) < 0)
|
||||||
|
goto done;
|
||||||
|
fprintf(stdout, "Inverse: %s\n", xpathi);
|
||||||
|
if (xpathi){
|
||||||
|
free(xpathi);
|
||||||
|
xpathi = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
|
||||||
/* Print results */
|
/* Print results */
|
||||||
cb = cbuf_new();
|
cb = cbuf_new();
|
||||||
ctx_print2(cb, xc);
|
ctx_print2(cb, xc);
|
||||||
|
|
@ -376,8 +397,6 @@ main(int argc,
|
||||||
ctx_free(xc);
|
ctx_free(xc);
|
||||||
if (xcfg)
|
if (xcfg)
|
||||||
xml_free(xcfg);
|
xml_free(xcfg);
|
||||||
if (xv)
|
|
||||||
free(xv);
|
|
||||||
if (buf)
|
if (buf)
|
||||||
free(buf);
|
free(buf);
|
||||||
if (x0)
|
if (x0)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue