Skip to content

Reading structures from XML

incoder edited this page Oct 18, 2018 · 15 revisions

Assuming you have a program that has some configuration, and this configuration must be stored in some file and this file can be edited manually. You have a few configurations and only one of them is used, but you don't what to use more than one file to store them. Classical solution for this task would be properties / ini files. The core issue of such an approach, user can make a typo in such a file, and if this file is not really a trivial, user cannot check for the error before running a program. Well known alternative for properties file - is XML. XML can be validated before your program is actually started. Including markup roles as well as for the content with XSD or legacy DTD. So you'd like to use XML and you don't want this XML have a big nesting level, so that it will be human readable and would not contain large amount of markup data. Other words you'd like to use XML attributes as well.

So we will use following XML:

<?xml version="1.0" encoding="UTF-8"?>
<configurations>
	<configuration id="0" enabled="true">
		<name>Test configuration 0</name>
	</configuration>
	<configuration id="1" enabled="false">
		<name>Test configuration 1</name>
	</configuration>
</configurations>

Let's implement the configuration read using IO.

For example - configuration is represented as the following POCO structure:

struct configuration
{
	std::size_t id;
	bool enabled;
	std::string name;
};

And those structures should be stored in std::vector

Algorithm will be simple:

  • Open a file and construct XML reader
  • Define a function to read xml attributes and nesting tags
  • Read xml tags into structures, and save them in std::vector

The code will looks like following:

// for converting characters into std::size_t
// can be replaced with boost lexical_cast
// or std::string_stream etc.
typedef io::xml::lexical_cast_traits<std::size_t> size_t_cast;
// same this for bool type
typedef io::xml::lexical_cast_traits<bool> bool_cast;

// Read a single configuration structure from XML reader
static configuration read_configuration(io::unsafe<io::xml::reader>& rd) {
	configuration ret;
	io::xml::start_element_event bev = rd.next_tag_begin();
		// read id field from attribute
		io::const_string tmp = bev.get_attribute("","id").first;
	  	ret.id = size_t_cast::from_string( tmp.data() );
                // read enabled field from attribute
	  	tmp = bev.get_attribute("", "enabled").first;
	  	ret.enabled = bool_cast::from_string( tmp.data() );
	  	// read name field from nesting tag <name> tag
		bev = rd.next_tag_begin(); // drop to next tag
		  ret.name = std::string( rd.next_characters().data() );
		rd.next_tag_end();
	// drop to </configuration>
	rd.next_tag_end();
	return ret;
}

int main(int argc, const char** argv)
{
        // Open a configuration xml file, and construct XML reader object
	io::file sf("test-config.xml");
	std::error_code ec;
	io::xml::s_source src = io::xml::source::create(ec, sf.open_for_read(ec) );
        // check for error coder, and exit the program if there were any
	io::check_error_code( ec );
        // construct the low-level streaming parser
	io::xml::s_event_stream_parser psr = io::xml::event_stream_parser::open(ec, std::move(src) );
	io::check_error_code( ec );
        // construct high level XML reader, use unsafe wrapper to avoid multiple calls to check_error_code
        io::unsafe<io::xml::reader> rd( std::move(psr) );

        // a vector to store configuration structures  
        std::vector<configuration> configurations;
	
        // Drop reader to <configurations>
	rd.next_tag_begin();

	// De-serialize configurations in loop
	rd.to_next_state();
	while( rd.is_tag_begin_next() ) {
           configurations.emplace_back( read_configuration(rd) );
	   rd.to_next_state();
	}

	return 0;
}
Clone this wiki locally