Skip to content

Formatters & Code

JSON to C++ Struct

C++ structs with std::optional and nlohmann::json.

Runs in your browser
JSON · source
lines: 17chars: 261size: 261 B
C++ struct · result
lines: 22chars: 408size: 408 B
live

Understanding JSON → C++

Header-only, macro-driven, surprisingly ergonomic.

Why nlohmann/json became the de-facto C++ JSON library, and the ADL trick that makes struct-to-JSON look like operator overloading magic.

nlohmann/json is the default choice.

The de-facto C++ JSON library is Niels Lohmann's single-header nlohmann/json. Header-only (drop the file in, no build-system surgery), modern (C++17 minimum), and unusually ergonomic for C++. Alternatives like RapidJSON are faster and lower-allocation but require more careful usage; for almost all application code, the default choice is nlohmann/json.

NLOHMANN_JSON_SERIALIZE — the macro.

The library defines a macro, NLOHMANN_DEFINE_TYPE_INTRUSIVE, that generates the to_json and from_json free functions for your struct in one line. ADL (argument-dependent lookup) then makes json j = my_struct; work automatically, just like an overloaded operator. The codegen output is a struct definition followed by one macro invocation per struct. Concise and unobtrusive.

Optional types for nullable fields.

C++17's std::optional<T> is the right type for a JSON field that might be missing or null. The library round-trips it correctly: a present, non-null value populates the optional; a missing or null field leaves it empty. Without optional, a missing field in the input causes a parse error (or worse, a default-constructed value masquerading as real data). Codegen tools should default optional on every nullable-by-schema field.

A worked example.

From { "user_id": 7, "name": "Q", "avatar": null }: #include <nlohmann/json.hpp> #include <optional> #include <string> struct User { int user_id; std::string name; std::optional<std::string> avatar; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(User, user_id, name, avatar); The struct fields use snake_case to match the JSON keys (or use the _WITH_DEFAULT variant for explicit name maps). The macro generates the serialization functions. json j = u; and User u2 = j; both work.

One struct + one macro

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(T, fields...)

The macro emits to_json/from_json free functions for the struct.

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(User, user_id, name, avatar);

= Round-trippable type

INTRUSIVE vs NON_INTRUSIVE.

Two variants of the macro. NLOHMANN_DEFINE_TYPE_INTRUSIVE goes inside the struct definition and needs access to private members. NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE goes outside the struct and only works on public members. Use intrusive when fields are private; non-intrusive when they're public (and you'd rather not change the struct itself).

Custom types and free functions.

For more complex types — enums, chrono durations, third-party types — write your own to_json / from_json overloads in the same namespace as the type. ADL finds them automatically. The library doesn't care whether the implementation came from a macro or a hand-written function; the contract is the two functions exist.

Performance — when to look elsewhere.

nlohmann/json is fast enough for most use cases but allocates more than it needs to. For hot paths in performance-critical code (serializing millions of records per second, parsing huge files), RapidJSON's SAX-style API or simdjson's vectorised parser can be 5-20× faster. The trade-off is ergonomics: those libraries require manual node traversal rather than struct-to-JSON automation. Default to nlohmann; switch only when profiling says you must.

Frequently asked questions

Quick answers.

Which C++ version is required?

The generated code uses `std::optional`, which requires C++17 or later. For older standards, you may need to manually replace these with pointers or third-party optional types.

Do I need any external libraries?

Yes. The generated code is designed to work with the `nlohmann::json` library, also known as JSON for Modern C++, which is a popular header-only library.

How are null values handled?

Fields that are null or missing in the JSON input are automatically wrapped in `std::optional` to reflect their nullable status in the resulting struct.

Can it handle nested objects?

Yes. The tool recursively parses the JSON and creates separate structs for nested objects, ensuring clear organisation and reusability of types.

People also search for

Related tools

More in this room.

See all in Formatters & Code