# `sqlgen::JSON` `sqlgen::JSON` stores structured JSON data in your database while preserving full C++ type-safety. It integrates with reflectcpp (`rfl`) so you can read and write arbitrary structured data without manual (de)serialization. Critically, `rfl::JSON` is fully serializable with reflectcpp and supports any data type also supported by reflectcpp (including nested structs, `std::optional`, containers like `std::vector`, and more), enabling seamless end-to-end integration. ## Usage ### Basic Definition Define a JSON field in your struct by wrapping the underlying C++ type with `sqlgen::JSON`: ```cpp struct Person { std::string first_name; std::string last_name; int age; sqlgen::JSON>> children; // Nested, optional JSON }; ``` This generates a table with a JSON-compatible column type (dialect-specific): ```sql -- PostgreSQL CREATE TABLE IF NOT EXISTS "Person"( "first_name" TEXT NOT NULL, "last_name" TEXT NOT NULL, "age" INTEGER NOT NULL, "children" JSONB ); -- MySQL / MariaDB CREATE TABLE IF NOT EXISTS `Person`( `first_name` TEXT NOT NULL, `last_name` TEXT NOT NULL, `age` INT NOT NULL, `children` JSON ); -- SQLite CREATE TABLE IF NOT EXISTS "Person"( "first_name" TEXT NOT NULL, "last_name" TEXT NOT NULL, "age" INTEGER NOT NULL, "children" JSONB ); ``` ### Construction and Assignment Assign any reflectcpp-supported value to the JSON field. For example, a nested vector of the same type: ```cpp const auto children = std::vector({ Person{.first_name = "Bart", .last_name = "Simpson", .age = 10}, Person{.first_name = "Lisa", .last_name = "Simpson", .age = 8}, Person{.first_name = "Maggie", .last_name = "Simpson", .age = 0} }); const auto homer = Person{ .first_name = "Homer", .last_name = "Simpson", .age = 45, .children = children // Automatically serialized to JSON }; ``` You can store any `T` that reflectcpp can serialize, such as: - `std::optional` - `std::vector`, `std::map`, and other standard containers - Nested `struct` types reflected with reflectcpp ### Reading and Writing Use the regular `sqlgen::write` and `sqlgen::read` APIs. JSON values are transparently serialized/deserialized. ```cpp using namespace sqlgen; using namespace sqlgen::literals; // PostgreSQL example (analogous for MySQL/SQLite) const auto credentials = sqlgen::postgres::Credentials{ .user = "postgres", .password = "password", .host = "localhost", .dbname = "postgres"}; const auto people = sqlgen::postgres::connect(credentials) .and_then(drop | if_exists) .and_then(write(std::ref(homer))) .and_then(sqlgen::read>) .value(); ``` ### Accessing Values Access the underlying C++ value via familiar methods: ```cpp // Underlying value access people[0].children(); people[0].children.get(); people[0].children.value(); // Serialize entire result to JSON with reflectcpp const std::string json = rfl::json::write(people); ``` ### Template Parameters The `sqlgen::JSON` template takes one parameter: 1. T: Any reflectcpp-supported C++ type to store as JSON ```cpp sqlgen::JSON field_name; ``` ### Database Integration `sqlgen::JSON` selects the appropriate JSON-capable storage per dialect: - **PostgreSQL**: `JSONB` - **MySQL/MariaDB**: `JSON` - **SQLite**: `JSONB` All (de)serialization is handled by reflectcpp (`rfl`) underneath, ensuring type-safe transformations without manual glue code. ## Notes - Works with any reflectcpp-serializable type (`rfl::JSON` support), including deeply nested structures - Integrates with `sqlgen::read` and `sqlgen::write` like any other field - Database JSON type is chosen automatically per dialect - Supports move and copy semantics - Provides multiple access methods for the underlying value - Ideal for flexible, schema-evolving nested attributes