I'm creating a C++14 header-only JSON library for fun, learning experience and to use it in my personal projects.

I'm looking for feedback on:

  • Its API/interface: since I'm using modern C++, I want the API to be as expressive and easy to write/read as possible. I also want it to be terse.

  • Its implementation: I'm trying to make the library's performance acceptable, but not looking to make the "fastest" library. It would be great to get feedback on how to improve the performance. I'm also making use of move semantics and since I'm not 100% sure of my perfect forwarding functions' correctness I'm also looking for feedback on them.

The full source code is available here. The JSON library is a module of a bigger "utility" library called SSVUtils.

Here's a quick rundown of the JSON module's files:

  • Json/Json.hpp: "Main" module header file. The only file to be included by the user.
  • Json/Common/Common.hpp: Typedefs and aliases.
  • Json/Common/VecMap.hpp: Simple map-like container implementation that uses a sorted vector as the underlying storage.
  • Json/Io/*: JSON reader/writer implementations.
  • Json/Num/Num.hpp*: Data structure intended for the storage of number. Uses an union. Can store in three different representations: IntS (signed integer), IntU (unsigned integer), Real (double).
  • Json/Num/NumHelper.hpp: Helper struct to get/set Num instances from C++ numeric types.
  • Json/Val/Val.hpp: The "core" of the JSON library. A "value" object that uses an union to store one of these types:
    • Obj: Key/value JSON map.
    • Arr: JSON array.
    • Str: Generic string - implemented with std::string.
    • Num: Numeric type.
    • Bln: Boolean type;
    • Nll: Null type;
  • Json/Val/Internal/Cnv.hpp: defines conversions between C++ types and JSON structures. You can think of these conversions as serializations/de-serializations. The user can define its own conversions. Conversions do not create new values - they either fill an existing C++ object taken by non-const reference from a Json::Val, or they fill a Json::Val from an existing C++ object.
  • Json/Val/Internal/AsHelper.hpp: defines copy-conversions between C++ types and JSON structures. Usually just creates a new C++ object, fills it using its Cnv converter, then returns it.
  • Json/Val/Internal/Chk.hpp: runtime checkers to determine whether a JSON value is storing a specific C++ type.
  • Json/Val/Internal/ValItrHelper.hpp: iteration helper struct that helps with iterating over JSON values.
  • Json/Val/Internal/CnvFuncs.hpp: helper functions to automatically convert from/to a JSON value depending on its type.

Syntax usage example:

// Simple struct that we're going to serialize/deserialize
struct Person
    std::string name, surname;
    int age;

// Macro that opens/closes conversion namespace
    // Macro that defines a converter for Person
    // `mV` is the name of the variable that will refer to the JSON value
    // `mX` is the name of the variable that will refer to the Person value
    SSVJ_CNV(Person, mV, mX)
        // Converting a Person to JSON simply writes/reads a JSON array
        // containing its name, surname and age
        ssvj::cnvArr(mV, mX.name, mX.surname, mX.age);

int main()
    // Initialize a JSON array
    ssvj::Val people{ssvj::Arr{}}; 

    // Emplace C++ objects in the JSON array
    people.emplace<Person>("John", "Doe", 35);
    people.emplace<Person>("Bill", "Gates", 75);

    // Get copies of the C++ objects from the JSON array
    auto johnDoe(people[0].as<Person>());
    auto billGates(people[1].as<Person>());

    // Modify the copy and put it back into the JSON array
    billGates.age = 50;    
    people[1] = billGates;

    assert(people[1][0].as<std::string>() == "Bill");
    assert(people[1][1].as<std::string>() == "Gates");
    assert(people[1][2].as<int>() == 50);

    // Iterate every item of the array as a `Person`
    for(const auto& p : people.forArrAs<Person>())
        std::cout << "Name: " << p.name << "\n"
                  << "Surname: " << p.surname << "\n"
                  << "Age: " << p.age << "\n\n";

    // Write and read from file
    auto people2(ssvj::Val::fromFile("/tmp/people.json"));

    assert(people == people2); 

  • What do you think about the user syntax? Can it be improved?
  • Could the implementation be improved? Are move semantics being used correctly?
  • Can the conversion of types be better both in its implementation and syntax?
  • 3
    \$\begingroup\$ If you are interested here is my Json Serializer/DeSerializer. It allows you to declare what members of a class get serialized in one statement. Then you can just serialize/de-serialize object with a stream. Have a look at the README.md \$\endgroup\$ Oct 15, 2014 at 15:14
  • 2
    \$\begingroup\$ I think it would be better if you posted separate modules of the library for review. It is very hard for someone to review the whole library from the repo. Try posting a few specific parts on a couple other questions. Include the code in the question. You're likely to get a lot more and more complete reviews. \$\endgroup\$
    – glampert
    Oct 16, 2014 at 18:00
  • 1
    \$\begingroup\$ Relying on macros is not modern C++ in my book. There is no need for macro voodoo. Is your goal to de/serialize JSON data or manage the JSON tree? \$\endgroup\$
    – aggsol
    Oct 20, 2014 at 8:59
  • \$\begingroup\$ @CodeClown: But... I'm not relying on macros. The macros you see in the example user code exist only to make the code (much) shorter. The user is free to write the code without macros, but 3 nested namespaces must be opened. My goal is both to serialize and deserialize JSON data and also manage JSON objects directly. \$\endgroup\$ Oct 20, 2014 at 14:34
  • 5
    \$\begingroup\$ In the question as-is right now we can only review the usage code. We can't review the library. We also can't really answer your questions. Please include the relevant library code in the question. \$\endgroup\$
    – Pimgd
    Oct 22, 2014 at 10:19

1 Answer 1


Your setup seems overly complicated. For just the de/serializing a minimal approach would be easier to understand. Especially if the the de/serializer support STL containers and streams. That requires every object to implemente a serialization interface or just the stream operator.

std::vector< Person > persons;

JsonSerializer serializer;
serializer << persons;

std::ofstream ofs;
ofs.open ("test.json");
ofs << serializer;

std::string json = serializer.ToString();

JsonDeserializer deserializer(json);
deserilizer >> person;

// or just use a file stream;
std::ifstream ifs;
ifs.open ("test.txt", std::ifstream::in);

JsonDeserializer deserializer(ifs);
deserilizer >> person;

  • \$\begingroup\$ Fixed typos, added some clarifications. \$\endgroup\$
    – aggsol
    Oct 24, 2014 at 8:29

Not the answer you're looking for? Browse other questions tagged or ask your own question.