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

How does it look like?

Now that we have a nice interface defined, how would serialization and deserialization actually look like? I’ll give a straight example from my project where the World class acts as a wrapper for a tree of Entity objects, i.e. a World has one root Entity and an Entity can contain 0:n entities itself. I have added additional tabs to emphasize the structure.

void vte::world::World::writeTo( vte::core::Serializer* destination )
{
	destination->beginAttribute( "world" );
		destination->writeNumberOfElements( 1 );
		writeEntity( root, destination );
	destination->endAttribute(); // world
}
void vte::world::World::writeEntity( vte::world::Entity* entity, vte::core::Serializer* destination )
{
	destination->beginAttribute( "entity" );
		destination->writeParameter( "type", entity->getType(), vte::core::lexical_cast< std::string >( entity->getType() ) );
		destination->writeParameter( "name", entity->getName() );
		destination->writeParameter( "id", entity->getID() );
		entity->writeTo( destination );
		destination->beginAttribute( "children" );
			destination->writeNumberOfElements( (u32) entity->getChildren().size() );
			for( vecEntities::iterator it = entity->getChildren().begin(); it != entity->getChildren().end(); ++it )
			{
				Entity* child = *it;
				writeEntity( child, destination );
			}
		destination->endAttribute(); // children
	destination->endAttribute(); // entity
}

Let’s dig into the code line by line. Line 1, method writeTo, defines the entry point where the however-it-was-created (we’ll get to this later) Serializer is handed over to do its work. In line 3, the enclosing tag I mentioned earlier is created. Since this tag simply wraps the entity hierarchy, it has exactly one child element, the root Entity, so I set the number of child elements to 1 in line 4. Next, I jump into the actual entity serialization in line 5.
writeEntity performs the recursive entity serialization. Starting with the root element handed over by writeTo, it starts a new entity attribute in line 11 and then writes some crucial parameters all entities have in common, i.e. a type, a name and a unique ID. The rest of attributes is written by the Entity itself in line 17 – here, I make use of polymorphism which means that each object inherited from Entity or its inheritors adds the attributes it requires to successfully serialize itself. Then, I create a new children attribute which wraps all child entities of the current Entity, save the number of children and dive into recursion for each child entity. After that, the children and entity attributes are closed in line 26 and 28 respectively and we’re done. Easy, huh?
Because an example often explains more than a thousand words, here’s a part of the output of the XML serializer of my project.

<world>
	<entity type="root" name="RootEntity_1" id="1">
		<transformation position="0 0 0" rotation="0 0 0" scale="1 1 1" />
		<scriptable fileName="" class="" />
		<children>
			<entity type="camera" name="Camera_2" id="2">
				<transformation position="0 0 0" rotation="0 0 0" scale="1 1 1" />
				<scriptable fileName="" class="" />
				<children />
			</entity>
			<entity type="skybox" name="SkyBox_3" id="3">
				<transformation position="0 0 0" rotation="0 0 0" scale="1 1 1" />
				<scriptable fileName="" class="" />
				<top texture="resource/defaults/skybox/irrlicht2_up.jpg" />
				<bottom texture="resource/defaults/skybox/irrlicht2_dn.jpg" />
				<left texture="resource/defaults/skybox/irrlicht2_lf.jpg" />
				<right texture="resource/defaults/skybox/irrlicht2_rt.jpg" />
				<front texture="resource/defaults/skybox/irrlicht2_ft.jpg" />
				<back texture="resource/defaults/skybox/irrlicht2_bk.jpg" />
				<children />
			</entity>
			...
		</children>
	</entity>
</world>

Before we move on to the concrete implementation of the (de)serializers, one last word of explanation regarding the interface. You may have wondered why the writeParameter method for type s32 has an additional attribute readable. The reason is that I have several enumerations (which are internally represented as s32) which would become numerical digits when written to XML. But since XML is so descriptive elsewhere, I wanted to have the opportunity to have descriptive values written to and read from XML.

4 Comments

Leave a Reply

Your email address will not be published.