70

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.

tgt
  • 1,308
  • 1
  • 10
  • 16
  • 1
    Which libraries have you looked at? So we know _what_ you find complex – sehe Nov 21 '11 at 23:48
  • See http://stackoverflow.com/questions/245973/whats-the-best-c-json-parser and http://stackoverflow.com/questions/6538725/is-there-a-c-library-to-read-json-documents-into-to-c-objects (now deleted; 10k+ only) – sehe Nov 21 '11 at 23:50
  • I've updated my post. The complexity comes in needing to do a lot of work to do something I'd expect to be very simple. I really feel like I'm missing something, possibly something obvious. – tgt Nov 22 '11 at 00:16
  • You are not missing anything, this is not possible in C++ (in the form as you described). – Tamás Szelei Nov 22 '11 at 00:51
  • You might be going about this the wrong way. Such "data only" classes should probably be a `std::tuple`; and you should be able to rig up some template action to output a JSON representation of a tuple quite easily. – Kerrek SB Nov 22 '11 at 03:17
  • Its not a data only class, I just gave an example with only data. Sorry. A solution to convert a std::map containing std::maps, std::lists, std::vectors, numbers and bools to JSON would be fine too. – tgt Nov 22 '11 at 11:23
  • There are libraries that can do this using adaptors. See this answer: http://stackoverflow.com/a/19957475/214777 – Sebastian Nov 13 '13 at 15:31
  • The same problem comes to me today. Hope it helps http://stackoverflow.com/questions/26773043/how-to-write-a-template-converts-vector-to-jsonvalue-jsoncpp – tryer3000 Nov 06 '14 at 08:52

12 Answers12

29

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.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • 4
    I'd like to be able to do this without having to copy every value into a separate JSON object. – tgt Nov 22 '11 at 00:06
  • @Ca1icoJack: you don't have to copy. You can _adapt_ your existing structures. It will require work, as Tamás pointed out, because C++ has no reflection – sehe Nov 22 '11 at 00:20
  • 2
    Would you mind elaborating on what you mean by _adapt_. Do you mean using the JSON Spirit Object instead of std::map? I'm new to C++, used to several languages which must use reflection (which I'm currently reading about). Thanks. – tgt Nov 22 '11 at 00:31
17

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++.

Martin York
  • 257,169
  • 86
  • 333
  • 562
Sebastian
  • 4,802
  • 23
  • 48
  • For others: if you are a newbie like me, don't try this library. It's terrible to use. I spent almost 4 hours try to run the simple example but nothing works. Even in the simple exmaple given by the author, there it's missing a declaration. I tried the homebrew one with fresh installed Ubuntu, it failed during `brew install xxx`. Then I tried downloading the header-only branch and compile successfully but got error in linking. Finally I tried to download the whole repo and make install, but in the `.configure` part, I just could not install all the dependencies in a easy way. – Rick Oct 14 '20 at 17:25
  • I gave up finally and decided to use another language to do my task. If anyone can write step-by-step tutorial about how to get this running. Please let me know. – Rick Oct 14 '20 at 17:27
  • @Rick If you still care, I am the author more than happy to help. Add an issue here: https://github.com/Loki-Astari/ThorsSerializer/issues – Martin York Jan 08 '22 at 01:27
  • @MartinYork Thank you Martin. I would probably come back to this when I learn reflection or somthing related in C++ :P. Nowadays I work on Python mostly. – Rick Jan 08 '22 at 03:01
4

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).

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Tamás Szelei
  • 23,169
  • 18
  • 105
  • 180
  • Sorry for the confusion. Preferably I'd like to do object to JSON, but constructing a map and storing it was an alternative option. I can't for the life of me figure out how to use JSON Spirit to do map to JSON. It worked for vector/list to JSON. – tgt Nov 22 '11 at 00:08
  • This answer is correct, C++ will never has a library such as Jackson that can convert a object to JSON without writing code tightly coupled to the specified class. – soulmachine May 13 '15 at 17:43
4

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.

ytj
  • 688
  • 5
  • 11
  • @Gelldur I don't have plan to support JSON array. Since ACML is pretty small (and clean!?), you could implement this feature youself, and your contribution is welcome. – ytj Apr 23 '13 at 13:07
2

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

Community
  • 1
  • 1
Robert
  • 2,330
  • 29
  • 47
  • 1
    Whilst this may theoretically answer the question, [it would be preferable](//meta.stackoverflow.com/q/8259) to include the essential parts of the answer here, and provide the link for reference. – Bhargav Rao Feb 09 '16 at 15:45
  • Well, you only need to head to cereal via the link to find the answer quickly . No need to downvote for that, really? But fine, updated with a link to another similar question, but regarding cereal. – Robert Feb 09 '16 at 20:07
  • Nope, I didn't downvote. Someone must have flagged your post as VLQ, hence a edit automatically triggered a downvote. Also I would upvote if I still had votes left for the day – Bhargav Rao Feb 09 '16 at 20:11
  • 1
    It's really hard to offend me. A few improvements. 1. Don't start with *Have you looked at ...*, you are answering a post and not asking for clarification. 2. Add a sample example from the other answer (or make one of your own) and add the link at the bottom for reference. (If you do these, I will certainly come back tomo to upvote the answer) – Bhargav Rao Feb 09 '16 at 20:22
  • @Robert . I upvoted to make up for whoever downvoted your answer. I'll be looking at cereal and the example tomorrow to see if it will work for me. Thanks. – kizeloo May 17 '16 at 20:16
  • @kizeloo, Thanks mate! :) – Robert May 18 '16 at 07:28
2

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.
burrata
  • 91
  • 1
  • 3
2

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.

Beached
  • 1,608
  • 15
  • 18
1

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

JBV06
  • 304
  • 2
  • 5
1

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"
    ]
}
mect
  • 98
  • 7
0

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.

TheNitesWhoSay
  • 167
  • 1
  • 5
  • How do I add that to a VisualStudio 2019 Project? – Tom Jun 26 '21 at 22:05
  • Just add the https://github.com/TheNitesWhoSay/RareCpp/blob/master/RareCppLib/Reflect.h and https://github.com/TheNitesWhoSay/RareCpp/blob/master/RareCppLib/Json.h heders to your project and include them where needed; also right click on your project -> properties and under "General" ensure C++ Language Standard is set to C++17 or higher – TheNitesWhoSay Jun 28 '21 at 06:41
-1

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

chris
  • 398
  • 2
  • 11
-1

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.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
kola
  • 310
  • 2
  • 10
  • Don't forget you have to implement the json_io member function, where you map the struct members to members in the JSON. This is due to the lack of reflection in C++. – Martijn Otto Aug 09 '18 at 06:52