简介:这篇教程介绍了如何在 Golang 中通过 Protobuf 的 Any 类型打包和解包不同类型的消息,并进行类型断言处理。
教程:
package main
import (
"fmt"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/any"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"log"
"strings"
)
func main() {
// 创建 MessageA 对象
messageA := &MessageA{
Message: "Hello, World!",
}
// 将 MessageA 对象打包到 google.protobuf.Any 中
anyA, err := proto.Marshal(messageA)
if err != nil {
log.Fatal(err)
}
// 创建 MessageB 对象
messageB := &MessageB{
Value: 42,
}
// 将 MessageB 对象打包到 google.protobuf.Any 中
anyB, err := proto.Marshal(messageB)
if err != nil {
log.Fatal(err)
}
// 创建包含 Any 类型的列表
anyList := []*any.Any{
{
TypeUrl: "type.googleapis.com/MessageA",
Value: anyA,
},
{
TypeUrl: "type.googleapis.com/MessageB",
Value: anyB,
},
}
// 遍历 Any 类型列表
for _, any := range anyList {
// 解析 Any 对象
message, err := getMessageTypeFromTypeURL(any.GetTypeUrl())
if err != nil {
log.Println(err)
continue
}
err = proto.Unmarshal(any.Value, message.Interface().(proto.Message))
if err != nil {
log.Println(err)
continue
}
// 处理解析后的消息对象
switch msg := message.Interface().(type) {
case *MessageA:
fmt.Println("MessageA:", msg.Message)
case *MessageB:
fmt.Println("MessageB:", msg.Value)
default:
log.Println("Unknown message type")
}
}
}
func getMessageTypeFromTypeURL(typeURL string) (protoreflect.Message, error) {
// 解析 typeURL
typeName := "grpc.test." + typeURL[strings.LastIndex(typeURL, "/")+1:]
// 获取类型
messageType, err := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(typeName))
if err != nil {
return nil, fmt.Errorf("Type %s not found", typeName)
}
return messageType.New(), nil
}
代码说明:
- 创建了 MessageA 和 MessageB 对象,并将这两个对象序列化并打包到 google.protobuf.Any 对象中。
- 创建包含 Any 类型的列表anyList。指定了 Any 对象的 TypeUrl,并赋值了之前序列化的 MessageA 和 MessageB 对象。
- 遍历 Any 类型列表anyList,解析每个 Any 对象,并进行类型断言处理。我们根据解析的 message 对象,断言其是 MessageA 或者是 MessageB,从而不同类型的处理方式。
函数 getMessageTypeFromTypeURL 中,首先解析TypeUrl获取typeName,然后在全局类型注册表中通过typeName查找对应的反射类型,并生成消息对象实例。
注意:MessageA和MessageB需提前定义,并通过protoc生成对应的Go代码。