• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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