跳到主要内容
版本:dev

原生序列化

C++ 原生序列化是通过 .xlang(false) 选择、仅限 C++ 的编码模式。当所有写入端和读取端都是 C++,并且载荷应遵循 C++ 类型行为而非可移植的跨语言类型系统时,请使用该模式。

如果字节需要由 Java、Python、Go、Rust、JavaScript/TypeScript、C#、Swift、Dart、Scala、Kotlin 或其他非 C++ Fory 实现读取,请使用 C++ 默认模式跨语言序列化

何时使用原生序列化

以下场景使用原生序列化:

  • 载荷仅由 C++ 应用程序生成和消费。
  • 数据模型使用字符类型、无符号原生类型 ID、std::tuple、智能指针或 C++ 多态模型等 C++ 专属类型。
  • 希望获得更快的序列化和更小的体积,并且每个读取端都使用与写入端相同的 Schema。
  • C++ 专属滚动部署需要兼容的 Schema 演进。
  • 希望在 C++ 边界避免可移植跨语言类型映射的约束。

创建原生模式 Fory 实例

#include "fory/serialization/fory.h"
#include <cassert>
#include <cstdint>
#include <string>

using namespace fory::serialization;

struct Order {
int64_t id;
double amount;

bool operator==(const Order &other) const {
return id == other.id && amount == other.amount;
}
};
FORY_STRUCT(Order, id, amount);

int main() {
auto fory = Fory::builder()
.xlang(false)
.build();
fory.register_struct<Order>(100);

Order order{1, 42.5};
auto bytes = fory.serialize(order).value();
auto decoded = fory.deserialize<Order>(bytes).value();
assert(order == decoded);
}

每个线程使用一个已配置的 Fory 实例;如果多个线程共享同一实例,则构建线程安全的 Fory 实例:

auto fory = Fory::builder()
.xlang(false)
.track_ref(true)
.build_thread_safe();

在并发序列化开始前注册类型。

Schema 演进

原生序列化默认使用兼容模式。当仅限 C++ 的写入端和读取端 Schema 可能不同时,请保留该默认设置:

auto fory = Fory::builder()
.xlang(false)
.build();

兼容模式写入 Schema 元数据,因此在字段标识保持兼容时,读取端可以容忍字段新增、删除或重排。参见 Schema 演进

只有每个读取端和写入端始终使用相同的 C++ Schema 时,才设置 .compatible(false) 以获得更快的序列化和更小的体积。

注册

序列化前使用稳定的 ID 或名称注册结构体:

fory.register_struct<Order>(100);
fory.register_struct<Order>("example.Order");

紧凑载荷使用数字 ID。当独立团队通过名称协调类型标识时使用名称注册;必要时使用 . 添加命名空间前缀。

C++ 对象范围

原生序列化支持以下 C++ 专属对象范围:

  • FORY_STRUCT 描述的结构体和类。
  • std::vectorstd::mapstd::unordered_mapstd::setstd::unordered_set 等标准容器。
  • std::optionalstd::variant 和类似元组的值。
  • std::shared_ptrstd::unique_ptr
  • charchar16_tchar32_t 等字符类型。
  • 使用原生模式类型 ID 的无符号整数类型。
  • 通过 C++ 实现注册的多态序列化。

完整类型范围和跨语言映射说明参见支持的类型

引用与智能指针

原生序列化支持智能指针和引用跟踪:

auto fory = Fory::builder()
.xlang(false)
.track_ref(true)
.build();

启用引用跟踪后,可以保留共享指针标识,并通过支持的指针模式表示循环对象图。如果标识不属于模型的一部分,请对值形态数据禁用引用跟踪。

仅限原生模式的标量形态

某些 C++ 标量形态不是可移植的跨语言载荷。当这些形态必须作为 C++ 值往返时,请使用原生序列化:

auto fory = Fory::builder().xlang(false).build();

auto char_bytes = fory.serialize(char32_t{U'A'}).value();
auto value = fory.deserialize<char32_t>(char_bytes).value();

auto unsigned_bytes = fory.serialize(uint64_t{42}).value();
auto unsigned_value = fory.deserialize<uint64_t>(unsigned_bytes).value();

对于跨语言载荷,请使用元数据和共享跨语言类型映射,不要依赖仅限 C++ 原生模式的类型 ID。

性能指南

  • 复用已配置的 Fory 实例。
  • 每个线程使用单线程 Fory 可获得最快路径;共享并发使用时采用 build_thread_safe()
  • 只有每个读取端和写入端始终使用相同 C++ Schema,且应用希望获得更快序列化和更小体积时,才使用 .compatible(false)
  • 使用显式数字 ID 注册结构体以获得紧凑载荷。
  • 对值形态对象图禁用引用跟踪。
  • 热路径上优先使用具体类型,而非多态/动态字段。

原生模式与跨语言模式对比

需求使用原生序列化使用跨语言序列化
仅限 C++ 的载荷可选
非 C++ 读取端或写入端
C++ 原生字符和无符号形态有限
智能指针和 C++ 对象图有限
相同 Schema 的紧凑载荷
默认支持兼容 Schema 演进
跨语言可移植类型映射

故障排查

非 C++ 实现无法读取载荷

写入端使用了原生序列化。请使用 .xlang(true) 重新构建,并与每个对等端对齐类型注册。

字段变更后滚动部署失败

原生序列化默认使用兼容模式。当 Schema 可能不同时,请保留该默认设置。

仅限原生模式的标量无法映射到其他语言

可移植载荷请使用带显式元数据的跨语言序列化。原生 C++ 类型 ID 仅供 C++ 读取端使用。

共享指针对象图丢失标识

启用 .track_ref(true),并确认对象图使用支持的指针模式。

相关主题