使用gRPC时google.protobuf.Any序列化反序列化问题求助
问题根源
你直接序列化anypb.Any得到的是protobuf Any类型的JSON结构(包含@type和value字段),但反序列化时错误地用了structpb.Value,两者结构不匹配导致报错。另外,anypb.Any是用来包裹protobuf消息的,基础类型(int、string、bool等)必须先包装成对应的protobuf包装类型才能存入,这也是你前期操作的疏漏点。
两种解决方案
方案一:正确使用google.protobuf.Any
1. 存入数据库前的正确包装
所有要存入anypb.Any的数据,都需要先转换成对应的protobuf消息类型:
- 基础类型用
google.protobuf.Int64Value/StringValue/BoolValue等包装 - 自定义结构体需要先在proto中定义对应的消息,编译后使用
示例代码:
import ( "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/wrapperspb" // 导入你的自定义proto包,比如mypb ) // 包装int类型 intVal := &wrapperspb.Int64Value{Value: 9} anyInt, err := anypb.New(intVal) if err != nil { // 处理错误 } // 包装string类型 strVal := &wrapperspb.StringValue{Value: "a string"} anyStr, err := anypb.New(strVal) // 包装自定义结构体(假设你有proto定义的MyStruct) myStruct := &mypb.MyStruct{Key: "test_value"} anyStruct, err := anypb.New(myStruct)
序列化存入数据库的代码和你之前的一致:
bytes, err := json.Marshal(anyInt) if err != nil { return err } itemStr := string(bytes) // 将itemStr存入数据库
2. 从数据库取出后的反序列化
先把字符串反序列化成*anypb.Any,再根据类型URL解析成对应的数据:
import ( "fmt" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/wrapperspb" // 导入自定义proto包 ) func getItem(itemStr string) (interface{}, error) { var anyItem anypb.Any err := json.Unmarshal([]byte(itemStr), &anyItem) if err != nil { return nil, err } // 根据类型URL匹配解析 switch anyItem.GetTypeUrl() { case wrapperspb.Int64Value{}.ProtoReflect().Descriptor().FullName(): var intVal wrapperspb.Int64Value err := anyItem.UnmarshalTo(&intVal) return intVal.Value, err case wrapperspb.StringValue{}.ProtoReflect().Descriptor().FullName(): var strVal wrapperspb.StringValue err := anyItem.UnmarshalTo(&strVal) return strVal.Value, err case wrapperspb.BoolValue{}.ProtoReflect().Descriptor().FullName(): var boolVal wrapperspb.BoolValue err := anyItem.UnmarshalTo(&boolVal) return boolVal.Value, err case mypb.MyStruct{}.ProtoReflect().Descriptor().FullName(): var myStruct mypb.MyStruct err := anyItem.UnmarshalTo(&myStruct) return &myStruct, err default: return nil, fmt.Errorf("不支持的类型: %s", anyItem.GetTypeUrl()) } }
方案二:改用google.protobuf.Value(更适合任意JSON场景)
如果你的需求只是支持任意JSON格式的动态数据(基础类型、对象、数组等),用structpb.Value比anypb.Any更简单,它天生就是为表示任意JSON值设计的,不需要提前定义消息类型。
1. 修改Proto定义
import "google/protobuf/struct.proto"; message MyRequest { google.protobuf.Value item = 1; // 直接支持任意JSON类型 }
编译后的Go结构体:
type MyRequest struct { Item *structpb.Value `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` }
2. 存入和取出操作
存入时直接序列化:
bytes, err := json.Marshal(m.request.Item) if err != nil { return err } itemStr := string(bytes) // 存入数据库
取出时直接反序列化成structpb.Value:
import "google.golang.org/protobuf/types/known/structpb" func getItem(itemStr string) (*structpb.Value, error) { var item structpb.Value err := json.Unmarshal([]byte(itemStr), &item) return &item, err }
如果需要转换成Go原生类型,用structpb.Unmarshal:
// 转成map[string]interface{} var data map[string]interface{} err := structpb.Unmarshal(item, &data) // 转成int var num int err := structpb.Unmarshal(item, &num)
内容的提问来源于stack exchange,提问作者Vanya
相关产品推荐
相关产品推荐

