Electively serialize to/from XML or binary in C++

XML Serialization

In order to manage the XMLs DOM tree, we should make use of a nice library which does all the magic for us. I went for TinyXML because I made good experiences with it in DVW. You may want to have a short look into its documentation in order to understand what I did in the following implementation.
Let’s jump straight into the implementation of die XMLSerializer class.

#ifndef VTE_CORE_XMLSERIALIZER_H
#define VTE_CORE_XMLSERIALIZER_H
#include "Serializer.h"
#include <core/tinyxml/tinyxml.h>
namespace vte
{
namespace core
{
class XMLSerializer : public Serializer
{
public:
	XMLSerializer();
	virtual ~XMLSerializer();
	void beginAttribute( const std::string& name );
	void writeNumberOfElements( u32 numElements )
	{
		// Not used for XML
	}
	void writeParameter( const std::string& name, const std::string& value );
	void writeParameter( const std::string& name, s32 value, const std::string& readable );
	void writeParameter( const std::string& name, const bool& value );
	void writeParameter( const std::string& name, const u32& value );
	void writeParameter( const std::string& name, const f32& value );
	void writeParameter( const std::string& name, const vec3& value );
	void writeParameter( const std::string& name, const irr::video::SColorf& value );
	void endAttribute();
	TiXmlDocument& getDocument()
	{
		return doc;
	}
protected:
	TiXmlDocument doc; // The actual XML document created by the serializer
	TiXmlElement* currentElement; // The current node we're working on
};
} // namespace core
} // namespace vte
#endif

As you see, there’s no magic going on here. XMLSerializer inherits from Serializer and implements the pure virtual methods. As I mentioned before, the XML serialization makes no use of the writeNumberOfElements method, so it’s left empty here.
On top of that the XMLSerializer has TinyXMLs TiXmlDocument as an attribute which holds the XML document the serializer builds during the serialization process and a TiXmlElement as the node the serializer currently works on. Moreover there’s an accessor method for the generated TiXmlDocument.
Now for the implementation.

vte::core::XMLSerializer::XMLSerializer( const std::string& fileName )
:	Serializer(),
	currentElement( 0 )
{
	TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" );
	doc.LinkEndChild( decl );
}
vte::core::XMLSerializer::~XMLSerializer()
{
}

The constructor initializes the currentElement attribute with 0 and creates an XML version declaration which is attached to the document which acts as TinyXML’s root element.

void vte::core::XMLSerializer::beginAttribute( const std::string& name )
{
	TiXmlElement* child = new TiXmlElement( name );
	if( currentElement == 0 )
	{
		doc.LinkEndChild( child );
	}
	else
	{
		currentElement->LinkEndChild( child );
	}
	currentElement = child;
}

This is the actual workhorse of the XMLSerializer. Whenever beginAttribute is called, a new child element is created in the XMLs DOM right under the current element. Unless we did not create one yet, the newly created child is appended directly under the document.
Writing a single parameter is pretty simple then.

void vte::core::XMLSerializer::writeParameter( const std::string& name, const std::string& value )
{
	VTE_ASSERT( currentElement != 0 ); // Make sure that beginAttribute has been called!
	currentElement->SetAttribute( name, value );
}

I leave the implementation of the rest of the writeParameter methods to you. Just remember that we defined some lexical_cast specializations e.g. for bool which you should use.

void vte::core::XMLSerializer::endAttribute()
{
	VTE_ASSERT( currentElement != 0 ); // Make sure that beginAttribute has been called!
	if( currentElement && currentElement->Parent() != 0 )
	{
		// becomes 0 if the document is reached
		currentElement = currentElement->Parent()->ToElement();
	}
}

Whenever an attribute is finished, we’ll move the currentElement pointer back to the parent element in the XML DOM. In order to recognize that we’re back at the top of the DOM, we use TinyXMLs ToElement method which will return 0 if Parent() returns the document (since document is of type TiXmlDocument, not TiXmlElement, just think of dynamic casting here).

4 Comments

Leave a Reply

Your email address will not be published.