I use xmlstarlet el -v
to display the structure of an xml file, including all the attributes and values. I would like to convert its output to some sort of key-valu
I decided to write a small program using libxml2, which parses the XML and recursively outputs it in the desired format.
The code can be compiled using the following command, or something similar (I decided to call it xmlkv, kv standing for key-value):
cc -o xmlkv xmlkv.c -s -Os -Wall -Wextra `xml2-config --cflags --libs`
The code:
#include
#include
#include
#include
static void print_elements(xmlNode *);
static void
print_elements(xmlNode *node)
{
xmlNode *n;
xmlAttrPtr a;
xmlChar *v, *p, *q;
for (n = node; n; n = n->next) {
if (n->type == XML_ELEMENT_NODE) {
if ((p = xmlGetNodePath(n)) == NULL)
return;
for (q = p; *q; q++)
if (*q == '/')
*q = '.';
for (a = n->properties; a; a = a->next) {
if ((v = xmlGetProp(n, a->name)) == NULL)
return;
printf("%s.%s=%s\n", p + 1, a->name, v);
xmlFree(v);
}
xmlFree(p);
}
print_elements(n->children);
}
}
int
main(int argc, char **argv)
{
xmlDoc *doc = NULL;
xmlNode *root = NULL;
LIBXML_TEST_VERSION
if (argc != 2) {
fprintf(stderr, "usage: %s \n", *argv);
return EXIT_FAILURE;
}
if ((doc = xmlReadFile(argv[1], NULL, 0)) == NULL)
return EXIT_FAILURE;
if ((root = xmlDocGetRootElement(doc)) == NULL)
return EXIT_FAILURE;
print_elements(root);
xmlFreeDoc(doc);
xmlCleanupParser();
return EXIT_SUCCESS;
}
A test:
$ cat test.xml
$ ./xmlkv test.xml
commSyslog.descr=Syslog Service
commSyslog.name=syslog
commSyslog.policyOwner=local
commSyslog.severity=critical
commSyslog.commSyslogClient[1].adminState=disabled
commSyslog.commSyslogClient[1].forwardingFacility=local7
commSyslog.commSyslogClient[1].hostname=none
commSyslog.commSyslogClient[1].name=secondary
commSyslog.commSyslogClient[1].severity=critical
commSyslog.commSyslogClient[2].adminState=disabled
commSyslog.commSyslogClient[2].forwardingFacility=local7
commSyslog.commSyslogClient[2].hostname=none
commSyslog.commSyslogClient[2].name=tertiary
commSyslog.commSyslogClient[2].severity=critical
commSyslog.commSyslogClient[3].adminState=disabled
commSyslog.commSyslogClient[3].forwardingFacility=local7
commSyslog.commSyslogClient[3].hostname=none
commSyslog.commSyslogClient[3].name=primary
commSyslog.commSyslogClient[3].severity=critical
commSyslog.commSyslogMonitor.adminState=disabled
commSyslog.commSyslogMonitor.descr=
commSyslog.commSyslogMonitor.name=
commSyslog.commSyslogMonitor.severity=critical
commSyslog.commSyslogConsole.adminState=disabled
commSyslog.commSyslogConsole.descr=
commSyslog.commSyslogConsole.name=
commSyslog.commSyslogConsole.severity=critical
commSyslog.commSyslogSource.audits=disabled
commSyslog.commSyslogSource.descr=
commSyslog.commSyslogSource.events=disabled
commSyslog.commSyslogSource.faults=enabled
commSyslog.commSyslogSource.name=
commSyslog.commSyslogFile.adminState=enabled
commSyslog.commSyslogFile.descr=
commSyslog.commSyslogFile.name=messages
commSyslog.commSyslogFile.severity=critical
commSyslog.commSyslogFile.size=4194304
Seems nice, just what I wanted. Of course, replacing /
with .
is pretty much useless, but I prefer this notation since it's better on the eyes.