'Cpp: JSON parser in Cpp that provide support Serialize/Deserialize feature, converting JSON objects to user-defined classes?

I'm working on native C++ development and looking for JSON parser that can handle complex JSON files and convert into class objects.

  1. I've looked at native benchmarks for JSON parsers available in C++ and came to conclusion that RapidJSON is popular and best fit considering processing time and size handling.

  2. My requirement is to convert JSON objects to user defined classes and vice versa.

  3. The Jackson has Objectmapper class that provides functionality for reading and writing JSON, either to and from basic POJOs (Plain Old Java Objects), or to and from a general-purpose JSON Tree Model (JsonNode), as well as related functionality for performing conversions.

QUESTIONS:

  1. Is there an equivalent in RapidJSON or other JSON parser that allow us to configure Serialize and Deserialize feature (ex: Jackson JAVA library is the highly customizable serialization and deserialization process, converting JSON objects to Java classes)?
  2. If No, What's the right way to work around it? Is there only way to build your own serializer to convert to our custom classes?

NOTE: I've looked at few posts on stackoverflow and did not find one that answers it.



Solution 1:[1]

Is there an equivalent in RapidJSON or other JSON parser that allow us to configure Serialize and Deserialize feature (ex: Jackson JAVA library is the highly customizable serialization and deserialization process, converting JSON objects to Java classes)?

I think ThorsSerializer does the job.
All you have to do is declare via ThorsAnvil_MakeTrait() what fields in a class are serializable (see below).

If No, What's the right way to work around it? Is there only way to build your own serializer to convert to our custom classes?

You can use RapidJSON (or several other libraries) but you need to write customer code to convert from the JSON objects generated by the library into your own objects. This is not terribly hard.

The other disadvantage of most libraries is that they actually build a full representation of the data in JSON like objects and you need to copy the data into your structure. For small objects not a problem but for more complex structures this can take up some space. The ThorsSerializer avoids this completely and copies data directly into your structures: see look at memory used.

Using the same example as @Daniel
Using ThorsSerializer: https://github.com/Loki-Astari/ThorsSerializer
Note: I am the author.

#include <string>

const std::string s = R"(
[
    {
        "author" : "Haruki Murakami",
        "title" : "Kafka on the Shore",
        "price" : 25.17
    },
    {
        "author" : "Charles Bukowski",
        "title" : "Pulp",
        "price" : 22.48
    }
]
)";

namespace ns {
    struct book
    {   
        std::string author;
        std::string title;
        double price;
    };  
} // namespace ns


#include <iostream>
#include "ThorSerialize/Traits.h"   // for ThorsAnvil_MakeTrait
#include "ThorSerialize/SerUtil.h"  // Has definitions for all STL types.
#include "ThorSerialize/JsonThor.h" // JSON version: There is also YAML


ThorsAnvil_MakeTrait(ns::book, author, title, price);

Then reading/writting json in main is simple:

int main()
{
     using ThorsAnvil::Serialize::jsonExport;
     using ThorsAnvil::Serialize::jsonImport;


     std::stringstream   stream(s);

     ns::book                   book;
     std::vector<ns::book>      allBooks;
     stream >> jsonImport(allBooks);

     std::cout << jsonExport(allBooks)
               << "\n\n"
               << jsonExport(allBooks, ThorsAnvil::Serialize::PrinterInterface::OutputType::Stream)
               << "\n\n";
}

TO build:

> g++ -std=c++14 main.cpp -lThorSerialize17

Output:

> ./a.out 
[ 
    { 
        "author": "Haruki Murakami", 
        "title": "Kafka on the Shore", 
        "price": 25.17
    }, 
    { 
        "author": "Charles Bukowski", 
        "title": "Pulp", 
        "price": 22.48
    }]

[{"author":"Haruki Murakami","title":"Kafka on the Shore","price":25.17},{"author":"Charles Bukowski","title":"Pulp","price":22.48}]

Solution 2:[2]

jsoncons, nlohmann and ThorsSerializer all support conversion between JSON and C++ objects. Examples with jsoncons and nlohmann are shown below, Martin York has one for his ThorsSerializer in a separate posting. The latter in my opinion is very nice, and certainly wins the prize for brevity. In the spirit of Oscar Wilde's quote that "imitation is the sincerest form of flattery", I've introduced the macro JSONCONS_ALL_MEMBER_TRAITS to jsoncons, and modified that example accordingly.

Consider

const std::string s = R"(
[
    {
        "author" : "Haruki Murakami",
        "title" : "Kafka on the Shore",
        "price" : 25.17
    },
    {
        "author" : "Charles Bukowski",
        "title" : "Pulp",
        "price" : 22.48
    }
]
)";

namespace ns {
    struct book
    {
        std::string author;
        std::string title;
        double price;
    };
} // namespace ns

Using jsoncons to convert between s and an std::vector<ns::book>:

#include <jsoncons/json.hpp>

namespace jc = jsoncons;

// Declare the traits. Specify which data members need to be serialized.
JSONCONS_ALL_MEMBER_TRAITS(ns::book,author,title,price);

int main()
{
    std::vector<ns::book> book_list = jc::decode_json<std::vector<ns::book>>(s);

    std::cout << "(1)\n";
    for (const auto& item : book_list)
    {
        std::cout << item.author << ", " 
                  << item.title << ", " 
                  << item.price << "\n";
    }

    std::cout << "\n(2)\n";
    jc::encode_json(book_list, std::cout, jc::indenting::indent);
    std::cout << "\n\n";
}

Output:

(1)
Haruki Murakami, Kafka on the Shore, 25.17
Charles Bukowski, Pulp, 22.48

(2)
[
    {
        "author": "Haruki Murakami",
        "price": 25.17,
        "title": "Kafka on the Shore"
    },
    {
        "author": "Charles Bukowski",
        "price": 22.48,
        "title": "Pulp"
    }
]

Using nlohmann to convert between s and an std::vector<ns::book>:

#include <nlohmann/json.hpp>
#include <iomanip>

namespace nh = nlohmann;

// Provide from_json and to_json functions in the same namespace as your type   
namespace ns {
    void from_json(const nh::json& j, ns::book& val) 
    {
        j.at("author").get_to(val.author);
        j.at("title").get_to(val.title);
        j.at("price").get_to(val.price);
    }

    void to_json(nh::json& j, const ns::book& val) 
    {
        j["author"] = val.author;
        j["title"] = val.title;
        j["price"] = val.price;
    }
} // namespace ns

int main()
{
    nh::json j = nh::json::parse(s);

    std::vector<ns::book> book_list = j.get<std::vector<ns::book>>();
    std::cout << "\n(1)\n";
    for (const auto& item : book_list)
    {
        std::cout << item.author << ", " 
                  << item.title << ", " 
                  << item.price << "\n";
    }

    std::cout << "\n(2)\n";
    nh::json j2 = book_list;
    std::cout << std::setw(4) << j2 << "\n\n";
}

Output:

(1)
Haruki Murakami, Kafka on the Shore, 25.17
Charles Bukowski, Pulp, 22.48

(2)
[
    {
        "author": "Haruki Murakami",
        "price": 25.17,
        "title": "Kafka on the Shore"
    },
    {
        "author": "Charles Bukowski",
        "price": 22.48,
        "title": "Pulp"
    }
]

Solution 3:[3]

I have recently released a new tool called json-cpp-gen. It parses C++ structures that you provide (currently not classes because they tend to have private data) and automatically generates highly efficient JSON parsers and serializers for those structures (as C++ code). I believe this might be exactly what you are looking for.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Detheroc