在proto中,可以使用OneOf类型,使用一个字段存储不同类型的数据。类似go中的interface。
假设有proto如下,Val是一个OneOf数据类型,它可以为double/int/str...中的任意一种。
TestPB中引用了value
,类型为Val
。
syntax = "proto3";
package protooneof;
option go_package = "pb/message";
message Val{
oneof oneof_val {
double double = 1;
int64 int = 2;
string str = 3;
bytes bytes = 4;
uint64 uint = 5;
float float = 6;
}
}
message TestPB {
Val value = 1;
}
赋值
编译这个proto,可以使用以下的代码对Val类型赋值:
代码语言:javascript复制 msg := &message.TestPB{
Value: &message.Val{
OneofVal: &message.Val_Str{Str: "hello"}, // 对oneof赋值需要使用Val包装,然后内部指定具体的类型
},
}
同理,使用=
赋值也是可以的:
msg.Value = &message.Val{
OneofVal: &message.Val_Float{Float: 123},
}
取值
假设知道值的类型,可以直接调用GetXXX
方法取值。
fv := msg.GetValue().GetStr()
fmt.Printf("fv:%vn", fv) // 输出 hello
但是如果这个值未设置或者不存在,则会返回这个类型的0值。
这就带来问题,如何判定这个值是否已设置,如果已设置是什么类型?
可以使用switch类型判定的方式来解决需求,因为已经穷举了Val
所有可能的类型,如果未设置值,会打印未设置值
。
switch v := msg.GetValue().GetOneofVal().(type) {
case *message.Val_Int:
fmt.Printf("int val:%vn", v.Int)
case *message.Val_Float:
fmt.Printf("float val:%vn", v.Float)
case *message.Val_Double:
fmt.Printf("double val:%vn", v.Double)
case *message.Val_Str:
fmt.Printf("str val:%vn", v.Str)
case *message.Val_Bytes:
fmt.Printf("bytes val:%vn", v.Bytes)
case *message.Val_Uint:
fmt.Printf("uint val:%vn", v.Uint)
default:
fmt.Printf("未设置值n")
}
通过善用Oneof类型,可以让pb的结构更紧凑,满足更多元化的需求。