Serialize and Deserialize array in C++ using RapidJSON

Share on:

Overview

Introduction

In my previous article, I covered serializing/deserializing JSON in C++ to a simple object using RapidJSON.

When working with JSON, my goal is to work always with object models and keep the actual JSON parsing behind the scenes. This keeps the readability and maintainability of the code as JSON manipulation, type checking and exception handling is done is a single class and not across the entire application. This also enables a future replacement of the JSON parsing library without the need to change the code of the entire application.

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

  • I am going to use the same C++ Console Application from the previous post about JSON Serialization in C++.
  • My JSON will be an array of products in an inventory. Here is the sample of 3 products in inventory:
 1[
 2  {
 3    "id": 1,
 4    "name": "Bush Somerset Collection Bookcase",
 5    "category": "Furniture",
 6    "sales": 22.0
 7  },
 8  {
 9    "id": 2,
10    "name": "Mitel 5320 IP Phone VoIP phone",
11    "category": "Technology",
12    "sales": 907.1519775390625
13  },
14  {
15    "id": 3,
16    "name": "Poly String Tie Envelopes",
17    "category": "Office Supplies",
18    "sales": 3.2639999389648439
19  }
20]

Creating the object models

For the single product, we used the following object model:

  1. We added member variable for every JSON property with the correct type (id, name, sales, category)
  2. Implemented getters/setters
  3. Implemented Serialize and Deserialize functions
 1namespace JSONModels
 2{
 3  class Product : public JSONBase
 4  {
 5  public:
 6    Product();		
 7    virtual ~Product();			
 8
 9    virtual bool Deserialize(const rapidjson::Value& obj);
10    virtual bool Serialize(rapidjson::Writer<rapidjson::StringBuffer>* writer) const;
11
12    // Getters/Setters.
13    const std::string& Name() const { return _name; }
14    void Name(const std::string& name) { _name = name; }
15
16    const std::string& Category() const { return _category; }
17    void Category(const std::string& category) { _category = category; }
18
19    float Sales() const { return _sales; }
20    void Sales(float sales) { _sales = sales; }
21
22    int Id() const { return _id; }
23    void Id(int id) { _id = id; }		
24  private:
25    std::string _name;
26    std::string _category;
27    float _sales;
28    int _id;
29  };	
30}

In the next step, we need to create a class that will hold a list of Product objects.

 1namespace JSONModels
 2{
 3  class Products : public JSONBase
 4  {
 5  public:		
 6    virtual ~Products() {};
 7    virtual bool Deserialize(const std::string& s);		
 8
 9    // Getters/Setters.
10    std::list<Product>& ProductsList() { return _products; }
11  public:
12    virtual bool Deserialize(const rapidjson::Value& obj) { return false; };
13    virtual bool Serialize(rapidjson::Writer<rapidjson::StringBuffer>* writer) const;
14  private:
15    std::list<Product> _products;
16  };
17}

Serialize

This function is called by JSONBase class once the object is being serialized. Our list is stored in the memory, so we need to write it to the StringBuffer.

In this code, we iterate over all Product in our _product list and call for Serialize function of the Product class.

 1bool Products::Serialize(rapidjson::Writer<rapidjson::StringBuffer>* writer) const
 2{
 3  writer->StartArray();
 4
 5     for (std::list<Product>::const_iterator it = _products.begin(); it != _products.end(); it++)
 6     {
 7        (*it).Serialize(writer);
 8     }
 9  writer->EndArray();
10
11  return true;
12}

Deserialize

Deserialize function is called by JSONBase class with JSON string parameter.

  1. First we need to call for InitDocument function to parse the string
  2. Once the doc object is initialized, we need to iterate over all JSON objects, call for Deserialize and finally add to _products list.
 1bool Products::Deserialize(const std::string& s)
 2{
 3  rapidjson::Document doc;
 4  if (!InitDocument(s, doc))
 5    return false;
 6
 7  if (!doc.IsArray())
 8    return false;
 9
10  for (rapidjson::Value::ConstValueIterator itr = doc.Begin(); itr != doc.End(); ++itr)
11  {
12    Product p;
13    p.Deserialize(*itr);
14    _products.push_back(p);
15  }
16
17  return true;
18}

Usage

Loading JSON array from file

 1// load json array
 2JSONModels::Products products;
 3products.DeserializeFromFile("DataSampleArray.json");
 4
 5for (std::list<JSONModels::Product>::const_iterator it = products.ProductsList().begin();
 6  it != products.ProductsList().end(); it++)
 7{
 8  // print some values.
 9  printf("Name:%s, Sales:%.3f", (*it).Name().c_str(), (*it).Sales());
10}

Adding a new object to the existing list

1// add new product
2JSONModels::Product newProduct;
3newProduct.Id(101);
4newProduct.Name("Global Value Mid-Back Manager's Chair, Gray");
5newProduct.Category("Furniture");
6newProduct.Sales(213.115f);    
7products.ProductsList().push_back(product);

Saving the list to a new file

1// save to new array file.
2products.SerializeToFile("DataSampleArrayNew.json")

Useful resources

  • Full source code of this post in GitHub