Converting C++ class to JSON

C++JsonSerialization

C++ Problem Overview


I'd like to create a JSON string containing the instance variables of my class.

For example,

class Example {  
    std::string string;  
    std::map<std::string, std:string> map;  
    std::vector<int> vector;  
};

would become:

{
    "string":"the-string-value",
    "map": {
        "key1":"val1",
        "key2":"val2"
    },
    "vector":[1,2,3,4]
}

I've looked into several C++ libraries for creating JSON and they all seem incredibly complex. I'd like something similar to Javascript's JSON.stringify(object). In other words just pass a std::map to it and receive a string. The map could contain other maps, vectors, lists, strings, numbers and bools.

What's the nicest way to do this?

Thanks for your help.

Edit

I've looked into the following:

json spirit, jsoncpp, zoolib, JOST, CAJUN, libjson, nosjob, JsonBox, jsonme--

Which I understand I can construct a separate JSON object as in an answer below and convert to JSON I'd like to be able to store my stuff in standard collections and convert.

Edit 2

Okay, scrap the idea of serializing a class since it appears that's impossible with C++'s lack of reflection.

Is there a nice way to convert a std::map containing std:maps, std::vectors, std::lists, numbers, strings, and bools to JSON without having to change datatypes or copying data to a new datatype?

Thanks.

C++ Solutions


Solution 1 - C++

JSON Spirit would allow you to do it like so:

Object addr_obj;

addr_obj.push_back( Pair( "house_number", 42 ) );
addr_obj.push_back( Pair( "road",         "East Street" ) );
addr_obj.push_back( Pair( "town",         "Newtown" ) );

ofstream os( "address.txt" );
os.write( addr_obj, os, pretty_print );
os.close();

Output:

{
    "house_number" : 42,
    "road" : "East Street",
    "town" : "Newtown"
}

The json_map_demo.cpp would be a nice place to start, I suppose.

Solution 2 - C++

Any good C++ JSON library should do this and it is sad to see that they don't -- with the exception of ThorsSerializer and apparently Nosjob as mentioned in this question.

Of course, C++ does not have reflection like Java, so you have to explicitly annotate your types:
(copied from the ThorsSerializer documentation)

#include "ThorSerialize/JsonThor.h"
#include "ThorSerialize/SerUtil.h"
#include <map>
#include <vector>
#include <string>
#include <iostream>

class Example {
    std::string string;
    std::map<std::string, std::string> map;
    std::vector<int> vector;

    // Allow access to the class by the serialization library.
    friend class ThorsAnvil::Serialize::Traits<Example>;

    public:
        Example(std::string const& s, std::map<std::string, std::string> const& m, std::vector<int> const& v)
            : string(s), map(m), vector(v)
        {}
};

// Define what members need to be serilizable
ThorsAnvil_MakeTrait(Example, string, map, vector);

Example Usage:

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


    Example     e1 {"Some Text", {{"ace", "the best"}, {"king", "second best"}}, {1 ,2 ,3, 4}};

    // Simply serialize object to json using a stream.
    std::cout << jsonExport(e1) << "\n";

    // Deserialize json text from a stream into object.
    std::cin  >> jsonImport(e1);
}

Running:

{
	"string": "Some Text",
	"map":
	{
		"ace": "the best",
		"king": "second best"
	},
	"vector": [ 1, 2, 3, 4]
}

You cannot do better than this in C++.

Solution 3 - C++

Do you want to JSON-ify a map or an object? (your example shows a class, yet you say a map). For a map, check out this library - JSON Spirit.

For objects: There is no reflection support in C++ (apart from the very limited RTTI), so there is no "one-click" solution for serialization either. Any solution will require you to write additional, possibly tightly coupled code to the class you want to serialize and de-serialize (that depends on if you want to serialize non-public data).

Solution 4 - C++

I wrote a library which designed to solve your problem. However, it is a very new project, not stable enough. Feel free to take a look, the homepage is here::

https://github.com/Mizuchi/acml

In your example, you have to add one line like this:

ACML_REGISTER(Example, ,(string)(map)(vector));

in order to tell the library which member you want to dump. Since C++ have no reflection. And you must give a way to access the member, either use public member level or use friend class.

And later you just need to do sth like this:

string result = acml::json::dumps(any_object);

would become::

