Introduction
In some of my programs, I have to write the data in XML format. Only writing: reading and parsing is the mission of some other tools. Actually I only have to write the simple plain text with some specific markup.
For these purposes, I have implemented a simple class, which behaves as usual std::stream
, but also track all the needed XML markings. The class implementation is very simple and template-based. One may write to XmlStream
, everything that can be written into std::ostream
. The tags and attributes are declared by special helper functions.
Using the code
Look at the example below, which tells everything about my class (I specially omitted the implementation of the XmlStream
class, if you�ve interest in it, you'll download the source code, I think).
ofstream f("sample.xml");
XmlStream xml(f);
xml << prolog()
<< tag("sample-tag")
<< tag("some-tag")
<< attr("int-attribute") << 123
<< attr("double-attribute") << 456.789
<< chardata() << "This is the text"
<< endtag()
<< tag("empty-self-closed-tag")
<< endtag()
<< tag() << "computed-name-tag"
<< attr("text-attr") << "a bit of text"
<< endtag()
<< tag("deep-tag")
<< tag("sub-tag-2")
<< tag("sub-tag-3")
<< endtag("deep-tag");
As you can see, writing of XML became a very simple procedure. XmlStream
delegates all the incoming data to a specified std::ostream
(std::ofstream
in the example above) except for the tags and attributes declarations, which are issued by helper functions: tag()
, endtag()
, attr()
, chardata()
and prolog()
. To fill attribute values and tag character data, one should use standard stream operations. If object of some class can be written into std::ostream
, it's automatically writeable into my XmlStream
. This feature is released by template operator<<
, which mostly calls standard streaming operations.
template<class t> XmlStream& operator<<(const t& value) {
if (stateTagName == state)
tagName << value;
s << value;
return *this;
}
The final executable contains only those implementations of this operator, which corresponds to actually used data types.
Advantages of my approach
- Writing of XML data is very fast (almost as fast as plain text writing).
- You use standard and well-known functions to build the content of the XML document. It's simple to implement new
operator
's supporting any user classes.
- The source code, which writes XML data, represents itself the structure of the resulting document.
XmlStream
is a really tiny class! Almost all of its work is placed in one overloaded operator<<
.
Disadvantages
- All the data are written into XML "as is", without any encoding (UTF-8, UTF-16 etc.). In my tasks, it's often not a matter of headache, because my data are simply either Latin alphas or numbers. But for the common case, one may have to implement special
std::ostream
(in fact: std::streambuf
) successor, which will perform the encoding work.
XmlStream
does not check if output XML data represent well-formed XML document. Programmer must check himself whether the tag and attribute names are valid and there are no special characters (like angle brackets) in the attribute values and character data.
Futures
The current version of XmlWriter
is used in my programs, which outputs the results of the scientific calculations in XML format. If someone wants to improve this simple class, he may first get rid of disadvantages mentioned above. Also he might change the processing of the tags to make output XML documents more human-readable (i.e. add line separators, padding etc).