At the center of Xml serialization there are following classes:
- RapidXmlSaveSerializer,
- RapidXmlLoadSerializer,
- formatters from xml_formatters directory:
- element_formatter,
- attribute_formatter,
- etc.
Note Xml serialization is built using RapidXml library by Marcin Kalicinski. See: http://rapidxml.sourceforge.net/
Table of Contents
- RapidXmlSaveSerializer and RapidXmlLoadSerializer
- Quick note about element names
- Example: formatting boost::optional
RapidXmlSaveSerializer and RapidXmlLoadSerializer both represent a whole xml document. The both provide access to document element, which is a top level node of the xml. We can use the document element to store (or load) nodes inside it, like this:
RapidXmlSaveSerializer document("utf-8");
serialize< element_formatter<_name_> >(document.getDocumentElement(), value);
The resulting xml will look like this:
<name>value</name>
But this xml lacks one important part: the xml declaration. Here's how to add it:
RapidXmlSaveSerializer document("utf-8");
serialize< declaration_formatter<> >(document.getDocumentElement(), encoding);
serialize< element_formatter<_name_> >(document.getDocumentElement(), value);
Now the xml will look like this:
<?xml version="1.0" encoding="utf-8"?>
<name>value</name>
Alternatively we can use a special document_formatter
to achieve the same result:
RapidXmlSaveSerializer document("utf-8");
using doc_format = document_formatter<element_formatter<_name_>>;
serialize<doc_format>(document, value);
document_formatter
simply adds an xml declaration. Please not that it uses document
as a serializer (not document.getDocumentElement()
as before). It also extracts encoding from the document
, so there's no need to specify it manually.
Element and attribute names are provided to formatters as compile time strings. Due to limitations of the C++ language, we need to declare such compile time strings as follows:
declare_compile_time_string(name, "value");
In all examples below we use convention, that compile time string for "something"
is declared as:
declare_compile_time_string(_something_, "something");
Note It is also possible to use runtime created strings as element and attribute names. Two ways to do this are:
- using a stateful formatter, for example by calling
create_element_formatter(name)
,- using a
assign_name
formatter.Those options will be discussed later.
boost::optional<T>
is a class that can hold either object T or boost::none
. In other words it either holds a value, or is empty.
This type will allow us to showcase how to use various formatters to achieve desired results.
So how do we want boost::optional
to be expressed in xml?
Here's the obvious proposal, for empty and not-empty optionals:
<optional/>
<optional>value</optional>
We could write a formatter for this by hand (by creating a class with save()
and load()
methods).
But let's use optional_formatter
, that is already provided by this library . It has two parameters:
optional_formatter< flag_formatter, value_formatter >
It then formats boost::optional
as an empty/not-empty flag (formatted by flag_formatter), followed by the value (if present; formatted by value_formatter).
So a formatter for the xml shown above would be like this:
using opt_format = optional_formatter< content_exists, assign_text_content<> >;
In this example we use content_exists
formatter for flag. On load it simply checks if element has any content. On save it does nothing.
We also use assign_text_content<>
as a formatter for value. It simply stores given value as text inside element.
So if we will now call this code:
boost::optional<std::string> opt("Hello!");
RapidXmlSaveSerializer document("utf-8");
serialize< opt_format >(document.getDocumentElement(), opt);
We will get:
Hello!
Wait. That's not what we wanted!
But let's analyze: we asked to format value as text content of an element. And the element we gave was document.getDocumentElement()
. So in fact we got exactly what we asked for: document element (which does not have a name, and is not marked in any way), filled with text Hello!
.
But we wanted the optional to be enclosed in an <optional>
element, and that is the part that we were missing:
boost::optional<std::string> optEmpty;
boost::optional<std::string> optHello("Hello!");
using opt_format = optional_formatter< content_exists, assign_text_content<> >;
using opt_elt_format = element_formatter< _optional_, opt_format >;
serialize< opt_elt_format >(document.getDocumentElement(), optEmpty);
serialize< opt_elt_format >(document.getDocumentElement(), optHello);
This will produce the desired output:
<optional/>
<optional>Hello!</optional>
Here is another way we might want to format empty and not-empty optionals:
<optional initialized="false"/>
<optional initialized="true">value</optional>
The formatter for this is trivial:
using opt_format = optional_formatter< attribute_formatter<_initialized_>, assign_text_content<> >;
using opt_elt_format = element_formatter< _optional_, opt_format >;
Here we used attribute_formatter
to format empty/not-empty flag as an attribute.
And yet another way of formatting optionals:
<optional>
<flag>false</false>
</optional>
<optional>
<flag>true</false>
<value>value</value>
</optional>
The formatter for this:
using opt_format = optional_formatter< element_formatter<_flag_>, element_formatter<_value_> >;
using opt_elt_format = element_formatter< _optional_, opt_format >;
Now something more interesting: element that exists when optional has value, and doesn't exist, when optional is empty:
<value>value</value>
The formatter for this:
using opt_format = optional_formatter< element_counter<_value_>, element_formatter<_value_> >;
Here we used element_counter
to format empty/not-empty flag. If the count is greater than zero, then it means optional has a value.
Here we will format optional is an unusual way: as an element, whose name is the value of the empty/not-empty flag:
<false/>
<true>value</true>
The formatter for this:
declare_compile_time_string(_empty_, "");
using opt_format = optional_formatter< assign_name, assign_text_content<> >;
using opt_elt_format = element_formatter< _empty_, opt_format >;
Here we used assign_name
to name the parent element.
On the example of boost::optional
we saw various ways to format data. Using the same tools it's very easy to format almost anything exactly the way you want it.