Schema 演化
Apache Fory™ 在 Compatible 模式下支持 schema 演化,允许序列化和反序列化双方拥有不同的类型定义。
Compatible 模式
使用 compatible(true) 启用 schema 演化:
#include "fory/serialization/fory.h"
using namespace fory::serialization;
// 版本 1:原始 schema
struct PersonV1 {
std::string name;
int32_t age;
};
FORY_STRUCT(PersonV1, name, age);
// 版本 2:添加 email 字段
struct PersonV2 {
std::string name;
int32_t age;
std::string email; // 新字段
};
FORY_STRUCT(PersonV2, name, age, email);
int main() {
// 为每个 schema 版本创建单独的 Fory 实例
auto fory_v1 = Fory::builder()
.compatible(true) // 启用 schema 演化
.xlang(true)
.build();
auto fory_v2 = Fory::builder()
.compatible(true)
.xlang(true)
.build();
// 使用相同的类型 ID 进行 schema 演化
constexpr uint32_t PERSON_TYPE_ID = 100;
fory_v1.register_struct<PersonV1>(PERSON_TYPE_ID);
fory_v2.register_struct<PersonV2>(PERSON_TYPE_ID);
// 使用 V1 序列化
PersonV1 v1{"Alice", 30};
auto bytes = fory_v1.serialize(v1).value();
// 反序列化为 V2 - email 获得默认值(空字符串)
auto v2 = fory_v2.deserialize<PersonV2>(bytes).value();
assert(v2.name == "Alice");
assert(v2.age == 30);
assert(v2.email == ""); // 缺失字段的默认值
return 0;
}
Schema 演化特性
Compatible 模式支持以下 schema 变更:
| 变更类型 | 支持 | 行为 |
|---|---|---|
| 添加新字段 | ✅ | 缺失字段使用默认值 |
| 删除字段 | ✅ | 额外字段被跳过 |
| 重排字段顺序 | ✅ | 按名称匹配字段,而非位置 |
| 更改可空性 | ✅ | T ↔ std::optional<T> |
| 更改字段类型 | ❌ | 类型必须兼容 |
| 重命名字段 | ❌ | 字段名必须匹配(区分大小写) |
添加字段(向后兼容)
当使用具有额外字段的新 schema 反序列化旧数据时:
// 旧 schema (V1)
struct ProductV1 {
std::string name;
double price;
};
FORY_STRUCT(ProductV1, name, price);
// 新 schema (V2) 带有额外字段
struct ProductV2 {
std::string name;
double price;
std::vector<std::string> tags; // 新字段
std::map<std::string, std::string> attributes; // 新字段
};
FORY_STRUCT(ProductV2, name, price, tags, attributes);
// 序列化 V1
ProductV1 v1{"Laptop", 999.99};
auto bytes = fory_v1.serialize(v1).value();
// 反序列化为 V2
auto v2 = fory_v2.deserialize<ProductV2>(bytes).value();
assert(v2.name == "Laptop");
assert(v2.price == 999.99);
assert(v2.tags.empty()); // 默认:空 vector
assert(v2.attributes.empty()); // 默认:空 map
删除字段(向前兼容)
当使用具有较少字段的旧 schema 反序列化新数据时:
// 完整 schema
struct UserFull {
int64_t id;
std::string username;
std::string email;
std::string password_hash;
int32_t login_count;
};
FORY_STRUCT(UserFull, id, username, email, password_hash, login_count);
// 精简 schema(删除了 3 个字段)
struct UserMinimal {
int64_t id;
std::string username;
};
FORY_STRUCT(UserMinimal, id, username);
// 序列化完整版本
UserFull full{12345, "johndoe", "john@example.com", "hash123", 42};
auto bytes = fory_full.serialize(full).value();
// 反序列化为精简版本 - 额外字段被跳过
auto minimal = fory_minimal.deserialize<UserMinimal>(bytes).value();
assert(minimal.id == 12345);
assert(minimal.username == "johndoe");
// email、password_hash、login_count 被跳过