跳到主要内容
版本:1.5.0

Schema 演进

Fory 通过兼容模式支持 Schema 演进;Swift 默认启用兼容模式。

在兼容模式下,读取端还允许对部分标量字段类型进行变更,前提是转换无损。当转换后的值 具有相同的逻辑值时,匹配字段可以在 BoolString、数值标量和 Decimal 之间读取。 例如,"true""false" 可以读取为布尔值,"123" 可以读取为能够容纳 123 的 数值字段,数值和十进制数可以读取为规范字符串,而数值拓宽或缩窄仅在不损失精度且不超出 范围时成功。数值字符串必须使用有限值的 ASCII 十进制语法。无效字符串和有损转换会在 反序列化期间失败。

标量转换也适用于可选字段。存在的可选值按相同规则转换;缺失的可选值则保留 Swift 兼容模式为本地字段提供的正常默认值。启用引用跟踪的标量类型变更不兼容。

默认兼容模式

let fory = Fory()

示例:结构体演进

import Fory

@ForyStruct
struct PersonV1 {
var name: String = ""
var age: Int32 = 0
var address: String = ""
}

@ForyStruct
struct PersonV2 {
var name: String = ""
var age: Int32 = 0
var phone: String? = nil // 新增字段
}

let writer = Fory()
try writer.register(PersonV1.self, id: 1)

let reader = Fory()
try reader.register(PersonV2.self, id: 1)

let v1 = PersonV1(name: "alice", age: 30, address: "main st")
let bytes = try writer.serialize(v1)
let v2: PersonV2 = try reader.deserialize(bytes)

assert(v2.name == "alice")
assert(v2.age == 30)
assert(v2.phone == nil)

外部目标

外部结构化序列化器声明会定义用于兼容性的本地 Schema:

@ForyStruct(target: ThirdParty.User.self)
struct UserV2Serializer {
var name: String
var age: Int32
var phone: String?
}

使用与对端相同的逻辑 ID 或名称注册该序列化器,然后在根值处选择它:

try reader.register(UserV2Serializer.self, id: 1)
let user = try reader.deserialize(
bytes,
with: UserV2Serializer.self
)

序列化器声明的名称不会影响编码格式中的 Schema。

兼容模式下安全的变更

  • 新增字段
  • 删除旧字段
  • 重排字段顺序
  • BoolString、数值标量或 Decimal 之间更改匹配字段的类型,前提是写入的每个值 对读取端而言都是无损的

不安全的变更

  • 任意修改既有字段的类型,包括标量值超出范围、需要舍入、不是有限值,或不符合兼容模式 数值字符串语法的情况
  • 对端之间使用不一致的注册映射

相同 Schema 优化

仅当所有读取端和写入端始终使用相同的 Schema,并且需要更快的序列化速度和更小的体积时, 才应设置 compatible: false。对于 xlang 载荷,只有在确认所有语言均使用相同的 Schema, 或原生类型由 Fory Schema IDL 生成后,才能设置 compatible: false