{"id":49592183,"url":"https://github.com/byebyebryan/lazy-serializable","last_synced_at":"2026-05-04T01:30:59.156Z","repository":{"id":350722740,"uuid":"1104817549","full_name":"byebyebryan/lazy-serializable","owner":"byebyebryan","description":"The laziest way to add serialization to C++ data classes.","archived":false,"fork":false,"pushed_at":"2026-04-11T20:06:52.000Z","size":65,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-11T20:11:01.391Z","etag":null,"topics":["binary","cmake","cpp","cpp20","data-serialization","header-only","json","prototyping","serialization","single-header","toml","yaml"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/byebyebryan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-11-26T18:22:26.000Z","updated_at":"2026-04-11T20:06:56.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/byebyebryan/lazy-serializable","commit_stats":null,"previous_names":["byebyebryan/lazy-serializable"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/byebyebryan/lazy-serializable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byebyebryan%2Flazy-serializable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byebyebryan%2Flazy-serializable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byebyebryan%2Flazy-serializable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byebyebryan%2Flazy-serializable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/byebyebryan","download_url":"https://codeload.github.com/byebyebryan/lazy-serializable/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byebyebryan%2Flazy-serializable/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32591596,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T22:12:39.696Z","status":"ssl_error","status_checked_at":"2026-05-03T22:09:10.534Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["binary","cmake","cpp","cpp20","data-serialization","header-only","json","prototyping","serialization","single-header","toml","yaml"],"created_at":"2026-05-04T01:30:58.577Z","updated_at":"2026-05-04T01:30:59.148Z","avatar_url":"https://github.com/byebyebryan.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lazy-serializable\n\n[![CI](https://github.com/byebyebryan/lazy-serializable/actions/workflows/ci.yml/badge.svg)](https://github.com/byebyebryan/lazy-serializable/actions/workflows/ci.yml)\n[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![C++20](https://img.shields.io/badge/C%2B%2B-20-blue.svg)](https://isocpp.org/)\n[![Header Only](https://img.shields.io/badge/header--only-yes-green.svg)]()\n\nProvides the **laziest** way to add serialization to C++ data classes.\n\nAdding serialization to a project often introduces unwanted friction: learning a complex IDL, setting up code generation steps, or maintaining repetitive `load`/`save` boilerplate.\n\n**lazy-serializable** solves this by letting you declare fields **once**, inline, using a simple macro. It effectively \"glues\" your data structures to a wide range of formats—JSON, Binary, Text, YAML, TOML—without requiring you to change your data model or build process.\n\nDesigned for rapid prototyping and simple projects, it offers:\n- **Zero Friction**: Header-only, no external dependencies (for built-in adapters), no build steps.\n- **Code-First**: No `.proto` or schema files; your C++ struct is the source of truth.\n- **Multi-Format**: Switch between human-readable JSON/Text (for debugging) and compact Binary (for release) instantly.\n- **Composition**: Automatically handles nested objects, `std::vector`, and sealed third-party types.\n- **Extensible**: Don't see the format you need? Writing a custom adapter is trivial (often ~50 lines) and works instantly with all your existing data types.\n\n### What it doesn't do\n- **No Pointer Chasing**: Focuses on value types and composition; does not handle pointers, references, or object graphs with cycles.\n- **No Schema Validation**: Assumes data fits the structure; validation is left to the underlying backend or user logic.\n- **No Built-in Versioning**: You control your data evolution (e.g., by adding version fields).\n\n## Quick start (single header)\n\nThe recommended distribution is the amalgamated header `include/lazy_serializable.h`. Drop that file into your project (or include it via your package manager) and pick the adapter you need.\n\n```cpp\n#include \"lazy_serializable.h\"\n\n// Define your data with one macro per field\nstruct SensorConfig : lazy::JsonSerializable\u003cSensorConfig\u003e {\n  LAZY_SERIALIZABLE_FIELD(std::string, name, \"default\");\n  LAZY_SERIALIZABLE_FIELD(int, sample_rate_hz, 1);\n  LAZY_SERIALIZABLE_FIELD(bool, enabled, true);\n};\n\nint main() {\n  SensorConfig cfg;\n  cfg.name = \"temp\";\n\n  // Serialize to JSON\n  std::cout \u003c\u003c cfg; \n  // Output: {\"name\":\"temp\",\"sample_rate_hz\":1,\"enabled\":true}\n\n  // Deserialize from JSON\n  std::stringstream input(R\"({\"name\":\"new\",\"sample_rate_hz\":10,\"enabled\":false})\");\n  input \u003e\u003e cfg;\n}\n```\n\n### Nested objects and sealed types\n\nNested types work automatically as long as they inherit from `lazy::Serializable`. For external/“sealed” types, use `LAZY_SERIALIZABLE_TYPE`.\n\n```cpp\n// Standard lazy-serializable struct\nstruct Address : lazy::JsonSerializable\u003cAddress\u003e {\n  LAZY_SERIALIZABLE_FIELD(std::string, city, \"\");\n};\n\n// External struct (e.g. from a 3rd party lib)\nstruct User {\n  std::string name;\n  Address home;\n};\n\n// Register the external type non-intrusively\nnamespace lazy::serializable {\n  LAZY_SERIALIZABLE_TYPE(JsonAdapter, User, name, home);\n}\n```\n\n## MultiSerializable (optional)\n\n`lazy::MultiSerializable` registers fields for multiple adapters at once, allowing you to switch formats on the fly.\n\n```cpp\n#include \"lazy_serializable.h\"\n\n// Register for ALL enabled adapters\nstruct Record : lazy::MultiSerializable\u003cRecord\u003e {\n  LAZY_MULTI_SERIALIZABLE_FIELD(std::string, id, \"\");\n  LAZY_MULTI_SERIALIZABLE_FIELD(int, value, 0);\n};\n\nRecord rec;\nrec.id = \"A1\";\nrec.value = 42;\n\n// Serialize to JSON\nrec.serialize\u003clazy::serializable::JsonAdapter\u003e(std::cout);\n// Output: {\"id\":\"A1\",\"value\":42}\n\n// Serialize to Binary\nrec.serialize\u003clazy::serializable::BinaryAdapter\u003e(std::cout);\n// Output: [compact binary data]\n```\n\nTo control which adapters MultiSerializable uses, define `LAZY_MULTI_SERIALIZABLE_ADAPTERS` before including the header, or override the relevant `LAZY_SERIALIZABLE_ENABLE_*` toggles to restrict which adapters are compiled in.\n\n## Installation and CMake integration\n\n### Single-header distribution\n\n- Copy `include/lazy_serializable.h` into your project or install the package from your package manager.\n- Configure feature toggles either via preprocessor defines before the include or via CMake options if you use the project as a subdirectory.\n\n### As a subproject (add_subdirectory / FetchContent)\n\n```cmake\nadd_subdirectory(lazy-serializable)\n\nadd_executable(my_app main.cpp)\ntarget_link_libraries(my_app PRIVATE lazy::serializable)\n```\n\nAll optional adapters that are enabled at configure time are available automatically; link their interface targets if you need them explicitly (e.g. `lazy::serializable::rapid-json`).\n\n### Installed package (find_package)\n\nAfter installing the project (headers and CMake package config), you can consume it via:\n\n```cmake\nfind_package(lazy-serializable CONFIG REQUIRED)\n\nadd_executable(my_app main.cpp)\ntarget_link_libraries(my_app PRIVATE lazy::serializable)\n```\n\nWhen using the project as a subdirectory, adapter-specific interface targets such as\n`lazy::serializable::rapid-json`, `lazy::serializable::nlohmann-json`,\n`lazy::serializable::yaml`, and `lazy::serializable::toml` are also available when those\nadapters are enabled at configure time.\n\n## Adapter overview\n\n| Adapter | Header | Macro | Dependencies | CMake target |\n|---------|--------|-------|--------------|--------------|\n| Lazy JSON (default) | `lazy/adapters/json_lazy.h` | `LAZY_SERIALIZABLE_ENABLE_LAZY_JSON` (default `1`) | none | `lazy::serializable` |\n| RapidJSON | `lazy/adapters/json_rapid.h` | `LAZY_SERIALIZABLE_ENABLE_RAPID_JSON` | RapidJSON | `lazy::serializable::rapid-json` |\n| nlohmann/json | `lazy/adapters/json_nlohmann.h` | `LAZY_SERIALIZABLE_ENABLE_NLOHMANN_JSON` | nlohmann/json | `lazy::serializable::nlohmann-json` |\n| Binary | `lazy/adapters/binary.h` | `LAZY_SERIALIZABLE_ENABLE_BINARY` (default `1`) | none | `lazy::serializable` |\n| Text (key/value) | `lazy/adapters/text.h` | `LAZY_SERIALIZABLE_ENABLE_TEXT` (default `1`) | none | `lazy::serializable` |\n| YAML | `lazy/adapters/yaml.h` | `LAZY_SERIALIZABLE_ENABLE_YAML` | fkYAML | `lazy::serializable::yaml` |\n| TOML | `lazy/adapters/toml.h` | `LAZY_SERIALIZABLE_ENABLE_TOML` | toml++ | `lazy::serializable::toml` |\n\n### Adapter limitations\n\n- **Lazy JSON**: minimal JSON implementation, trades completeness for small size; precision is limited by `std::stod` and strings with `\\uXXXX` escapes are round-tripped as `?` in those positions.\n- **Binary**: compact, order-dependent format; uses host endianness (only portable across machines with the same endianness).\n- **Text**: human-readable key/value format; does not support arrays of objects or sealed types that contain arrays.\n- **YAML/TOML/RapidJSON/nlohmann/json**: rely on the underlying libraries for exact syntax/semantics; lazy-serializable adds only a thin mapping layer.\n\nChoose the JSON backend by defining one of:\n\n```cpp\n#define LAZY_SERIALIZABLE_JSON_BACKEND_RAPIDJSON      // requires LAZY_SERIALIZABLE_ENABLE_RAPID_JSON=1\n// or\n#define LAZY_SERIALIZABLE_JSON_BACKEND_NLOHMANN_JSON  // requires LAZY_SERIALIZABLE_ENABLE_NLOHMANN_JSON=1\n```\n\nIf neither is defined, the lightweight builtin LazyJsonAdapter is used.\nWhen using the single header, set the corresponding `LAZY_SERIALIZABLE_ENABLE_*` macro to `1` before including it so the adapter code is available.\n\n## Feature toggles\n\nAll major components can be enabled/disabled via preprocessor defines before including the headers or via CMake options with the same names.\n\n| Macro | Default (single header) | Purpose |\n|-------|-------------------------|---------|\n| `LAZY_SERIALIZABLE_ENABLE_MULTI` | `1` | Provide `lazy::MultiSerializable` |\n| `LAZY_SERIALIZABLE_ENABLE_LAZY_JSON` | `1` | Include `LazyJsonAdapter` |\n| `LAZY_SERIALIZABLE_ENABLE_BINARY` | `1` | Include `BinaryAdapter` |\n| `LAZY_SERIALIZABLE_ENABLE_TEXT` | `1` | Include `TextAdapter` |\n| `LAZY_SERIALIZABLE_ENABLE_RAPID_JSON` | `0` (dev CMake defaults to ON) | Include RapidJSON adapter |\n| `LAZY_SERIALIZABLE_ENABLE_NLOHMANN_JSON` | `0` (dev CMake defaults to ON) | Include nlohmann/json adapter |\n| `LAZY_SERIALIZABLE_ENABLE_YAML` | `0` (dev CMake defaults to ON) | Include YAML adapter |\n| `LAZY_SERIALIZABLE_ENABLE_TOML` | `0` (dev CMake defaults to ON) | Include TOML adapter |\n\nDefine any of them to `0` to trim unused code from the single header. Define to `1` (and provide the required dependency) to enable optional adapters.\n\n## Development workflow\n\nThe modular headers under `src/lazy/...` are the canonical source for contributors. The amalgamated `lazy_serializable.h` is generated from them and should not be edited manually.\n\nSee `AGENTS.md` for contributor workflow, conventions, and project structure.\n\n### Generating the single header\n\n- Manually: `python3 scripts/generate_single_header.py`\n- Via CMake target: `cmake --build build --target lazy-serializable-single-header`\n- Via the helper script: `./build.sh --regen-single-header`\n\n### Running the tests\n\n`build.sh` and the provided `CMakeLists.txt` are intended for development. They:\n\n1. Enable all optional adapters by default (and fetch dependencies through `FetchContent`).\n2. Build from the modular headers.\n3. Provide an opt-in mode to run the test suite against the generated single header as well (`./build.sh --single-header-tests` or `-DLAZY_SERIALIZABLE_TEST_SINGLE_HEADER=ON`).\n\nUse these scripts when contributing; for packaging, use the generated single header (or copy the few modular headers you need) and configure the feature toggles to match your environment.\n\n## Custom adapters\n\nOne of the core design goals is extensibility. If you need a format not supported out of the box (e.g., MessagePack, XML, or a custom protocol), you don't need to fork the library.\n\nJust implement a class with four methods (`fromStream`, `toStream`, `writeField`, `readField`), and it will immediately work with all `lazy::Serializable` features (nested types, arrays, sealed types).\n\n```cpp\nclass CsvAdapter {\n public:\n  static CsvAdapter fromStream(std::istream\u0026 is);\n  void toStream(std::ostream\u0026 os) const;\n\n  template \u003ctypename T\u003e\n  void writeField(const char* name, const T\u0026 value);\n\n  template \u003ctypename T\u003e\n  void readField(const char* name, T\u0026 out);\n};\n\nstruct CsvRow : lazy::Serializable\u003cCsvRow, CsvAdapter\u003e {\n  LAZY_SERIALIZABLE_FIELD(std::string, symbol, \"\");\n  LAZY_SERIALIZABLE_FIELD(double, price, 0.0);\n};\n```\n\nOnce your adapter implements `writeField`/`readField` and `fromStream`/`toStream`, all `Serializable`/`MultiSerializable` features (nested types, sealed types, vectors, etc.) work automatically.\n\n## Best practices\n\n- Always specify meaningful default values in `LAZY_SERIALIZABLE_FIELD` to keep backward compatibility when new fields are added.\n- Include versioning fields for on-disk formats to detect schema changes early.\n- When using `MultiSerializable`, keep the adapter list minimal to avoid unnecessary code size or dependency pulls.\n- For sealed/external types, prefer registering adapters near the type definition inside `namespace lazy::serializable` to ensure ADL finds the helpers.\n- BinaryAdapter uses the host’s endianness; if you need cross-endian portability, add an explicit byte-order conversion step on read/write.\n\n## License\n\nlazy-serializable is available under the MIT License. See [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyebyebryan%2Flazy-serializable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbyebyebryan%2Flazy-serializable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyebyebryan%2Flazy-serializable/lists"}