0

I’m using libxml2 to create and read XML files in c that contain configuration information for the program I’m writing. The program makes its own configuration files (or another program sends it a configuration file and asks the program to run based off the config file), so the XML config files don’t need to be really easy for a human to read.

These configuration files contain lots of values and are really long. So right now I have a function that makes the XML files and another that reads the XML files. However any-time I change the write XML function than I need to also change the read xml function. So there isn’t actual code duplication, but something really close (ie. BAD) and because the configuration files are so long it is rather tedious to try to make sure everything is reading and writing the same thing.

This is the current set up.

struct config_data
{
    // category one
    int X
    int Y

    // category two
    int Z
    int A
}


int makeXMLsheet(char* fileout)
{
    xmlDocPtr doc = NULL;       /* document pointer */
    xmlNodePtr root_node = NULL; /* node pointers */   

    LIBXML_TEST_VERSION;

    doc = xmlNewDoc((xmlChar*) "1.0");
    root_node = xmlNewNode(NULL, BAD_CAST "configuration_file");
    xmlDocSetRootElement(doc, root_node);    

    // catogory one
    xmlNodePtr category_one =  xmlNewChild(root_node, NULL, BAD_CAST "category_one", NULL);    

    xmlNewChild(category_one, NULL, BAD_CAST "x", BAD_CAST "12345");
    xmlNewChild(category_one, NULL, BAD_CAST "y", BAD_CAST "1");

     // catogory two
    xmlNodePtr category_two =  xmlNewChild(root_node, NULL, BAD_CAST "category_two", NULL);    

    xmlNewChild(category_two, NULL, BAD_CAST "Z", BAD_CAST "12345");
    xmlNewChild(category_two, NULL, BAD_CAST "A", BAD_CAST "1");

    xmlSaveFormatFileEnc(fileout, doc, "UTF-8", 1);
    xmlFreeDoc(doc);
    xmlCleanupParser();

    return 0;
}


int readXMLsheet(char* filename,struct *config_data)
 {
     xmlDocPtr doc = getdoc(filename);

     config_data->X = getIntegerFromXML(0,doc,(xmlChar*)"//configuration_file/category_one/X"); 
     config_data->Y = getIntegerFromXML(0,doc,(xmlChar*)"//configuration_file/category_one/Y");
     config_data->Z = getIntegerFromXML(0,doc,(xmlChar*)"//configuration_file/category_two/Z");
     config_data->A = getIntegerFromXML(0,doc,(xmlChar*)"//configuration_file/category_two/a");

    xmlFreeDoc(doc);
     return 0;
 }

Where

 int getIntegerFromXML(int defaultValue, xmlDocPtr doc, xmlChar *xpath)

Does as its name says and gets a integer from the opened XML document that has the xpath location, and if it fails then it fills it with the default value so that the program doesn't crash and burn.

So I want to try to some how combine the read and write functions into one. My sample struct config-data is tiny compared to the number of values I actually have in my configuration struct, so combining them would make keeping track of everything much easier.

So I was thinking something like this.

int openXMLvalue(X, Y, Z, readOrWrite, defaultValue, value);

where X, Y, Z are the parent nodes, but there might be more or less than 3.

Any ideas on how to do this? Maybe make some type of array?

alk
  • 69,737
  • 10
  • 105
  • 255
Anthony
  • 108
  • 10

2 Answers2

0

I would make generic read and write functions that populate (or serialize) a generic configuration structure.

A simplified case would be to create an key/value structure in memory with get/set methods. The generic writeToXml function would simply create elements with key names containing the values.

If warranted, a hierarchical tree structure could be used instead, and perhaps add a few validation rules when reading a configuration file (a simple one would be to use an XML Schema for validation) to verify that required configuration values exist and are valid.

To add, change or remove configuration values would then only require the following steps (note that neither read or write functions require update):

  • Decide the new format of the configuration file
  • Update existing configuration files
  • Update any places in the application using the configuration values
  • Optionally update validation rules
erikxiv
  • 3,965
  • 1
  • 23
  • 22
0

Because of the large configuration file size, we switched to using sqlite. Then we made a function that would read a database and make an xml sheet, we made a function that would read an xml sheet and populate the database, and working on functions to print the database to stout and fill the C struct. Think this is going to make life much easier.

Anthony
  • 108
  • 10