Saturday, March 28, 2015

Serialize C++ Object with QString Instance Variables

boost::serialization library can be used to serialize and deserialize the state of QString variables in a C++ program.
By default boost::serialization library can not serialize a directly given QString variable, so you need an extra Serializer implementation for your serialization and deserialization requirements.

When you try to serialize a QString variable directly by using boost::serialization library, compiler gives errors about missing serialize method as follows :

error: 'class QString' has no member named 'serialize'

In order to use boost::serialization library in your applications, it is required to have libboost-all-dev library on your OS which contains libboost-serialization-dev. Following terminal command installs required boost libraries on linux OS :

$ sudo apt-get install libboost-all-dev

After installing boost serialization library, you can start including related header files into your application.

Following sample project created by qt creator and contains following files :

1-BoostSerializeQString.pro
2-QStringSerializer.h
3-User.h
4-main.cpp



BoostSerializeQString.pro file contains project configuration.

QT       += core
TARGET    = BoostSerializeQString
CONFIG   += console
CONFIG   -= app_bundle
TEMPLATE  = app
LIBS     += -lboost_serialization

SOURCES  += main.cpp

HEADERS  += QStringSerializer.h \
            User.h

boost_serialization library is included into this Qt Project by adding "LIBS += -lboost_serialization" line.
In this example User class contains two QString instance variables to serialize and deserialize. When saving and loading User class it is required to save and load QString variable state properly. Also, User class has an intrusive serialize function. For User class case, the serialize function is implemented as a member of the class.

#ifndef USER_H
#define USER_H

class User
{
public:

    User() {}

    User(const QString &name, const QString &surname)
    {
        this->name = name;
        this->surname = surname;
    }

    QString getName() { return name; }
    QString getSurname() { return surname; }

private:
    QString name;
    QString surname;

    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        // serialize deserialize QString instance variables
        ar & BOOST_SERIALIZATION_NVP(name);
        ar & BOOST_SERIALIZATION_NVP(surname);
    }

};

#endif // USER_H


QStringSerializer.h file contains save and load functions which are going to be invoked during serialization and deserialization process of QString instance variables.

By declaring non-intrusive serialization mechanism we were able to implement serialization for QString without changing its original class definition.

boost::serialization is able to work on std::string type correctly, so it is required to retrieve std::string value of QString for serialization. And also for the reverse operation it is required to construct QString from loaded std::string value.

QString has got both toStdString and fromStdString functions to achieve these requirements.

Inside serialize and deserialize functions different statements are executed so it is required to implement save and load functions separately.

Depending on the type of the archieve used for saving or loading current QString variable, save or load function is invoked.

#ifndef QSTRINGSERIALIZER_H
#define QSTRINGSERIALIZER_H

namespace boost {
    namespace serialization {

        template<class Archive>
        void save( Archive & ar, const QString& qStringParam, const unsigned int )
        {
            // save class member variables
            std::string stdString = qStringParam.toStdString();
            ar & BOOST_SERIALIZATION_NVP(stdString);
        }

        template<class Archive>
        void load( Archive & ar, QString& qStringParam, const unsigned int )
        {
            // load class member variables
            std::string stdString;
            ar & BOOST_SERIALIZATION_NVP(stdString);
            qStringParam = qStringParam.fromStdString(stdString);
        }

        template<class Archive>
        void serialize(Archive & ar, QString & t, const unsigned int file_version)
        {
            split_free(ar, t, file_version);
        }

    } // namespace serialization
} // namespace boost
#endif // QSTRINGSERIALIZER_H


main method in main.cpp file contains two code blocks which are respectively used for serialization and deserialization of User object with QString instance variables or states. After reconstructing User object, QString instance variables name and surname are initialized with the original values as serialized.

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/nvp.hpp>

#include <QString>
#include <fstream>
#include <iostream>

#include "User.h"
#include "QStringSerializer.h"

using namespace std;

int main()
{
        {
            // Initialize User object to serialize with data
            User user("userName","userSurname");
            std::ofstream ofs("stateInfoFile.xml");
            boost::archive::xml_oarchive oa(ofs);
            // write class instance to archive
            oa & BOOST_SERIALIZATION_NVP(user);
        }

        {
            User user;
            std::ifstream ifs("stateInfoFile.xml");
            boost::archive::xml_iarchive ia(ifs);
            // read class instance back from archive
            ia & BOOST_SERIALIZATION_NVP(user);

            std::cout << "Name : " << user.getName().toStdString() << std::endl;
            std::cout << "Surname : " << user.getSurname().toStdString() << std::endl;
        }
}


User object state is saved into stateInfoFile.xml file and loaded back from the same xml file again for object reconstruction. stateInfoFile.xml file is an xml file and its content is as follows :