{
    "string": "the-string-value",
    "map":
    {
        "key1": "val1",
        "key2": "val2"
    },
    "vector":
    {
        "type": "std::vector",
        "size": "4",
        "0": "1",
        "1": "2",
        "2": "3",
        "3": "4"
    }
}

As you see, JSON array is not implemented yet. And everything becomes string now.

Solution 5 - C++

Have you looked at cereal (http://uscilab.github.io/cereal/) ? It has JSON archives for serializing to/from JSON using C++.

An example with minimal overhead (from cereal) can be found here on SO: https://stackoverflow.com/a/22587527/255635

Solution 6 - C++

This is a very light-weight solution that allows you to selectively store member variables of your existing classes / structs without the need of manually creating and maintaining your keys and values in a custom dictionary. It only uses nlohmann json (no boost or other heavy library).

nlohmann::json allows you to have arbitrary type conversions. It's described here https://nlohmann.github.io/json/features/arbitrary_types/ in more detail.

Code:

// example.cc
#include <string>
#include <iostream>
#include <map>
#include <vector>

#include "third_party/nlohmann/json.hpp"

struct Example {
    std::string name;
    std::map<std::string, int> dict;
    std::vector<int> vec;
    std::string this_member_is_not_part_of_the_json;
};

// Use NLOHMANN_DEFINE_TYPE_INTRUSIVE if your members are private.
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Example,
    name,
    dict,
    vec);

int main() {
  Example example;
  example.name = "Example";
  example.dict = {{"AnyKey", 42}};
  example.vec = {1, 2, 3};

  nlohmann::json json;
  to_json(json, example);  // "generated" function

  std::cout << json.dump(1) << std::endl;

  return 0;
}

Compile the code:

g++ -I third_party/nlohmann/ example.cc -o example

Running ./example outputs:

{
 "dict": {
  "AnyKey": 42
 },
 "name": "Example",
 "vec": [
  1,
  2,
  3
 ]
}

Explanation:

Note, how I used NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE outside of the struct definition this generates the function to_json(...) in the same namespace.

  • json.dump(1) converts the json to a formatted string.

Solution 7 - C++

I have written an experimental library that can do the job, but it requires external description of classes structures and hierarchy. It uses GCCXML to build an xml dictionary, used for serialization de-serialization :

http://code.google.com/p/cjson/

It's for the moment an experimental project, that can deals with fundamental types (int, float double), pointers to fundamentals types, classes, inherited members etc ... It implements basic std::vector ans std::map serialization, and also std::string instances.

See details for implementation here

Solution 8 - C++

You could use Boost.PropertyTree.

#include <map>
#include <vector>
#include <string>
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

namespace pt = boost::property_tree;

int main() {
    // Create an empty property tree object.
    pt::ptree tree;

    // Put a string value into the tree.
    tree.put("string", "the-string-value");

    // Put a map object into the tree.
    pt::ptree child1;
    std::map<std::string, std::string> map = {{"key1", "val1"},
                                              {"key2", "val2"}};
    for (auto &p : map) {
        child1.add(p.first, p.second);
    }
    tree.add_child("map", child1);

    // Put a vector of numbers into the tree
    pt::ptree child2;
    std::vector<int> vector = {1, 2, 3, 4};
    for (auto &v : vector) {
        pt::ptree item;
        item.put("", v);
        child2.push_back(std::make_pair("", item));
    }
    tree.add_child("vector", child2);

    // Write property tree to JSON file
    pt::write_json("output.json", tree);

    return 0;
}

Output:

{
    "string": "the-string-value",
    "map": {
        "key1": "val1",
        "key2": "val2"
    },
    "vector": [
        "1",
        "2",
        "3",
        "4"
    ]
}

Solution 9 - C++

In RareCpp I've created a very effective JSON Library on top of a reflection implementation. It's written for C++17 and works with Visual Studios, g++, and Clang. The library is header only, meaning you need only copy the reflect and json headers into your project to use it.

The JSON library only requires that you list the fields once in the REFLECT macro; from there it auto identifies appropriate JSON output representations for reading or writing, and can recursively traverse any reflected fields, as well as arrays, STL containers, references, pointers, and more.

struct MyOtherObject { int myOtherInt; REFLECT(MyOtherObject, myOtherInt) };
struct MyObject
{
    int myInt;
    std::string myString;
    MyOtherObject myOtherObject;
    std::vector<int> myIntCollection;

