3.9 KiB
sqlgen::JSON
sqlgen::JSON<T> 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:
struct Person {
std::string first_name;
std::string last_name;
int age;
sqlgen::JSON<std::optional<std::vector<Person>>> children; // Nested, optional JSON
};
This generates a table with a JSON-compatible column type (dialect-specific):
-- 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:
const auto children = std::vector<Person>({
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<T>std::vector<T>,std::map<K,V>, and other standard containers- Nested
structtypes reflected with reflectcpp
Reading and Writing
Use the regular sqlgen::write and sqlgen::read APIs. JSON values are transparently serialized/deserialized.
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<Person> | if_exists)
.and_then(write(std::ref(homer)))
.and_then(sqlgen::read<std::vector<Person>>)
.value();
Accessing Values
Access the underlying C++ value via familiar methods:
// 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:
- T: Any reflectcpp-supported C++ type to store as JSON
sqlgen::JSON<T> field_name;
Database Integration
sqlgen::JSON<T> 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::JSONsupport), including deeply nested structures - Integrates with
sqlgen::readandsqlgen::writelike 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