Usage

A guide on how to use GPDS

Terminology

Value vs. Container

The Value and Container classes implement the core concept of GPDS's serialization design. Therefore, it's important to understand their difference and how to use them properly.

The following figure illustrates how these two classes are used to model a C++ object for the (de)serialization process:

Overview

This topic explains the usage of the GPDS library. See Integration (General) for instructions on how to integrate the library into a project.

General usage

The following figure illustrates how GPDS is used by an address book application:

Make class serializable

A class needs to inherit from the Container class in order to become serializable through GPDS.

Implement Serialize interface

Implement the Gpds::Serialize interface by inheriting from the respective class to make your own class become serializable through GPDS as shown by the following UML class diagram:

Corresponding C++ code:

class Car : public Gpds::Serialize {
public:
    std::string manufacturer;
    std::string model;
    int year_of_construction;
    Color color;

    virtual Gpds::Container toContainer() const override
    {
        Gpds::Container c;

        c.addValue("manufacturer", manufacturer);
        c.addValue("model", model);
        c.addValue("year_of_construction", year_of_construction);
        c.addValue("color", color.toContainer());

        return c;
    }

    virtual void fromContainer(const Gpds::Container &c) override
    {
        manufacturer = c.getValue<std::string>("manufacturer");
        model = c.getValue<std::string>("model");
        year_of_construction = c.getValue<int>("year_of_construction");
        color.fromContainer( *c.getValue<Gpds::Container*>( "color" ) );
    }
};

class Color : public Gpds::Serialize
{
public:
    std::string name;
    int red;
    int green;
    int blue;

    virtual Gpds::Container toContainer() const override
    {
        Gpds::Container c;

        c.addAttribute("format", "rgb");
        c.addAttribute("name", name);

        c.addValue("red", red).addAttribute("depth", "32");
        c.addValue("green", green).addAttribute("depth", "32");
        c.addValue("blue", blue).addAttribute("depth", "32");

        return c;
    }

    virtual void fromContainer(const Gpds::Container& c) override
    {
        // Retrieve format
        const std::string& formatString = c.getAttribute("format").value_or("n/a");
        assert( formatString == "rgb" );

        name = c.getAttribute("name").value_or("n/a");
        red = c.getValue<int>("red");
        green = c.getValue<int>("green");
        blue = c.getValue<int>("blue");
    }
};

Once serialized with the XML archiver, the following output is produced:

<car>
    <color format="rgb" name="Black">
        <blue depth="32">0</blue>
        <green depth="32">0</green>
        <red depth="32">0</red>
    </color>
    <manufacturer>Jeep</manufacturer>
    <model>Grand Cherokee</model>
    <year_of_construction>2009</year_of_construction>
</car>
Note: Values within a container are sorted alphabetically to improve look-up time.

Serialize

Create an instance of ArchiverXml (or of any other Archiver class).

Call Archiver::save() to serialize:

Car car;

std::sstream stream;
Gpds::ArchiverXml ar;
ar.save(stream, car, "car");

std::cout << stream.str() << std::endl

Deserialize

Create an instance of ArchiverXml (or of any other Archiver class).

Call Archiver::load() to serialize:

Car car;

Gpds::ArchiverXml ar;
ar.load(stream, car, "car");

Nesting

The Container allows for nesting.

The Container class allows for nesting to produce serialized output that represents hierarchical/polymorphic data.

Created nested values

Nesting is achieved by passing a Container to Container::addValue().

Color color;
Gpds::Container c;
c.addValue( "color", color.toContainer() );

Retrieve nested values

Nested containers can be retrieved by calling Container::getValue<Gpds::Container*>().

Color color;
color.fromContainer( *carContainer.getValue<Gpds::Container*>( "color" );