    REFLECT(MyObject, myInt, myString, myOtherObject, myIntCollection)
};

int main()
{
    MyObject myObject = {};
    std::cout << "Enter MyObject:" << std::endl;
    std::cin >> Json::in(myObject);
    std::cout << std::endl << std::endl << "You entered:" << std::endl;
    std::cout << Json::pretty(myObject);
}

The above could be ran like so...

Enter MyObject:
{
  "myInt": 1337, "myString": "stringy", "myIntCollection": [2,4,6],
  "myOtherObject": {
    "myOtherInt": 9001
  }
}


You entered:
{
  "myInt": 1337,
  "myString": "stringy",
  "myOtherObject": {
    "myOtherInt": 9001
  },
  "myIntCollection": [ 2, 4, 6 ]
}

You can also annotate fields to do things like give them a different name in the JSON representation, force them to be strings, and so on.

struct Point
{
    NOTE(latitude, Json::Name{"lat"})
    double latitude;

    NOTE(longitude, Json::Name{"long"})
    double longitude;

    REFLECT(Point, latitude, longitude)
};

See here for more examples, there are many other features such as capturing super classes, support for reading, traversing, and writing JSON not known at compile time, further customizing JSON representations for specific fields or types, and more.

Solution 10 - C++

I am the author of https://github.com/beached/daw_json_link . You are correct that C++ does not have reflection at this time and it would be nice for getting the simple stuff out of the way. But by defining a simple declarative mapping, JSON Link will provide a type checked parser for your type(s). For example, the class you specified, could be mapped like:

    class Example {  
      std::string string;  
      std::map<std::string, std:string> map;  
      std::vector<int> vector;  
    };
        
    namespace daw::json {
      template<> 
      struct json_data_contract<Example> {
        using type = json_member_list<
          json_link<"string", std::string>, 
          json_link<"map", std::map<std::string, std::string>>
          json_link<"vector", std::vector<int>>>;
        
        static inline auto to_json_data( Example const & v ) {
          return std::forward_as_tuple( v.string, v.map, v.vector );
        }
      };
    }

From here you can use this mapping inside another as a json_class<"Name", Example>. To serialize as you ask it is just a matter of auto json_document = daw::json::to_json( MyExampleValue ) or to parse it daw::json::from_json<Example>( json_document );. The beauty of the library is that it generates a custom parser for your type and does type checking of the data as it is parsing.

Solution 11 - C++

this python script generates c++ pod classes with one member for each json property

you want quite the opposite thing, but is trivial to generate a mapping class which does both loading and saving

generated code relies on an external json parser library

Solution 12 - C++

If the question is still actual, then look at json_dto library, a small header-only helper for converting data between JSON representation and c++ structs.

For example having the following structs:

struct message_source_t
{
  // Worker thread.
  std::int32_t m_thread_id;

  // Sender.
  std::string m_subsystem;
};

struct message_t
{
  // Who sent a message.
  message_source_t m_from;

  // When the message was sent (unixtime).
  std::tm m_when;
  
  // Message text.
  std::string m_text;
};

with the help of json_dto you can create the following JSON:

{
  "from" : 
    {
      "thread_id" : 4242,
      "sybsystem" : "json_dto"
    },
  "when" : "2016.09.28 19:55:00",
  "text" : "Hello world!"
}  

And given such JSON string you can convert it to structs.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontgtView Question on Stackoverflow
Solution 1 - C++seheView Answer on Stackoverflow
Solution 2 - C++SebastianView Answer on Stackoverflow
Solution 3 - C++Tamás SzeleiView Answer on Stackoverflow
Solution 4 - C++ytjView Answer on Stackoverflow
Solution 5 - C++RobertView Answer on Stackoverflow
Solution 6 - C++burrataView Answer on Stackoverflow
Solution 7 - C++JBV06View Answer on Stackoverflow
Solution 8 - C++mectView Answer on Stackoverflow
Solution 9 - C++TheNitesWhoSayView Answer on Stackoverflow
Solution 10 - C++BeachedView Answer on Stackoverflow
Solution 11 - C++chrisView Answer on Stackoverflow
Solution 12 - C++kolaView Answer on Stackoverflow