[mrpt-serialization]

Overview

Serialization (marshalling) portable library for C++ objects persistence.

Library mrpt-serialization

This library is part of MRPT and can be installed in Debian-based systems with:

sudo apt install libmrpt-serialization-dev

Read also how to import MRPT into your CMake scripts.

Binary serialization (most efficient)

Main classes and concepts associated with this library:

  • mrpt::serialization::CArchive provides:

    • An abstraction of I/O streams (e.g. std::stream’s, MRPT’s mrpt::io::CStream ‘s, sockets)

    • A portable API (endianness aware) for serialization of data structures on those streams.

  • mrpt::serialization::CSerializable provides:

    • A generic way to define persistent C++ classes.

    • Versioning: if class fields are added or removed, you’ll still be able to read old data files.

Serialization happens via archive << object operators in all cases but, underneath, two mechanisms are provided:

  • Direct overloading of the << / >> operators for mrpt::serialization::CArchive objects.

    • Pros: concise declaration (just the two operator functions).

    • Cons: Cannot handle versioning. Cannot deserialize unknown types (i.e. no support for class factory).

  • Via mrpt::serialization::CSerializable and associated macros:

    • Pros: Allows polymorphic classes to be (de)serialized. Allows versioning.

    • Cons: Requires adding macros to class definitions. Requires registering the class with [mrpt-rtti].

Support for STL containers is provided via this “direct mechanism” for the container structure itself, but contained elements can use any of the serialization mechanisms.

Serializing shared_ptr<T> is supported for any arbitrary type T. It is legal to serialize an empty (nullptr) smart pointer; an empty pointer will be read back. Polymorphic classes can be also written and read, although reading a smart pointer to a polymorphic base class is only supported for classes derived from MRPT’s CSerializable, since that operation requires registering types in a class factory (see mrpt_rtti_grp and mrpt::serialization::CSerializable).

Example #1: serialize STL container via MRPT <tt>CStream</tt>s

See: serialization_stl/test.cpp

#include <mrpt/io/CFileInputStream.h>
#include <mrpt/io/CFileOutputStream.h>
#include <mrpt/serialization/CArchive.h>
#include <mrpt/serialization/stl_serialization.h>

#include <iostream>  // cout

void WriteAndReadExample()
{
    // Declare data to be serialized:
    std::map<std::string, uint32_t> m1{{"one", 1}, {"two", 2}};

    // === Write ===
    {
        // CStream output:
        mrpt::io::CFileOutputStream ofs("file.bin");
        auto arch_out = mrpt::serialization::archiveFrom(ofs);
        // Use << to serialize in binary form:
        arch_out << m1;
    }

    // === Read ===
    std::map<std::string, uint32_t> m2;
    {
        // CStream output:
        mrpt::io::CFileInputStream ifs("file.bin");
        auto arch_in = mrpt::serialization::archiveFrom(ifs);
        // Use >> to deserialize:
        arch_in >> m2;
    }

    std::cout << "Wrote: ";
    printMap(m1);
    std::cout << "Read : ";
    printMap(m2);
}

Output:

Wrote: one=1, two=2,
Read: one=1, two=2,

Example #2: serialize STL container via <tt>std::ostream</tt> and <tt>std::istream</tt>

See: serialization_stl/test.cpp

#include <mrpt/serialization/CArchive.h>
#include <mrpt/serialization/archiveFrom_std_streams.h>
#include <mrpt/serialization/stl_serialization.h>

#include <fstream>  // io std streams
#include <iostream>  // cout

void WriteAndReadExampleStdIO()
{
    // Declare data to be serialized:
    std::map<std::string, uint32_t> m1{{"one", 1}, {"two", 2}};

    // === Write ===
    {
        // CStream output:
        std::ofstream ofs("file.bin");
        auto arch_out = mrpt::serialization::archiveFrom<std::ostream>(ofs);
        // Use << to serialize in binary form:
        arch_out << m1;
    }

    // === Read ===
    std::map<std::string, uint32_t> m2;
    {
        // CStream output:
        std::ifstream ifs("file.bin");
        auto arch_in = mrpt::serialization::archiveFrom<std::istream>(ifs);
        // Use >> to deserialize:
        arch_in >> m2;
    }

    std::cout << "Wrote: ";
    printMap(m1);
    std::cout << "Read : ";
    printMap(m2);
}

Output:

Wrote: one=1, two=2,
Read: one=1, two=2,

Example #3: Serialization of existing MRPT classes

Write me!

Example #4: Polymorphic serialization of user classes

Write me!

Schema (plain-text) serialization (human readable)

