1# Arbitrary Types Conversions 2 3Every type can be serialized in JSON, not just STL containers and scalar types. Usually, you would do something along those lines: 4 5```cpp 6namespace ns { 7 // a simple struct to model a person 8 struct person { 9 std::string name; 10 std::string address; 11 int age; 12 }; 13} 14 15ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60}; 16 17// convert to JSON: copy each value into the JSON object 18json j; 19j["name"] = p.name; 20j["address"] = p.address; 21j["age"] = p.age; 22 23// ... 24 25// convert from JSON: copy each value from the JSON object 26ns::person p { 27 j["name"].get<std::string>(), 28 j["address"].get<std::string>(), 29 j["age"].get<int>() 30}; 31``` 32 33It works, but that's quite a lot of boilerplate... Fortunately, there's a better way: 34 35```cpp 36// create a person 37ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60}; 38 39// conversion: person -> json 40json j = p; 41 42std::cout << j << std::endl; 43// {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} 44 45// conversion: json -> person 46auto p2 = j.get<ns::person>(); 47 48// that's it 49assert(p == p2); 50``` 51 52## Basic usage 53 54To make this work with one of your types, you only need to provide two functions: 55 56```cpp 57using nlohmann::json; 58 59namespace ns { 60 void to_json(json& j, const person& p) { 61 j = json{ {"name", p.name}, {"address", p.address}, {"age", p.age} }; 62 } 63 64 void from_json(const json& j, person& p) { 65 j.at("name").get_to(p.name); 66 j.at("address").get_to(p.address); 67 j.at("age").get_to(p.age); 68 } 69} // namespace ns 70``` 71 72That's all! When calling the `json` constructor with your type, your custom `to_json` method will be automatically called. 73Likewise, when calling `get<your_type>()` or `get_to(your_type&)`, the `from_json` method will be called. 74 75Some important things: 76 77* Those methods **MUST** be in your type's namespace (which can be the global namespace), or the library will not be able to locate them (in this example, they are in namespace `ns`, where `person` is defined). 78* Those methods **MUST** be available (e.g., proper headers must be included) everywhere you use these conversions. Look at [issue 1108](https://github.com/nlohmann/json/issues/1108) for errors that may occur otherwise. 79* When using `get<your_type>()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.) 80* In function `from_json`, use function [`at()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a93403e803947b86f4da2d1fb3345cf2c.html#a93403e803947b86f4da2d1fb3345cf2c) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior. 81* You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these. 82 83 84## Simplify your life with macros 85 86If you just want to serialize/deserialize some structs, the `to_json`/`from_json` functions can be a lot of boilerplate. 87 88There are two macros to make your life easier as long as you (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object: 89 90- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)` is to be defined inside of the namespace of the class/struct to create code for. 91- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` is to be defined inside of the class/struct to create code for. This macro can also access private members. 92 93In both macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. 94 95!!! note 96 97 At most 64 member variables can be passed to `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` or `NLOHMANN_DEFINE_TYPE_INTRUSIVE`. 98 99??? example 100 101 The `to_json`/`from_json` functions for the `person` struct above can be created with: 102 103 ```cpp 104 namespace ns { 105 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age) 106 } 107 ``` 108 109 Here is an example with private members, where `NLOHMANN_DEFINE_TYPE_INTRUSIVE` is needed: 110 111 ```cpp 112 namespace ns { 113 class address { 114 private: 115 std::string street; 116 int housenumber; 117 int postcode; 118 119 public: 120 NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode) 121 }; 122 } 123 ``` 124 125## How do I convert third-party types? 126 127This requires a bit more advanced technique. But first, let's see how this conversion mechanism works: 128 129The library uses **JSON Serializers** to convert types to json. 130The default serializer for `nlohmann::json` is `nlohmann::adl_serializer` (ADL means [Argument-Dependent Lookup](https://en.cppreference.com/w/cpp/language/adl)). 131 132It is implemented like this (simplified): 133 134```cpp 135template <typename T> 136struct adl_serializer { 137 static void to_json(json& j, const T& value) { 138 // calls the "to_json" method in T's namespace 139 } 140 141 static void from_json(const json& j, T& value) { 142 // same thing, but with the "from_json" method 143 } 144}; 145``` 146 147This serializer works fine when you have control over the type's namespace. However, what about `boost::optional` or `std::filesystem::path` (C++17)? Hijacking the `boost` namespace is pretty bad, and it's illegal to add something other than template specializations to `std`... 148 149To solve this, you need to add a specialization of `adl_serializer` to the `nlohmann` namespace, here's an example: 150 151```cpp 152// partial specialization (full specialization works too) 153namespace nlohmann { 154 template <typename T> 155 struct adl_serializer<boost::optional<T>> { 156 static void to_json(json& j, const boost::optional<T>& opt) { 157 if (opt == boost::none) { 158 j = nullptr; 159 } else { 160 j = *opt; // this will call adl_serializer<T>::to_json which will 161 // find the free function to_json in T's namespace! 162 } 163 } 164 165 static void from_json(const json& j, boost::optional<T>& opt) { 166 if (j.is_null()) { 167 opt = boost::none; 168 } else { 169 opt = j.get<T>(); // same as above, but with 170 // adl_serializer<T>::from_json 171 } 172 } 173 }; 174} 175``` 176 177## How can I use `get()` for non-default constructible/non-copyable types? 178 179There is a way, if your type is [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible). You will need to specialize the `adl_serializer` as well, but with a special `from_json` overload: 180 181```cpp 182struct move_only_type { 183 move_only_type() = delete; 184 move_only_type(int ii): i(ii) {} 185 move_only_type(const move_only_type&) = delete; 186 move_only_type(move_only_type&&) = default; 187 188 int i; 189}; 190 191namespace nlohmann { 192 template <> 193 struct adl_serializer<move_only_type> { 194 // note: the return type is no longer 'void', and the method only takes 195 // one argument 196 static move_only_type from_json(const json& j) { 197 return {j.get<int>()}; 198 } 199 200 // Here's the catch! You must provide a to_json method! Otherwise you 201 // will not be able to convert move_only_type to json, since you fully 202 // specialized adl_serializer on that type 203 static void to_json(json& j, move_only_type t) { 204 j = t.i; 205 } 206 }; 207} 208``` 209 210## Can I write my own serializer? (Advanced use) 211 212Yes. You might want to take a look at [`unit-udt.cpp`](https://github.com/nlohmann/json/blob/develop/test/src/unit-udt.cpp) in the test suite, to see a few examples. 213 214If you write your own serializer, you'll need to do a few things: 215 216- use a different `basic_json` alias than `nlohmann::json` (the last template parameter of `basic_json` is the `JSONSerializer`) 217- use your `basic_json` alias (or a template parameter) in all your `to_json`/`from_json` methods 218- use `nlohmann::to_json` and `nlohmann::from_json` when you need ADL 219 220Here is an example, without simplifications, that only accepts types with a size <= 32, and uses ADL. 221 222```cpp 223// You should use void as a second template argument 224// if you don't need compile-time checks on T 225template<typename T, typename SFINAE = typename std::enable_if<sizeof(T) <= 32>::type> 226struct less_than_32_serializer { 227 template <typename BasicJsonType> 228 static void to_json(BasicJsonType& j, T value) { 229 // we want to use ADL, and call the correct to_json overload 230 using nlohmann::to_json; // this method is called by adl_serializer, 231 // this is where the magic happens 232 to_json(j, value); 233 } 234 235 template <typename BasicJsonType> 236 static void from_json(const BasicJsonType& j, T& value) { 237 // same thing here 238 using nlohmann::from_json; 239 from_json(j, value); 240 } 241}; 242``` 243 244Be **very** careful when reimplementing your serializer, you can stack overflow if you don't pay attention: 245 246```cpp 247template <typename T, void> 248struct bad_serializer 249{ 250 template <typename BasicJsonType> 251 static void to_json(BasicJsonType& j, const T& value) { 252 // this calls BasicJsonType::json_serializer<T>::to_json(j, value); 253 // if BasicJsonType::json_serializer == bad_serializer ... oops! 254 j = value; 255 } 256 257 template <typename BasicJsonType> 258 static void to_json(const BasicJsonType& j, T& value) { 259 // this calls BasicJsonType::json_serializer<T>::from_json(j, value); 260 // if BasicJsonType::json_serializer == bad_serializer ... oops! 261 value = j.template get<T>(); // oops! 262 } 263}; 264``` 265