Go Opaque API:手动迁移
Opaque API 是 Go 编程语言的 Protocol Buffers 实现的最新版本。旧版本现在称为 Open Struct API。有关介绍,请参阅 Go Protobuf:发布 Opaque API 博客文章。
这是将 Go Protobuf 用法从旧的 Open Struct API 迁移到新的 Opaque API 的用户指南。
生成的代码指南 提供了更多详细信息。本指南并排比较了旧 API 和新 API。
消息构造
假设有一个 protobuf 消息定义如下
message Foo {
uint32 uint32 = 1;
bytes bytes = 2;
oneof union {
string string = 4;
MyMessage message = 5;
}
enum Kind { … };
Kind kind = 9;
}
这是一个如何从字面值构造此消息的示例
Open Struct API(旧) | Opaque API(新) |
|
|
如您所见,构建器结构允许 Open Struct API(旧)和 Opaque API(新)之间几乎 1:1 的转换。
通常,为了提高可读性,首选使用构建器。仅在极少数情况下,例如在热内部循环中创建 Protobuf 消息时,才可能更喜欢使用 setter 而不是构建器。有关更多详细信息,请参阅 Opaque API FAQ:我应该使用构建器还是 setter?。
上述示例的一个例外是使用 oneofs 时:Open Struct API(旧)为每个 oneof case 使用包装器结构类型,而 Opaque API(新)将 oneof 字段视为常规消息字段
Open Struct API(旧) | Opaque API(新) |
|
|
对于与 oneof 联合关联的 Go 结构字段集,只能填充一个字段。如果填充了多个 oneof case 字段,则最后一个字段(在您的 .proto 文件中的字段声明顺序中)获胜。
标量字段
假设有一个消息定义了标量字段
message Artist {
int32 birth_year = 1;
}
Go 使用标量类型(bool、int32、int64、uint32、uint64、float32、float64、string、[]byte 和 enum)的 Protobuf 消息字段将具有 Get
和 Set
访问器方法。具有 显式存在性 的字段也将具有 Has
和 Clear
方法。
对于名为 birth_year
的 int32
类型的字段,将为其生成以下访问器方法
func (m *Artist) GetBirthYear() int32
func (m *Artist) SetBirthYear(v int32)
func (m *Artist) HasBirthYear() bool
func (m *Artist) ClearBirthYear()
Get
返回字段的值。如果未设置该字段或消息接收者为 nil,则返回默认值。默认值是 零值,除非使用 default 选项显式设置。
Set
将提供的值存储到字段中。当在 nil 消息接收者上调用时,它会 panic。
对于字节字段,使用 nil []byte 调用 Set
将被视为已设置。例如,立即调用 Has
会返回 true。立即调用 Get
将返回零长度切片(可以是 nil 或空切片)。用户应使用 Has
来确定存在性,而不是依赖于 Get
是否返回 nil。
Has
报告字段是否已填充。当在 nil 消息接收者上调用时,它返回 false。
Clear
清除字段。当在 nil 消息接收者上调用时,它会 panic。
使用字符串字段的示例代码片段:
Open Struct API(旧) | Opaque API(新) |
|
|
消息字段
假设有一个消息定义了消息类型的字段
message Band {}
message Concert {
Band headliner = 1;
}
消息类型的 Protobuf 消息字段将具有 Get
、Set
、Has
和 Clear
方法。
对于名为 headliner
的消息类型字段,将为其生成以下访问器方法
func (m *Concert) GetHeadliner() *Band
func (m *Concert) SetHeadliner(*Band)
func (m *Concert) HasHeadliner() bool
func (m *Concert) ClearHeadliner()
Get
返回字段的值。如果未设置或在 nil 消息接收者上调用,则返回 nil。检查 Get
是否返回 nil 等同于检查 Has
是否返回 false。
Set
将提供的值存储到字段中。当在 nil 消息接收者上调用时,它会 panic。使用 nil 指针调用 Set
等同于调用 Clear
。
Has
报告字段是否已填充。当在 nil 消息接收者上调用时,它返回 false。
Clear
清除字段。当在 nil 消息接收者上调用时,它会 panic。
示例代码片段:
Open Struct API(旧) | Opaque(新) |
|
|
重复字段
假设有一个消息定义了重复消息类型的字段
message Concert {
repeated Band support_acts = 2;
}
重复字段将具有 Get
和 Set
方法。
Get
返回字段的值。如果未设置该字段或消息接收者为 nil,则返回 nil。
Set
将提供的值存储到字段中。当在 nil 消息接收者上调用时,它会 panic。Set
将存储提供的切片标头的副本。切片内容的更改在重复字段中是可观察的。因此,如果使用空切片调用 Set
,则立即调用 Get
将返回相同的切片。对于线路或文本编组输出,传入的 nil 切片与空切片无法区分。
对于消息 Concert
上名为 support_acts
的重复消息类型字段,将为其生成以下访问器方法
func (m *Concert) GetSupportActs() []*Band
func (m *Concert) SetSupportActs([]*Band)
示例代码片段:
Open Struct API(旧) | Opaque API(新) |
|
|
映射
假设有一个消息定义了映射类型的字段
message MerchBooth {
map<string, MerchItems> items = 1;
}
映射字段将具有 Get
和 Set
方法。
Get
返回字段的值。如果未设置该字段或消息接收者为 nil,则返回 nil。
Set
将提供的值存储到字段中。当在 nil 消息接收者上调用时,它会 panic。Set
将存储提供的映射引用的副本。提供的映射的更改在映射字段中是可观察的。
对于消息 MerchBooth
上名为 items
的映射字段,将为其生成以下访问器方法
func (m *MerchBooth) GetItems() map[string]*MerchItem
func (m *MerchBooth) SetItems(map[string]*MerchItem)
示例代码片段:
Open Struct API(旧) | Opaque API(新) |
|
|
Oneofs
对于每个 oneof 联合分组,消息上都将有一个 Which
、Has
和 Clear
方法。在该联合中的每个 oneof case 字段上也将有一个 Get
、Set
、Has
和 Clear
方法。
假设有一个消息定义了 oneof 字段 image_url
和 image_data
在 oneof avatar
中,如下所示:
message Profile {
oneof avatar {
string image_url = 1;
bytes image_data = 2;
}
}
为此 oneof 生成的 Opaque API 将是
func (m *Profile) WhichAvatar() case_Profile_Avatar { … }
func (m *Profile) HasAvatar() bool { … }
func (m *Profile) ClearAvatar() { … }
type case_Profile_Avatar protoreflect.FieldNumber
const (
Profile_Avatar_not_set_case case_Profile_Avatar = 0
Profile_ImageUrl_case case_Profile_Avatar = 1
Profile_ImageData_case case_Profile_Avatar = 2
)
Which
报告设置了哪个 case 字段,方法是返回字段编号。当未设置任何字段或在 nil 消息接收者上调用时,它返回 0。
Has
报告是否设置了 oneof 中的任何字段。当在 nil 消息接收者上调用时,它返回 false。
Clear
清除 oneof 中当前设置的 case 字段。它在 nil 消息接收者上 panic。
为每个 oneof case 字段生成的 Opaque API 将是
func (m *Profile) GetImageUrl() string { … }
func (m *Profile) GetImageData() []byte { … }
func (m *Profile) SetImageUrl(v string) { … }
func (m *Profile) SetImageData(v []byte) { … }
func (m *Profile) HasImageUrl() bool { … }
func (m *Profile) HasImageData() bool { … }
func (m *Profile) ClearImageUrl() { … }
func (m *Profile) ClearImageData() { … }
Get
返回 case 字段的值。如果未设置 case 字段或在 nil 消息接收者上调用,它将返回零值。
Set
将提供的值存储到 case 字段中。它还会隐式清除先前在 oneof 联合中填充的 case 字段。使用 nil 值在 oneof 消息 case 字段上调用 Set
会将字段设置为空消息。当在 nil 消息接收者上调用时,它会 panic。
Has
报告 case 字段是否已设置。当在 nil 消息接收者上调用时,它返回 false。
Clear
清除 case 字段。如果先前已设置,则 oneof 联合也会被清除。如果 oneof 联合设置为不同的字段,则不会清除 oneof 联合。当在 nil 消息接收者上调用时,它会 panic。
示例代码片段:
Open Struct API(旧) | Opaque API(新) |
|
|
反射
当从 Open Struct API 迁移时,在 proto 消息类型上使用 Go reflect
包来访问结构字段和标签的代码将不再有效。代码将需要迁移到使用 protoreflect
一些常用库在底层使用 Go reflect
,例如:
- encoding/json
- pretty
- cmp
- 要将
cmp.Equal
与 protobuf 消息正确配合使用,请使用 protocmp.Transform
- 要将