A C++20 library for fast serialization, deserialization and validation using reflection. Supports JSON, Avro, Boost Serialization, BSON, Cap'n Proto, CBOR, Cere
# reflect-cpp
** Documentation**: https://rfl.getml.com
**reflect-cpp** is a C++-20/C++-26 library for **fast serialization, deserialization and validation** using reflection, similar to [pydantic](https://github.com/pydantic/pydantic) in Python, [serde](https://github.com/serde-rs) in Rust, [encoding](https://github.com/golang/go/tree/master/src/encoding) in Go or [aeson](https://github.com/haskell/aeson/tree/master) in Haskell.
reflect-cpp supports C++-26 reflection, but most of the functionality is also available in C++-20, except where explicitly noted otherwise.
Moreover, reflect-cpp is the basis for [sqlgen](https://github.com/getml/sqlgen), a **modern, type-safe ORM and SQL query generator** for C++20, inspired by Python's SQLAlchemy/SQLModel and Rust's Diesel. It provides a fluent, composable interface for database operations with compile-time type checking and SQL injection protection.
reflect-cpp and sqlgen fill important gaps in C++ development. They reduce boilerplate code and increase code safety. Together, they enable reliable and efficient ETL pipelines.
### Design principles for reflect-cpp include:
- Close integration with [containers](https://github.com/getml/reflect-cpp?tab=readme-ov-file#support-for-containers) from the C++ standard library
- Close adherence to C++ idioms
- Out-of-the-box support for [JSON](https://rfl.getml.com/supported_formats/json)
- Simple [installation](https://rfl.getml.com/install)
- Simple extendability to [other serialization formats](https://rfl.getml.com/supported_formats/supporting_your_own_format)
- Simple extendability to [custom classes](https://rfl.getml.com/concepts/custom_classes)
- Being one of the fastest serialization libraries in existence, as demonstrated by our [benchmarks](https://rfl.getml.com/benchmarks)
## Table of Contents
### On this page
- [Serialization formats](#serialization-formats)
- [Feature Overview](#feature-overview)
- [Simple Example](#simple-example)
- [More Comprehensive Example](#more-comprehensive-example)
- [Tabular data](#tabular-data)
- [CLI argument parsing](#cli-argument-parsing)
- [Error messages](#error-messages)
- [JSON schema](#json-schema)
- [Enums](#enums)
- [Algebraic data types](#algebraic-data-types)
- [Extra fields](#extra-fields)
- [Reflective programming](#reflective-programming)
- [Standard Library Integration](#support-for-containers)
- [The team behind reflect-cpp](#the-team-behind-reflect-cpp)
- [License](#license)
### More in our [documentation](https://rfl.getml.com):
- [Installation ↗](https://rfl.getml.com/install/#option-2-compilation-using-cmake)
- [C++26 reflection ↗](https://rfl.getml.com/cpp26_reflection)
- [Benchmarks ↗](https://rfl.getml.com/benchmarks)
- [How to contribute ↗](https://rfl.getml.com/contributing)
- [Compiling and running the tests ↗](https://rfl.getml.com/contributing/#compiling-and-running-the-tests)
## Serialization formats
reflect-cpp provides a unified reflection-based interface across different serialization formats. It is deliberately designed in a very modular way, using [concepts](https://en.cppreference.com/w/cpp/language/constraints), to make it as easy as possible to interface various C or C++ libraries related to serialization. Refer to the [documentation](https://rfl.getml.com/supported_formats/bson/) for details.
The following table lists the serialization formats currently supported by reflect-cpp and the underlying libraries used:
| Format | Library | Version | License | Remarks |
|---------------------|------------------------------------------------------|--------------|------------| -----------------------------------------------------|
| JSON | [yyjson](https://github.com/ibireme/yyjson) | >= 0.8.0 | MIT | out-of-the-box support, included in this repository |
| Avro | [avro-c](https://avro.apache.org/docs/1.11.1/api/c/) | >= 1.11.3 | Apache 2.0 | Schemaful binary format |
| Boost.Serialization | [Boost.Serialization](https://www.boost.org/doc/libs/release/libs/serialization/) | >= 1.74.0 | BSL 1.0 | Streaming binary format with archive interop |
| BSON | [libbson](https://github.com/mongodb/mongo-c-driver) | >= 1.25.1 | Apache 2.0 | JSON-like binary format |
| Cap'n Proto | [capnproto](https://capnproto.org) | >= 1.0.2 | MIT | Schemaful binary format |
| CBOR | [jsoncons](https://github.com/danielaparker/jsoncons)| >= 0.176.0 | BSL 1.0 | JSON-like binary format |
| cli | *(none)* | *(none)* | MIT | Command line interface |
| env | *(none)* | *(none)* | MIT | Environment variables |
| Cereal | [Cereal](https://uscilab.github.io/cereal/) | >= 1.3.2 | BSD | C++ serialization library with multiple formats |
| CSV | [Apache Arrow](https://arrow.apache.org/) | >= 21.0.0 | Apache 2.0 | Tabular textual format |
| flexbuffers | [flatbuffers](https://github.com/google/flatbuffers) | >= 23.5.26 | Apache 2.0 | Schema-less version of flatbuffers, binary format |
| msgpack | [msgpack-c](https://github.com/msgpack/msgpack-c) | >= 6.0.0 | BSL 1.0 | JSON-like binary format |
| parquet | [Apache Arrow](https://arrow.apache.org/) | >= 21.0.0 | Apache 2.0 | Tabular binary format |
| TOML | [toml++](https://github.com/marzer/tomlplusplus) | >= 3.4.0 | MIT | Textual format with an emphasis on readability |
| UBJSON | [jsoncons](https://github.com/danielaparker/jsoncons)| >= 0.176.0 | BSL 1.0 | JSON-like binary format |
| XML | [pugixml](https://github.com/zeux/pugixml) | >= 1.14 | MIT | Textual format used in many legacy projects |
| YAML | [yaml-cpp](https://github.com/jbeder/yaml-cpp) | >= 0.8.0 | MIT | Textual format with an emphasis on readability |
| yas | [yas](https://github.com/niXman/yas) | >= 7.1.0 | BSL 1.0 | Very fast and compact serialization library |
Support for more serialization formats is in development. Refer to the [issues](https://github.com/getml/reflect-cpp/issues) for details.
Please also refer to the *conanfile.py* or *vcpkg.json* in this repository.
## Feature Overview
### Simple Example
```cpp
#include
#include
struct Person {
std::string first_name;
std::string last_name;
int age;
};
const auto homer =
Person{.first_name = "Homer",
.last_name = "Simpson",
.age = 45};
// We can now write into and read from a JSON string.
const std::string json_string = rfl::json::write(homer);
auto homer2 = rfl::json::read(json_string).value();
```
The resulting JSON string looks like this:
```json
{"first_name":"Homer","last_name":"Simpson","age":45}
```
You can transform the field names from `snake_case` to `camelCase` like this:
```cpp
const std::string json_string =
rfl::json::write(homer);
auto homer2 =
rfl::json::read(json_string).value();
```
The resulting JSON string looks like this:
```json
{"firstName":"Homer","lastName":"Simpson","age":45}
```
Or you can use another format, such as YAML.
```cpp
#include
// ... (same as above)
const std::string yaml_string = rfl::yaml::write(homer);
auto homer2 = rfl::yaml::read(yaml_string).value();
```
The resulting YAML string looks like this:
```yaml
first_name: Homer
last_name: Simpson
age: 45
```
This will work for just about any example in the entire documentation
and any of the following formats, except where explicitly noted otherwise:
```
…
```
### More Comprehensive Example
```
…
```
This results in the following JSON string:
```json
{"firstName":"Homer","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":45,"email":"
[email protected]","children":[{"firstName":"Bart","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":10,"email":"
[email protected]","children":[]},{"firstName":"Lisa","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":8,"email":"
[email protected]","children":[]},{"firstName":"Maggie","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":0,"email":"
[email protected]","children":[]}]}
```
We can also create structs from the string:
```cpp
auto homer2 = rfl::json::read(json_string).value();
// Fields can be accessed like this:
std::cout << "Hello, my name is " << homer.first_name() << " "
<< homer.last_name() << "." << std::endl;
// Since homer2 is mutable, we can also change the values like this:
homer2.first_name = "Marge";
std::cout << "Hello, my name is " << homer2.first_name() << " "
<< homer2.last_name() << "." << std::endl;
```
### Tabular data
reflect-cpp also supports tabular data formats, like CSV or Parquet:
```
…
```
This will resulting CSV will look like this:
```
"first_name","last_name","town","birthday","age","email"
"Bart","Simpson","Springfield",1987-04-19,10,"
[email protected]"
"Lisa","Simpson","Springfield",1987-04-19,8,"
[email protected]"
"Maggie","Simpson","Springfield",1987-04-19,0,"
[email protected]"
"Homer","Simpson","Springfield",1987-04-19,45,"
[email protected]"
```
### Environment variables
reflect-cpp can also read from and write to environment variables using `rfl::env::read` and `rfl::env::write`:
```cpp
#include
struct Config {
std::string host;
int port;
bool verbose;
};
const auto config = Config{.host = "localhost", .port = 8080, .verbose = true};
rfl::env::write(config);
// Sets HOST=localhost, PORT=8080, VERBOSE=true
const auto config2 = rfl::env::read().value();
```
Nested structs are flattened with `_` as a separator (e.g., `DATABASE_HOST`).
Arrays use `_INDEX` suffix (e.g., `TAGS_0`, `TAGS_1`).
Enums are serialized via their enumerator name (e.g., `"green"` for `Color::green`).
All field names are converted to uppercase by default.
### CLI argument parsing
reflect-cpp can also parse command-line arguments directly into structs using `rfl::cli::read`:
```cpp
#include
struct Config {
std::string host_name;
int port;
bool verbose;
std::vector tags;
};
int main(int argc, char* argv[]) {
const auto config = rfl::cli::read(argc, argv).value();
// ./app --host-name=localhost --port=8080 --verbose --tags=a,b,c
}
```
Field names are automatically converted from `snake_case` to `kebab-case` (`host_name` matches `--host-name`).
You can mark fields as positional arguments with `rfl::Positional` and add single-character aliases with `rfl::Short<"x", T>`:
```cpp
struct Config {
rfl::Positional input_file;
rfl::Short<"o", std::string> output_dir;
rfl::Short<"v", bool> verbose;
int count;
};
// ./app data.csv -o /tmp/out -v --count=10
```
Nested structs, `std::optional`, `std::vector`, enums, `rfl::Flatten` and `rfl::Rename` are all supported. Refer to the [documentation](https://rfl.getml.com/cli) for details.
### Error messages
reflect-cpp returns clear and comprehensive error messages:
```cpp
const std::string faulty_json_string =
R"({"firstName":"Homer","lastName":12345,"town":"Springfield","birthday":"04/19/1987","age":145,"email":"h