Perhaps the JSON module from CCAN? http://ccodearchive.net/ It doesn't even depend on anything else from CCAN, and consists of exactly two files json.c and json.h
(The JSON module is here http://git.ozlabs.org/?p=ccan;a=tree;f=ccan/json )
Answer from Spudd86 on Stack Overflowjemi: a compact JSON serializer for embedded systems
C++ JSON Serialization - Stack Overflow
How do I turn a C# object into a JSON string in .NET? - Stack Overflow
Modern C++ JSON serialization library recommendations
Videos
Short form: I'm announcing (and welcoming comments for) jemi, a lightweight, pure-C JSON serializer.
Context: I needed to emit rather complex compound JSON data for a C-based project I'm working on. I could have done it all with sprintf(), but it got messy quickly. I looked at available libraries such as jansson and CCAN's json, but they both use malloc(), which isn't an option in my case.
So I created jemi ("Json EMItter"): you use jemi primitives to build up the JSON structure that you need, then call jemi_emit() to "walk the tree" and dump the resulting JSON string to the output of your choice.
Two files. No malloc. Unit tests included. MIT license. Comments welcome!
For C++20, just use a reflection library like boost-ext-reflect (not part of boost). C++ supports full reflection of aggregate types today.
For C++14, read the old answer.
There is no reflection in C++. True. But if the compiler can't provide you the metadata you need, you can provide it yourself.
Let's start by making a property struct:
template<typename Class, typename T>
struct PropertyImpl {
constexpr PropertyImpl(T Class::*aMember, const char* aName) : member{aMember}, name{aName} {}
using Type = T;
T Class::*member;
const char* name;
};
template<typename Class, typename T>
constexpr auto property(T Class::*member, const char* name) {
return PropertyImpl<Class, T>{member, name};
}
Of course, you also can have a property that takes a setter and getter instead of a pointer to member, and maybe read only properties for calculated value you'd like to serialize. If you use C++17, you can extend it further to make a property that works with lambdas.
Ok, now we have the building block of our compile-time introspection system.
Now in your class Dog, add your metadata:
struct Dog {
std::string barkType;
std::string color;
int weight = 0;
bool operator==(const Dog& rhs) const {
return std::tie(barkType, color, weight) == std::tie(rhs.barkType, rhs.color, rhs.weight);
}
constexpr static auto properties = std::make_tuple(
property(&Dog::barkType, "barkType"),
property(&Dog::color, "color"),
property(&Dog::weight, "weight")
);
};
We will need to iterate on that list. To iterate on a tuple, there are many ways, but my preferred one is this:
template <typename T, T... S, typename F>
constexpr void for_sequence(std::integer_sequence<T, S...>, F&& f) {
using unpack_t = int[];
(void)unpack_t{(static_cast<void>(f(std::integral_constant<T, S>{})), 0)..., 0};
}
If C++17 fold expressions are available in your compiler, then for_sequence can be simplified to:
template <typename T, T... S, typename F>
constexpr void for_sequence(std::integer_sequence<T, S...>, F&& f) {
(static_cast<void>(f(std::integral_constant<T, S>{})), ...);
}
This will call a function for each constant in the integer sequence.
If this method don't work or gives trouble to your compiler, you can always use the array expansion trick.
Now that you have the desired metadata and tools, you can iterate through the properties to unserialize:
// unserialize function
template<typename T>
T fromJson(const Json::Value& data) {
T object;
// We first get the number of properties
constexpr auto nbProperties = std::tuple_size<decltype(T::properties)>::value;
// We iterate on the index sequence of size `nbProperties`
for_sequence(std::make_index_sequence<nbProperties>{}, & {
// get the property
constexpr auto property = std::get<i>(T::properties);
// get the type of the property
using Type = typename decltype(property)::Type;
// set the value to the member
// you can also replace `asAny` by `fromJson` to recursively serialize
object.*(property.member) = Json::asAny<Type>(data[property.name]);
});
return object;
}
And for serialize:
template<typename T>
Json::Value toJson(const T& object) {
Json::Value data;
// We first get the number of properties
constexpr auto nbProperties = std::tuple_size<decltype(T::properties)>::value;
// We iterate on the index sequence of size `nbProperties`
for_sequence(std::make_index_sequence<nbProperties>{}, & {
// get the property
constexpr auto property = std::get<i>(T::properties);
// set the value to the member
data[property.name] = object.*(property.member);
});
return data;
}
If you want recursive serialization and unserialization, you can replace asAny by fromJson.
Now you can use your functions like this:
Dog dog;
dog.color = "green";
dog.barkType = "whaf";
dog.weight = 30;
Json::Value jsonDog = toJson(dog); // produces {"color":"green", "barkType":"whaf", "weight": 30}
auto dog2 = fromJson<Dog>(jsonDog);
std::cout << std::boolalpha << (dog == dog2) << std::endl; // pass the test, both dog are equal!
Done! No need for run-time reflection, just some C++14 goodness!
This code could benefit from some improvement, and could of course work with C++11 with some adjustments.
Note that one would need to write the asAny function. It's just a function that takes a Json::Value and call the right as... function, or another fromJson.
Here's a complete, working example made from the various code snippet of this answer. Feel free to use it.
As mentioned in the comments, this code won't work with msvc. Please refer to this question if you want a compatible code: Pointer to member: works in GCC but not in VS2015
For that you need reflection in C/C++, which doesn't exist. You need to have some meta data describing the structure of your classes (members, inherited base classes). For the moment C/C++ compilers don't automatically provide that information in built binaries.
I had the same idea in mind, and I used GCC XML project to get this information. It outputs XML data describing class structures. I have built a project and I'm explaining some key points in this page :
Serialization is easy, but we have to deal with complex data structure implementations (std::string, std::map for example) that play with allocated buffers. Deserialization is more complex and you need to rebuild your object with all its members, plus references to vtables ... a painful implementation.
For example you can serialize like this:
// Random class initialization
com::class1* aObject = new com::class1();
for (int i=0; i<10; i++){
aObject->setData(i,i);
}
aObject->pdata = new char[7];
for (int i=0; i<7; i++){
aObject->pdata[i] = 7-i;
}
// dictionary initialization
cjson::dictionary aDict("./data/dictionary.xml");
// json transformation
std::string aJson = aDict.toJson<com::class1>(aObject);
// print encoded class
cout << aJson << std::endl ;
To deserialize data it works like this:
// decode the object
com::class1* aDecodedObject = aDict.fromJson<com::class1>(aJson);
// modify data
aDecodedObject->setData(4,22);
// json transformation
aJson = aDict.toJson<com::class1>(aDecodedObject);
// print encoded class
cout << aJson << std::endl ;
Ouptuts:
>:~/cjson$ ./main
{"_index":54,"_inner": {"_ident":"test","pi":3.141593},"_name":"first","com::class0::_type":"type","com::class0::data":[0,1,2,3,4,5,6,7,8,9],"com::classb::_ref":"ref","com::classm1::_type":"typem1","com::classm1::pdata":[7,6,5,4,3,2,1]}
{"_index":54,"_inner":{"_ident":"test","pi":3.141593},"_name":"first","com::class0::_type":"type","com::class0::data":[0,1,2,3,22,5,6,7,8,9],"com::classb::_ref":"ref","com::classm1::_type":"typem1","com::classm1::pdata":[7,6,5,4,3,2,1]}
>:~/cjson$
Usually these implementations are compiler dependent (ABI Specification for example), and require external descriptions to work (GCCXML output), thus are not really easy to integrate to projects.
Since we all love one-liners
... this one depends on the Newtonsoft NuGet package, which is popular and better than the default serializer.
Newtonsoft.Json.JsonConvert.SerializeObject(new {foo = "bar"})
Documentation: Serializing and Deserializing JSON
Please Note
Microsoft recommends that you DO NOT USE JavaScriptSerializer
See the header of the documentation page:
For .NET Framework 4.7.2 and later versions, use the APIs in the System.Text.Json namespace for serialization and deserialization. For earlier versions of .NET Framework, use Newtonsoft.Json.
Original answer:
You could use the JavaScriptSerializer class (add reference to System.Web.Extensions):
using System.Web.Script.Serialization;
var json = new JavaScriptSerializer().Serialize(obj);
A full example:
using System;
using System.Web.Script.Serialization;
public class MyDate
{
public int year;
public int month;
public int day;
}
public class Lad
{
public string firstName;
public string lastName;
public MyDate dateOfBirth;
}
class Program
{
static void Main()
{
var obj = new Lad
{
firstName = "Markoff",
lastName = "Chaney",
dateOfBirth = new MyDate
{
year = 1901,
month = 4,
day = 30
}
};
var json = new JavaScriptSerializer().Serialize(obj);
Console.WriteLine(json);
}
}
I'm currently using nlohmann/json but am wondering about better alternatives. In particular I'm wondering if there's anything that leverages C++11 or newer features that offers a more Go-like experience where you can conveniently serialize and deserialize structs without having to write a bunch of JSON<->struct CRUD code to copy values back and forth.
Is there anything like this? I've searched GitHub and haven't found anything better than what I'm using.