Serialize and Deserialize object in C++ using RapidJSON
Overview
Introduction
In one of my projects in C++, I had to work with an input in JSON format as we were using Django Web API that produces a JSON response for REST API calls.
Parsing the JSON format in C++ should be easy with the open source libraries such as RapidJSON, nlohmann/jsonm, Boost.JSON.
My goal was to work with strongly-typed objects and keep the parsing behind the scenes.
Such task sounds trivial if you work in C# or other programming language that supports reflection, but in C++ it requires a bit more work.
Full source code of this post in GitHub.
My stack
- Visual Studio 2019 Community Edition, 16.5.1
- RapidJSON 1.1.0 release
- Windows 10 Pro 64-bit (10.0, Build 18363)
Preparing the project
- In Visual Studio, create a new Console Application in C++. It can be both, 32-bit or 64-bit.
- In Windows Explorer, open the root folder of the project in command line prompt.
- Clone the RapidJSON repository using the following command line:
1 git clone https://github.com/Tencent/rapidjson.git
- I am going to use a very simple JSON that represents a product in an inventory. It has a few properties of different types:
1{
2 "id": 9,
3 "name": "Bush Somerset Collection Bookcase",
4 "category": "Furniture",
5 "sales":122.0
6}
Creating the object model
As I mentioned before, in the serialize/deserialize process I want to be able to work with classes with strongly typed members and accessors functions.
For this purpose, I created an abstract base class JSONBase
that provides following capabilities:
- Document parsing using RapidJSON
- Serializing to file and Deserializing from file
Inheriting from JSONBase requires an implementation of Serialize and Deserialize pure virtual functions.
1class JSONBase
2{
3public:
4 bool DeserializeFromFile(const std::string& filePath);
5 bool SerializeToFile(const std::string& filePath);
6
7 virtual std::string Serialize() const;
8 virtual bool Deserialize(const std::string& s);
9
10 virtual bool Deserialize(const rapidjson::Value& obj) = 0;
11 virtual bool Serialize(rapidjson::Writer<rapidjson::StringBuffer>* writer) const = 0;
12protected:
13 bool InitDocument(const std::string & s, rapidjson::Document &doc);
14};
Creating the Product class
1class Product : public JSONBase
2{
3public:
4 Product();
5 virtual ~Product();
6
7 virtual bool Deserialize(const rapidjson::Value& obj);
8 virtual bool Serialize(rapidjson::Writer<rapidjson::StringBuffer>* writer) const;
9
10 // Getters/Setters.
11 const std::string& Name() const { return _name; }
12 void Name(const std::string& name) { _name = name; }
13
14 const std::string& Category() const { return _category; }
15 void Category(const std::string& category) { _category = category; }
16
17 float Sales() const { return _sales; }
18 void Sales(float sales) { _sales = sales; }
19
20 int Id() const { return _id; }
21 void Id(int id) { _id = id; }
22private:
23 std::string _name;
24 std::string _category;
25 float _sales;
26 int _id;
27};
As my goal is to work with objects and not JSON, we need following:
- Create a class that derives from
JSONBase
that represents aProduct
. - Add member variables to the class. (id, name, category, sales)
- Add Getters/Setters for member variables.
- Implement the pure virtual functions Serialize and Deserialize
Serialize
This function is called by the JSON base class passing the StringBuffer
pointer. We need to write the property names and values to the buffer.
1bool Product::Serialize(rapidjson::Writer<rapidjson::StringBuffer> * writer) const
2{
3 writer->StartObject();
4
5 writer->String("id"); // create Id property
6 writer->Int(_id); // write the Id value from the object instance
7
8 writer->String("name");
9 writer->String(_name.c_str());
10
11 writer->String("category");
12 writer->String(_category.c_str());
13
14 writer->String("sales");
15 writer->Double(_sales);
16
17 writer->EndObject();
18
19 return true;
20}
Deserialize
Deserialize function is called by JSONBase
class with the current object being parsed. We need to call the setter functions of our object to update the value from JSON.
1bool Product::Deserialize(const rapidjson::Value & obj)
2{
3 Id(obj["id"].GetInt());
4 Name(obj["name"].GetString());
5 Category(obj["category"].GetString());
6 Sales(obj["sales"].GetFloat());
7
8 return true;
9}
Notice that you should access every property with the correct type.
- int - GetInt()
- int64 - GetInt64()
- string - GetString()
- float - GetFloat()
- double - GetDouble()
Check this section in RapidJSON tutorial for more info about the supported types.
Usage
After we've done all the hard work with defining the classes, the usage is very straight forward:
Loading JSON from file
1JSONModels::Product product;
2product.DeserializeFromFile("DataSample.json");
3printf("Name:%s, Sales:%.3f", product.Name().c_str(), product.Sales());
Loading JSON from file, changing values to writing back to file
1JSONModels::Product product;
2product.DeserializeFromFile("DataSample.json");
3product.Sales(product.Sales() + 100.0f); // increase the sales by 100
4product.SerializeToFile("DataSampleNew.json");
In the next article, I am going to cover serializing/deserialzing JSON arrays in C++.
Useful resources
- Full source code of this post in GitHub