Serialization

Overview

Serialization is the process to save plain C++ structures from memory to hard drive. In fw4spl, fwAtoms library provides tools to serialize all data (and especially Object that extend ::fwData::Object) to a JSON format [1]. Of course, this process is also available for loading data from JSON format to plain C++ structures.

[1]Introducing JSON. http://json.org/

To achieve this serialization, fwAtoms provides basic structures (which extend ::fwAtoms::Base) to manage better plain C++ structure evolution. Thus, there are two main steps in the serialization process:

  • Converting a ::fwData::Object into a ::fwAtoms::Object
  • Serializing a ::fwAtoms::Base in a JSON format

Atom objects

The basic structures provided by fwAtoms library are a set of restricted C++ type. All these structures extend ::fwAtoms::Base and cover all basic types and containers:

type brief
::fwAtoms::Base Base class of all atoms
::fwAtoms::String Atom to represent string types
::fwAtoms::Numeric Atom to represent numeric types (floating number or integer)
::fwAtoms::Boolean Atom to represent a boolean value
::fwAtoms::Map Atom to represent an associative container (std::string to ::fwAtoms::Base)
::fwAtoms::Sequence Atom to represent a sequence of object like vector or list
::fwAtoms::Object Atom to represent a C++ object with attributes
::fwAtoms::Blob Atom to represent binary information like buffers

For instance, consider the following C++ class:

class SimpleClass
{
    bool m_myBoolean;
};

class ComplexClass
{
    std::string m_myString;
    float m_myFloat;
    SimpleClass* m_mySimpleClass;
};

It’s Atom equivalent is (simplified code):

fwAtoms::Object
{
    metaInfos
    {
        "CLASSNAME_METAINFO" : "SimpleClass"
        "ID_METAINFO" : "<ID of the object>"
    }

    attributes
    {
        "myBoolean" : ::fwAtoms::Boolean
    }
}


fwAtoms::Object
{
    metaInfos
    {
        "CLASSNAME_METAINFO" : "ComplexClass"
        "ID_METAINFO" ; "<ID of the object>"
    }

    attributes
    {
        "myString" : ::fwAtoms::String
        "myFloat" : ::fwAtoms::Numeric
        "mySimpleClass" : ::fwAtoms::Object("SimpleClass")
    }
}

The main advantage of this representation is the ability to change easily the form of a class. In fact, all plain C++ objects are represented as Atoms::Object with a map of attributes. Thus, adding, removing or changing the content of an attribute is easy. Moreover, because of these restricted types, atom parsing is also made easier. The main difficulty is how to convert plain C++ object using this set of restricted types.

Convert a fwData::Object

As explained earlier, all objects in fw4spl inherit from the ::fwData::Object class. To convert a C++ object in Atom, it must inherit from this class. To allow this conversion, some work must be done.

The first thing is to update the header file of the structure and add these lines :

// Before all namespace
fwCampAutoDeclareDataMacro((<namespace elem>)
        (<namespace elem>)(<class name>), <method export macro>);

// In the public class part
fwCampMakeFriendDataMacro((<namespace elem>)
        (<namespace elem>)(<class name>));

These two functions allow the declaration of the class to the conversion process.

Next, the conversion systems must know the class information including attributes, base class, library location and data version. This is achieved by creating a class which defines these properties.

Example

This can be illustrated by taking the previous class and creating these two files:

Header file of the newly created class: ComplexClass.hpp

// Reference class

fwCampAutoDeclareDataMacro((fwData)(ComplexClass), FWDATA_API);

namespace fwData
{
class ComplexClass : public ::fwData::Object
{
    fwCampMakeFriendDataMacro((fwData)(ComplexClass));

    std::string m_myString;
    float m_myFloat;
    ::fwData::SimpleClass* m_mySimpleClass;
};
}

Header file of serialization class :

// hpp binding file
#include <fwCamp/macros.hpp>
#include <fwData/ComplexClass.hpp>
#include "fwDataCamp/config.hpp"

fwCampDeclareAccessor((fwData)(ComplexClass), (fwData)(SimpleClass));

Source file of serialization class :

// cpp binding file
// include previous cpp file

#include <fwCamp/UserObject.hpp>

fwCampImplementDataMacro((fwData)(ComplexClass))
{
    builder
        .tag("object_version", "1")
        .tag("lib_name", "fwData")
        .base< ::fwData::Object>()
        .property("myString" , &::fwData::ComplexClass::m_myString)
        .property("myFloat" , &::fwData::ComplexClass::m_myFloat)
        .property("mySimpleClass" , &::fwData::ComplexClass::m_mySimpleClass)
        ;
}

In a header file, the method fwCampDeclareAccessor is necessary when an object has a pointer or a smart pointer to another object.

In a source file, fwCampImplementDataMacro declares the properties of the bound object with an object called a builder: it provides several methods to describe the object to bind.

method brief
tag(key, value) Register a tag in the atom meta information.
base<BaseClass>() Identify the base class of the bound object
property(arg1, arg2) Set property of the object and how to access it

Most of the work is completed when the header file of the relevant class has been updated and a binding class created. The last step is to register the binding class in the conversion system using the following line in the library containing binding classes:

localDeclarefwDataComplexClass();

In fw4spl, data are located in fwData library whereas data binding classes are located in fwDataCamp library. The above line registering a binding class can be found in fwDataCamp autoload.hpp files.

Serialization file example

For more information about serialization see:

location brief
Srclib/core/fwData/include/ fwData header files with serialization macros
Srclib/core/fwDataCamp Serialization description of all fw4spl data
Srclib/core/fwDataCamp/include/fwDataCamp/autoload.hpp Auto loading data bindings in the system

fwData::Object to fwAtoms::Object conversion

The requirements to convert an fwData::Object into an fwAtoms::Object are in the fwAtomConversion library.

Two functions are necessary to achieve this conversion:

//Convert a fwData::Object into fwAtoms::Object
SPTR(::fwAtoms::Object) convert( const SPTR(::fwData::Object) &data );

//Convert a fwAtoms::Object into fwData::Object
SPTR(::fwData::Object) convert( const SPTR(::fwAtoms::Object) &atom );

Serialize an Atoms object to JSON format

When a fw4spl data is converted into Atoms, it can be saved in JSON format. Both an Atom reader and Atom writer are available in the fwAtomsBoostIO fw4spl library: simply instantiate one of these classes with an Atom object and call the read or write method.

To serialize atoms into JSON, a visitor pattern is used. An example can be found in the fwAtomsBoostIO/Reader.cpp file.

Conclusion

Accordingly, you have now the requirements to serialize data in the framework and a basic knowledge about the mechanism behind it. To conclude, this is a diagram of the serialization mechanism:

../../_images/serialization.png