An alternative mechanism to serialize objects is based on mrpt::serialization::CSchemeArchive and allow objects to be (de)serialized in plain text formats like XML, JSON, YAML, etc. For now (Aug 2018) only JSON is implemented.

Example #1: Easy JSON serialization

This method only requires having MRPT built against jsoncpp, but does not enforce the user to also depend on that library.

See: serialization_json_example/test.cpp

#include <mrpt/poses/CPosePDFGaussian.h>
#include <mrpt/serialization/CSchemeArchive.h>

#include <iostream>  // cout

void WriteAndReadExample()
{
    // Define the MRPT objects to be serialized:
    mrpt::poses::CPosePDFGaussian pdf1{
        mrpt::poses::CPose2D{1.0, 2.0, mrpt::DEG2RAD(90.0)},
        mrpt::math::CMatrixDouble33()};
    mrpt::poses::CPose2D p1{5.0, 6.0, mrpt::DEG2RAD(.0)};

    // --------------------
    // JSON Serialization
    // --------------------
    // Create a JSON archive:
    auto arch = mrpt::serialization::archiveJSON();

    // Writes the objects to the JSON archive:
    arch["pose_pdf"] = pdf1;
    arch["pose"] = p1;

    // Writes the JSON representation to an std::ostream
    std::stringstream ss;
    ss << arch;

    // also, print to cout for illustration purposes:
    std::cout << arch << std::endl;

    // --------------------
    // JSON Deserialization
    // --------------------
    // rewind stream for reading from the start
    ss.seekg(0);

    // Create a new JSON archive for reading
    auto arch2 = mrpt::serialization::archiveJSON();

    // Load the plain text representation into the archive:
    ss >> arch2;

    // Parse the JSON data into an MRPT object:
    mrpt::poses::CPosePDFGaussian pdf2;
    arch2["pose_pdf"].readTo(pdf2);
    mrpt::poses::CPose2D p2;
    arch2["pose"].readTo(p2);

    std::cout << "read pose:" << p2.asString() << std::endl;
}

Output:

{
    "pose" :
    {
        "datatype" : "CPose2D",
        "phi" : 0,
        "version" : 1,
        "x" : 5,
        "y" : 6
    },
    "pose_pdf" :
    {
        "cov" :
        {
            "data" : "[0.000000e+00 0.000000e+00 0.000000e+00 ;0.000000e+00 0.000000e+00 0.000000e+00 ;0.000000e+00 0.000000e+00 0.000000e+00 ]",
            "datatype" : "CMatrixD",
            "ncols" : 3,
            "nrows" : 3,
            "version" : 1
        },
        "datatype" : "CPosePDFGaussian",
        "mean" :
        {
            "datatype" : "CPose2D",
            "phi" : 1.5707963267948966,
            "version" : 1,
            "x" : 1,
            "y" : 2
        },
        "version" : 1
    }
}
read pose:[5.000000 6.000000 0.000000deg]

Example #2: JSON serialization with full access to jsoncpp

If you want to have full control on the JSON formatting and other details, you may directly depend on jsoncpp and use the following, template-based access to MRPT serialization:

See: serialization_json_example/test.cpp

#include <json/json.h>

void test()
{
    Json::Value val;
    auto arch = mrpt::serialization::CSchemeArchiveBase(
        std::make_unique<CSchemeArchive<Json::Value>>(val));

    mrpt::poses::CPose2D pt1{1.0, 2.0, 3.0};
    // Store any CSerializable object into the JSON value:
    arch = pt1;
    // Alternative:
    // arch["pose"] = pt1;

    std::stringstream ss;
    ss << val;
    std::cout << val << std::endl;
}

Library contents

// classes

class mrpt::serialization::CArchive;
class mrpt::serialization::CMessage;

template <typename SCHEME_CAPABLE>
class mrpt::serialization::CSchemeArchive;

class mrpt::serialization::CSchemeArchiveBase;
class mrpt::serialization::CSchemeArchiveBase_impl;
class mrpt::serialization::CSerializable;

// global functions

CSchemeArchiveBase mrpt::serialization::archiveJSON();
void mrpt::serialization::registerAllClasses_mrpt_serialization();

Global Functions

CSchemeArchiveBase mrpt::serialization::archiveJSON()

Returns an archive for reading/writing in JSON format.

This feature requires compiling MRPT with jsoncpp support. See [mrpt-serialization] for examples of use.

void mrpt::serialization::registerAllClasses_mrpt_serialization()

Forces manual RTTI registration of all serializable classes in this namespace.

Should never be required to be explicitly called by users, except if building MRPT as a static library.