JSON to C++ Struct Generator

Generate C++17 struct definitions from JSON with nlohmann/json serialization, std::optional for nullable fields, std::vector for arrays, and nested struct support.

Standard:

What is JSON to C++ Struct Generator?

Modern C++ has no reflection, so the compiler cannot serialize an arbitrary struct to JSON on its own. The de facto solution is nlohmann/json, a header-only library that bridges JSON and your own types through a pair of free functions, to_json and from_json, found via argument-dependent lookup (ADL). This generator produces both the struct and that bridge: it infers a C++ type for each JSON value, converts keys to snake_case, emits nested structs children-first, and — when you enable it — adds the NLOHMANN_DEFINE_TYPE_INTRUSIVE macro so a single line wires up bidirectional conversion. The macro is the load-bearing piece. NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, field1, field2, ...) expands, inside the struct, into the to_json/from_json members the library calls. Because it is intrusive it can access private members; the non-intrusive variant exists for types you cannot modify. Once defined, j.get<MyStruct>() and json(myStruct) just work, including for nested structs and std::vector, because the library recursively dispatches to each member's own conversion. The other C++-specific decision is how to model nullable JSON fields. Since C++17, std::optional<T> is the idiomatic choice — it is a value type, needs no heap allocation, and nlohmann/json serializes a nullopt as JSON null. For C++11/14 the generator falls back to std::unique_ptr<T>, which models the same may-be-absent semantics but owns a heap allocation and is move-only.

How to Use

  1. Set the root struct name and an optional namespace to keep generated types out of the global scope
  2. Pick your C++ standard — choose C++17 or later to get std::optional for nullable fields; C++11/14 falls back to std::unique_ptr
  3. Enable nlohmann/json macros to emit NLOHMANN_DEFINE_TYPE_INTRUSIVE so each struct gets automatic to_json/from_json
  4. Enable include guards if you are pasting the output straight into a .hpp header used across translation units
  5. Paste your JSON, click Generate, then add nlohmann/json to your build (single header, or find_package(nlohmann_json) with CMake)

Why Use This Tool?

Emits NLOHMANN_DEFINE_TYPE_INTRUSIVE so a single macro line gives each struct bidirectional JSON conversion via ADL
std::optional<T> for nullable fields on C++17+, with a std::unique_ptr<T> fallback for C++11/14 projects
std::vector<T> for arrays with element type inferred from the first item, recursing into nested object types
Children-first struct ordering so to_json/from_json for nested members are visible when the parent is defined
snake_case conversion plus optional namespace wrapping to match common C++ house styles
Header-only target: the output compiles against nlohmann/json with no linking step

Tips & Best Practices

  • NLOHMANN_DEFINE_TYPE_INTRUSIVE requires the struct to be default-constructible — from_json default-constructs then assigns. If you add a custom constructor, also declare a default one or the macro will not compile.
  • Prefer std::optional<T> over T* or std::unique_ptr<T> for nullable fields in C++17+: optional is a value type with no heap allocation, and nlohmann/json maps std::nullopt to JSON null automatically. Use unique_ptr only for genuinely recursive or polymorphic shapes.
  • The INTRUSIVE macro silently errors if the JSON is missing a key that maps to a non-optional field — it throws nlohmann::json::out_of_range. Use NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT to tolerate missing keys, or make those fields std::optional.
  • For a field that can be int or string (a sloppy API), use nlohmann::json itself as the field type and inspect .is_string() at runtime — the macro will round-trip it untouched.
  • to_json/from_json are found by ADL, so they must live in the same namespace as the struct. If you move the struct into a namespace, the macro handles this automatically; hand-written conversions must follow the same rule or you get an opaque template error.

Frequently Asked Questions

What is the complete JSON type to C++ type mapping?

JSON string → std::string; JSON integer → int (use int64_t for values past 2^31); JSON float → double; JSON boolean → bool; JSON null → std::optional<T> on C++17+ or std::unique_ptr<T> on C++11/14; JSON array → std::vector<T> with T inferred from the first element; JSON object → a separate nested struct. A field with mixed or unknown shape is best typed as nlohmann::json directly.

What does NLOHMANN_DEFINE_TYPE_INTRUSIVE actually do?

Placed inside a struct, NLOHMANN_DEFINE_TYPE_INTRUSIVE(MyStruct, a, b, c) expands into the to_json and from_json functions the library invokes. Because it is intrusive, it can read and write private members. After this, json j = myStruct serializes and auto s = j.get<MyStruct>() deserializes, recursing automatically into nested structs and vectors. The non-intrusive NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE variant is for types you cannot edit and must sit in the same namespace as the type.

How does from_json/to_json ADL work in nlohmann/json?

When the library needs to convert a value of type T, it calls an unqualified to_json/from_json and relies on argument-dependent lookup to find the overload in T's own namespace. This is why the conversion functions must be declared in the same namespace as the struct, and why the macro generates them there. ADL is what lets the library convert your custom types without you ever registering them centrally — the right overload is discovered from the argument type.

Should I use std::optional or a raw/unique pointer for nullable fields?

On C++17 and later use std::optional<T>: it is a stack value type, communicates may-be-absent in the type system, and nlohmann/json round-trips std::nullopt as JSON null. Reach for std::unique_ptr<T> only when the field is genuinely recursive (a tree node pointing at the same type) where a value member would be infinitely sized, or when you need polymorphism. Avoid raw pointers entirely — they carry no ownership information and leak.

How are nested objects and arrays serialized?

Each nested JSON object becomes its own struct with its own NLOHMANN_DEFINE_TYPE_INTRUSIVE line, emitted before the parent so it is visible at the point of use. JSON arrays become std::vector<T>; the library serializes a vector by calling to_json on each element, so a std::vector of your nested struct works with no extra code. The recursion bottoms out at the primitive types the library knows natively.

Is my data sent to a server?

No, all processing happens entirely in your browser. Your JSON data never leaves your device.

Real-world Examples

A user record with an optional nested address (C++17 + nlohmann/json)

The address is null until the user fills it in. With C++17 selected, the nullable nested object becomes std::optional<Address>, which the library serializes as JSON null when empty. Both structs get the INTRUSIVE macro so j.get<User>() reconstructs the whole tree in one call.

Input
{
  "id": 42,
  "name": "Ada Lovelace",
  "active": true,
  "address": null,
  "roles": ["admin", "editor"]
}
Output
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
#include <optional>

struct Address {
    std::string street;
    std::string city;
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Address, street, city)
};

struct User {
    int id;
    std::string name;
    bool active;
    std::optional<Address> address;   // JSON null -> std::nullopt
    std::vector<std::string> roles;
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(User, id, name, active, address, roles)
};

// Usage:
//   nlohmann::json j = nlohmann::json::parse(text);
//   User u = j.get<User>();
//   nlohmann::json back = u;

An array of nested objects round-tripping through std::vector

A playlist response carries a list of track objects. The Track struct gets its own conversion macro, and because the library dispatches per element, std::vector<Track> serializes and deserializes with no extra code. The duration is a float so it maps to double.

Input
{
  "playlistName": "Focus",
  "tracks": [
    { "title": "Reverie", "durationSec": 214.5 },
    { "title": "Glass", "durationSec": 198.0 }
  ]
}
Output
#include <nlohmann/json.hpp>
#include <string>
#include <vector>

struct Track {
    std::string title;
    double duration_sec;
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Track, title, duration_sec)
};

struct Playlist {
    std::string playlist_name;
    std::vector<Track> tracks;   // each element converted via Track's macro
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Playlist, playlist_name, tracks)
};

Related Tools