引用处理
Fory Go 支持引用跟踪,可处理循环引用与共享对象。这对图结构、带父指针的树、存在环的链表等复杂对象图尤其重要。
启用引用跟踪
引用跟踪默认关闭。创建 Fory 实例时显式启用:
f := fory.New(fory.WithTrackRef(true))
注意:只有全局开启 WithTrackRef(true) 之后,字段级 ref 标记才会生效。默认的 WithTrackRef(false) 会忽略所有字段级引用标记。
引用跟踪如何工作
不启用引用跟踪(默认)
关闭时,每个对象都会被独立序列化:
f := fory.New() // 默认关闭 TrackRef
shared := &Data{Value: 42}
container := &Container{A: shared, B: shared}
data, _ := f.Serialize(container)
// shared 会被序列化两次,不做去重
启用引用跟踪
开启后,Fory 会按对象 身份记录已经写出的对象:
f := fory.New(fory.WithTrackRef(true))
shared := &Data{Value: 42}
container := &Container{A: shared, B: shared}
data, _ := f.Serialize(container)
// shared 只会写出一次,第二次出现时写入引用
引用标记
Fory 在序列化时通过标记值表达引用状态:
| 标记 | 值 | 含义 |
|---|---|---|
NullFlag | -3 | nil / null 值 |
RefFlag | -2 | 指向已序列化对象的引用 |
NotNullValueFlag | -1 | 非空值,后续紧跟实际数据 |
RefValueFlag | 0 | 引用值标记 |
支持引用跟踪的类型
只有部分类型支持引用跟踪。在 xlang 模式下,以下类型可以参与引用跟踪:
| 类型 | 支持引用跟踪 | 说明 |
|---|---|---|
*struct(结构体指针) | 是 | 通过 fory:"ref" 开启 |
any(接口) | 是 | 自动支持 |
[]T(slice) | 是 | 通过 fory:"ref" 开启 |
map[K]V | 是 | 通过 fory:"ref" 开启 |
*int、*string 等 | 否 | 基础类型指针不支 持 |
| 基础类型 | 否 | 值类型 |
time.Time、time.Duration | 否 | 值类型 |
数组([N]T) | 否 | 值类型 |
字段级引用控制
即使全局设置了 WithTrackRef(true),字段默认仍然不做引用跟踪。可以通过结构体 tag 为特定字段启用:
type Container struct {
// 为该字段启用引用跟踪
SharedData *Data `fory:"ref"`
// 显式关闭引用跟踪,与默认行为一致
SimpleData *Data `fory:"ref=false"`
}
要点如下:
- 字段级 tag 只有在全局开启
WithTrackRef(true)时才会生效。 - 全局关闭时,所有字段级
ref标记都会被忽略。 - 该能力适用于 slice、map 和结构体指针字段。
- 基础类型指针(如
*int、*string)不能使用该标记。 - 默认是
ref=false,也就是字段不做引用跟踪。
更多细节可参考 Struct Tags。