


A cross-platform C++20 library providing extensible C++ type serialization on top of nfx-json
Overview
nfx-serialization is a modern C++20 library that provides a powerful C++ type serialization layer built on top of the nfx-json library. It offers automatic serialization and deserialization of C++ types to/from JSON through an extensible trait system, supporting STL containers, smart pointers, optional types, and custom user-defined types - all optimized for performance across multiple platforms and compilers.
Key Features
🔄 C++ Type Serialization
- Serializer<T>: Template-based automatic serialization/deserialization with compile-time type detection
- SerializationTraits: Extensible trait system for custom type support with asymmetric read/write
- Write (serialize): Streaming JSON generation via Builder API (zero DOM overhead)
- Read (fromDocument): DOM-based deserialization via Document API (type-safe navigation)
- Type-safe: Automatic type mapping with compile-time verification
- Zero-overhead: Header-only template implementations with inline expansion and SFINAE-based dispatch
📦 Supported Types
- POD types (integers, floats, booleans, strings)
- STL containers (vector, array, list, forward_list, deque, set, unordered_set, map, unordered_map, multimap, unordered_multimap, multiset, unordered_multiset)
- Tuple types (std::tuple, std::pair)
- Variant types (std::variant, std::monostate)
- Smart pointers (unique_ptr, shared_ptr)
- Optional types (std::optional, std::nullopt)
- Views (std::span - serialization only, non-owning view)
- Custom types via SerializationTraits specialization
- Nested structures and containers
🔌 Optional nfx Library Extensions
- nfx-containers: FastHashMap, FastHashSet, PerfectHashMap, OrderedHashMap, OrderedHashSet, SmallVector
- nfx-datatypes: Int128, Decimal
- nfx-datetime: DateTime, DateTimeOffset, TimeSpan
🏗️ Architecture
nfx-serialization is built on nfx-json, which provides:
- Document: Variant-based JSON document with type-safe manipulation
- Builder: Streaming JSON generation API (SAX-like, single-pass, optimal performance)
- SchemaValidator: JSON Schema Draft 2020-12 validation
- SchemaGenerator: Automatic schema inference from samples
- PathView: Zero-copy traversal with JSON Pointer support
📊 Real-World Applications
- Configuration Management: Serialize app settings and configuration objects
- API Integration: Automatic serialization for REST/GraphQL request/response
- Data Persistence: Save/load C++ objects to/from JSON files
- IPC & Messaging: Serialize C++ structures for inter-process communication
- Game Development: Save game state, player data, inventory systems
- Database Integration: Convert C++ objects for document databases (MongoDB, etc.)
- Logging Systems: Structured logging with automatic JSON formatting
- Testing & Mocking: Easy test data generation and comparison
🌍 Cross-Platform Support
- Linux, Windows
- GCC 14+, Clang 18+, MSVC 2022+
- Thread-safe operations
- Consistent behavior across platforms
Quick Start
Requirements
- C++20 compatible compiler:
- GCC 14+ (14.2.0 tested)
- Clang 18+ (19.1.7 tested)
- MSVC 2022+ (19.44+ tested)
- CMake 3.20 or higher
CMake Integration
# --- JSON serialization support ---
option(NFX_SERIALIZATION_WITH_JSON "Enable JSON serialization support" OFF)
# --- Performance optimizations ---
option(NFX_SERIALIZATION_ENABLE_SIMD "Enable SIMD CPU optimizations" ON )
# --- Build components ---
option(NFX_SERIALIZATION_BUILD_TESTS "Build tests" OFF)
option(NFX_SERIALIZATION_BUILD_EXTENSION_TESTS "Build extension tests" OFF)
option(NFX_SERIALIZATION_BUILD_SAMPLES "Build samples" OFF)
option(NFX_SERIALIZATION_BUILD_BENCHMARKS "Build benchmarks" OFF)
option(NFX_SERIALIZATION_BUILD_DOCUMENTATION "Build Doxygen documentation" OFF)
# --- Installation & packaging ---
option(NFX_SERIALIZATION_INSTALL_PROJECT "Install project" OFF)
option(NFX_SERIALIZATION_PACKAGE_SOURCE "Enable source package generation" OFF)
Using in Your Project
Option 1: Using FetchContent (Recommended)
include(FetchContent)
FetchContent_Declare(
nfx-serialization
GIT_REPOSITORY https://github.com/nfx-libs/nfx-serialization.git
GIT_TAG main # or use specific version tag like "0.5.0"
)
FetchContent_MakeAvailable(nfx-serialization)
# Link with header-only library
target_link_libraries(your_target PRIVATE nfx-serialization::nfx-serialization)
Option 2: As a Git Submodule
# Add as submodule
git submodule add https://github.com/nfx-libs/nfx-serialization.git third-party/nfx-serialization
# In your CMakeLists.txt
add_subdirectory(third-party/nfx-serialization)
target_link_libraries(your_target PRIVATE nfx-serialization::nfx-serialization)
Option 3: Using find_package (After Installation)
find_package(nfx-serialization REQUIRED)
target_link_libraries(your_target PRIVATE nfx-serialization::nfx-serialization)
Building
Build Commands:
# Clone the repository
git clone https://github.com/nfx-libs/nfx-serialization.git
cd nfx-serialization
# Create build directory
mkdir build && cd build
# Configure with CMake
cmake .. -DCMAKE_BUILD_TYPE=Release
# Build the library
cmake --build . --config Release --parallel
# Run tests (optional)
ctest -C Release --output-on-failure
# Run benchmarks (optional)
./bin/Release/BM_JSON_Serialization
Documentation
nfx-serialization includes API documentation generated with Doxygen.
📚 Online Documentation
The complete API documentation is available online at: https://nfx-libs.github.io/nfx-serialization
Building Documentation Locally
# Configure with documentation enabled
cmake .. -DCMAKE_BUILD_TYPE=Release -DNFX_SERIALIZATION_BUILD_DOCUMENTATION=ON
# Build the documentation
cmake --build . --target nfx-serialization-documentation
Requirements
- Doxygen - Documentation generation tool
- Graphviz Dot (optional) - For generating class diagrams
Accessing Local Documentation
After building, open ./build/doc/html/index.html in your web browser.
Sample Programs
nfx-serialization includes tutorial samples demonstrating progressive JSON serialization workflows. Each sample is self-contained and follows a consistent educational format with numbered sections, inline validation, and formatted output.
Available samples:
- Sample_JsonSerializationBasics.cpp - Document and Builder API fundamentals
- Sample_JsonSerializationContainers.cpp - Automatic STL container serialization
- Sample_JsonSerializationTraits.cpp - Custom type serialization with SerializationTraits
- Sample_JsonSerializationStlContainers.cpp - Complete guide to all STL container types
- Sample_JsonSerializationNfxTypes.cpp - Complete guide to nfx library extension types (containers, datatypes, datetime)
See samples/README.md for detailed descriptions and learning path.
Building and running samples:
# Configure with samples enabled
cmake -B build -DCMAKE_BUILD_TYPE=Release -DNFX_SERIALIZATION_BUILD_SAMPLES=ON
# Build all samples
cmake --build build --config Release
# Run samples
./build/bin/Sample_JsonSerializationBasics
./build/bin/Sample_JsonSerializationContainers
./build/bin/Sample_JsonSerializationTraits
./build/bin/Sample_JsonSerializationStlContainers
./build/bin/Sample_JsonSerializationNfxTypes
Usage Examples
Basic Serialization - STL Containers
#include <vector>
#include <map>
using namespace nfx::serialization::json;
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::map<std::string, int> scores;
scores["Alice"] = 95;
scores["Bob"] = 87;
std::string scoresJson =
Serializer<
decltype(scores)>::toString(scores);
std::multimap<std::string, int> grades;
grades.insert({"Alice", 95});
grades.insert({"Alice", 92});
grades.insert({"Bob", 87});
std::string gradesJson =
Serializer<
decltype(grades)>::toString(grades);
std::multiset<int> numbers = {1, 2, 2, 3, 3, 3};
std::string numbersJson =
Serializer<
decltype(numbers)>::toString(numbers);
std::variant<int, std::string, double> value = 42;
std::string variantJson =
Serializer<
decltype(value)>::toString(value);
value = std::string("hello");
variantJson =
Serializer<
decltype(value)>::toString(value);
opts.prettyPrint = true;
Main umbrella header for nfx-serialization library.
Templated JSON serializer with compile-time type mapping.
Working with JSON Documents (from nfx-json)
using namespace nfx::json;
using namespace nfx::serialization::json;
auto docOpt = Document::fromString(R"({
"name": "John Doe",
"age": 30,
"hobbies": ["reading", "gaming"]
})");
if (!docOpt) {
std::cerr << "Invalid JSON" << std::endl;
return 1;
}
Document& doc = *docOpt;
auto name = doc.get<std::string>("/name");
auto age = doc.get<int64_t>("/age");
auto hobbiesDoc = doc.get<Document>("/hobbies");
if (hobbiesDoc) {
std::string hobbiesJson = hobbiesDoc->toString();
}
Custom Type Serialization with SerializationTraits
using namespace nfx::json;
using namespace nfx::serialization::json;
struct Person {
std::string name;
int age;
std::vector<std::string> hobbies;
};
template<>
static void serialize(const Person& person, Builder& builder) {
builder.startObject();
builder.key("name");
builder.string(person.name);
builder.key("age");
builder.number(person.age);
builder.key("hobbies");
builder.endObject();
}
static void fromDocument(
const Document& doc, Person& person) {
person.name = doc.get<std::string>("/name").value_or("");
person.age = static_cast<int>(doc.get<int64_t>("/age").value_or(0));
if (auto hobbiesDoc = doc.get<Document>("/hobbies")) {
person.hobbies = Serializer<std::vector<std::string>>().deserialize(*hobbiesDoc);
}
}
};
Person alice;
alice.name = "Alice";
alice.age = 30;
alice.hobbies = {"reading", "coding"};
Serialization traits template (forward declaration).
static void fromDocument(const Document &doc, T &obj)
Convert Document to object (deserialization).
static T fromString(std::string_view jsonStr, const Options &options={})
Deserialize object from JSON string.
static std::string toString(const T &obj, const Options &options={})
Serialize object to JSON string.
Immutable Types with Factory Deserialization
For types with deleted default constructors (e.g., types with const members or immutable objects), use the factory deserialization pattern. The serializer automatically detects which pattern to use via SFINAE:
using namespace nfx::json;
using namespace nfx::serialization::json;
struct ImmutableConfig {
const std::string endpoint;
const int port;
const bool secure;
ImmutableConfig() = delete;
ImmutableConfig(std::string ep, int p, bool s)
: endpoint{std::move(ep)},
port{p},
secure{s}
{
}
};
template<>
static void serialize(const ImmutableConfig& config, Builder& builder) {
builder.writeStartObject();
builder.write("endpoint", config.endpoint);
builder.write("port", static_cast<int64_t>(config.port));
builder.write("secure", config.secure);
builder.writeEndObject();
}
auto endpoint = doc.get<std::string>("endpoint").value();
auto port = static_cast<int>(doc.get<int64_t>("port").value());
auto secure = doc.get<bool>("secure").value();
return ImmutableConfig{std::move(endpoint), port, secure};
}
};
ImmutableConfig config{"https://api.example.com", 443, true};
Complete Example - Combining nfx-json and nfx-serialization
int main()
{
using namespace nfx::json;
using namespace nfx::serialization::json;
Document doc;
doc.set<std::string>("/title", "My Application");
doc.set<std::string>("/version", "1.0.0");
std::vector<std::string> users = {"Alice", "Bob", "Charlie"};
auto usersDoc = Document::fromString(usersJson);
if (usersDoc) {
doc.set<Document>("/users", *usersDoc);
}
doc.set<bool>("/config/debug", true);
doc.set<int64_t>("/config/timeout", 30);
std::string json = doc.toString(2);
std::cout << "Generated JSON:\n" << json << std::endl;
auto usersArrayOpt = doc.get<Array>("/users");
if (usersArrayOpt) {
std::cout << "\nUsers:" << std::endl;
for (const auto& userDoc : usersArrayOpt.value()) {
auto user = userDoc.get<std::string>("");
if (user) {
std::cout << " - " << *user << std::endl;
}
}
}
auto usersDocOpt = doc.get<Document>("/users");
if (usersDocOpt) {
usersDocOpt->toString()
);
std::cout << "\nRestored " << restored.size() << " users" << std::endl;
}
return 0;
}
Output:
Generated JSON:
{
"title": "My Application",
"version": "1.0.0",
"users": [
"Alice",
"Bob",
"Charlie"
],
"config": {
"debug": true,
"timeout": 30
}
}
Users:
- Alice
- Bob
- Charlie
Restored 3 users
Custom Traits - Extending for Your Types
nfx-serialization provides two approaches for integrating custom types:
Approach 1: SerializationTraits for Custom Objects
For types that need custom JSON representation, implement SerializationTraits with asymmetric read/write:
using namespace nfx::json;
using namespace nfx::serialization::json;
struct Point3D {
double x, y, z;
};
template<>
static void serialize(const Point3D& point, Builder& builder) {
builder.startObject();
builder.key("x");
builder.number(point.x);
builder.key("y");
builder.number(point.y);
builder.key("z");
builder.number(point.z);
builder.endObject();
}
static void fromDocument(
const Document& doc, Point3D& point) {
point.x = doc.get<double>("/x").value_or(0.0);
point.y = doc.get<double>("/y").value_or(0.0);
point.z = doc.get<double>("/z").value_or(0.0);
}
};
Point3D origin{0, 0, 0};
Approach 2: Trait Specialization for Custom Containers
If you have a custom container that implements a standard container interface, mark it as a container so the Serializer automatically handles it:
template<typename K, typename V>
class CustomHashMap {
public:
using key_type = K;
using mapped_type = V;
using value_type = std::pair<const K, V>;
V& operator[](const K& key);
auto begin() -> ;
auto end() -> ;
};
namespace nfx::serialization::json::detail {
template<typename K, typename V>
struct is_container<CustomHashMap<K,V>> : std::true_type {};
}
CustomHashMap<std::string, int> myMap;
myMap["answer"] = 42;
Required Container Interface:
For sequence containers (vector-like):
- using value_type = T;
- begin() / end() iterators
- insert(const value_type&) for deserialization
For associative containers (map-like):
- using key_type = K;, using mapped_type = V;
- using value_type = std::pair<const K, V>;
- operator[](const key_type&) for deserialization
- begin() / end() iterators
Note: See samples/Sample_JsonSerializer.cpp for complete working examples of both approaches.
Optional Extensions
nfx-serialization provides optional integration headers for other nfx libraries. These headers use conditional compilation (__has_include()) and are safe to include even if the external library is not installed:
Available Extensions
- extensions/ContainersTraits.h - Serialization support for nfx-containers types
- nfx::containers::PerfectHashMap - Compile-time perfect hash map
- nfx::containers::FastHashMap - Runtime open-addressing hash map
- nfx::containers::FastHashSet - Runtime open-addressing hash set
- nfx::containers::OrderedHashMap - Hash map with insertion order preservation
- nfx::containers::OrderedHashSet - Hash set with insertion order preservation
- nfx::containers::SmallVector - Small vector optimization (stack/heap storage)
- extensions/DatatypesTraits.h - Serialization support for nfx-datatypes types
- nfx::datatypes::Int128 - 128-bit integer (cross-platform)
- nfx::datatypes::Decimal - Fixed-point decimal arithmetic
- extensions/DateTimeTraits.h - Serialization support for nfx-datetime types
- nfx::time::DateTime - Date and time representation (ISO 8601)
- nfx::time::DateTimeOffset - Date and time with timezone offset
- nfx::time::TimeSpan - Duration/time interval
Usage
#include <nfx/containers/FastHashMap.h>
#include <nfx/datetime/DateTime.h>
using namespace nfx::serialization::json;
nfx::containers::FastHashMap<std::string, int> scores;
scores.insertOrAssign("Alice", 95);
scores.insertOrAssign("Bob", 87);
std::string json =
Serializer<
decltype(scores)>::toString(scores);
nfx::time::DateTime now = nfx::time::DateTime::now();
SerializationTraits specializations for nfx-datetime types.
SerializationTraits specializations for nfx-containers types.
Templated JSON serializer with compile-time type mapping.
Note: These extensions are header-only and zero-cost - if you don't include them or don't have the external library installed, they have no impact on compile time or binary size.
Project Structure
nfx-serialization/
├── benchmark/ # Performance benchmarks
├── cmake/ # CMake configuration modules
├── include/nfx/
│ └── serialization/json/
│ ├── Serializer.h # Main serializer class
│ ├── SerializationTraits.h # Trait specialization interface (asymmetric read/write)
│ ├── Concepts.h # C++20 concepts and type traits
│ └── extensions/ # Optional nfx library integrations
│ ├── ContainersTraits.h # nfx-containers support (FastHashMap, etc.)
│ ├── DatatypesTraits.h # nfx-datatypes support (Int128, Decimal)
│ └── DateTimeTraits.h # nfx-datetime support (DateTime, DateTimeOffset, TimeSpan)
├── samples/ # Example code and demonstrations
└── test/ # Unit tests with GoogleTest
Note: JSON core functionality (Document, SchemaValidator, SchemaGenerator, PathView) is provided by nfx-json, which is automatically fetched as a dependency.
Performance
For detailed performance metrics and benchmarks, see the benchmark documentation.
Roadmap
See TODO.md for upcoming features and project roadmap.
Changelog
See the CHANGELOG.md for a detailed history of changes, new features, and bug fixes.
License
This project is licensed under the MIT License.
Dependencies
Core Dependencies
- nfx-json: JSON library with Document, SchemaValidator, and SchemaGenerator (MIT License)
Development Dependencies
- GoogleTest: Testing framework (BSD 3-Clause License) - Development only
- Google Benchmark: Performance benchmarking framework (Apache 2.0 License) - Development only
All dependencies are automatically fetched via CMake FetchContent when building the library, tests, or benchmarks.
Updated on February 15, 2026