Go将外部JSON反序列化为protobuf Any报错解决方法
问题根因
你遇到的问题来自三个核心错误:
- 错误使用标准库
encoding/json反序列化包含google.protobuf.Any类型的Protobuf结构体。标准JSON库不遵循Protobuf的JSON序列化规范,而Any类型的JSON格式要求必须携带@type字段标记消息类型,再将内容序列化为Protobuf二进制存入Value字段。标准库JSON只会将Any当做普通结构体解析,最终得到TypeUrl和Value都为空的无效实例。 - 手动填充
TypeUrl无法解决问题:Any.Value要求存储Protobuf二进制编码内容,直接传入JSON格式数据会导致后续UnmarshalTo解码失败。 - 泛型初始化逻辑有误:直接声明
var item T在T为指针类型时会得到nil值,传入UnmarshalTo会因为接收者为nil无法写入内容报错。 - 额外前提:如果API返回的
results数组元素没有携带符合规范的@type字段(格式通常为type.googleapis.com/包名.消息名),标准Any解析逻辑无法识别消息类型,同样会报错。
修改方案
方案1:遵循Protobuf Any规范,使用官方protojson解析
如果你的API返回格式符合Protobuf JSON规范,results元素自带@type字段,按以下方式修改:
- 替换反序列化依赖:使用官方
google.golang.org/protobuf/encoding/protojson包替代标准encoding/json,该包内置Any类型的解析逻辑,会自动读取@type字段填充TypeUrl,并将JSON内容转成Protobuf二进制存入Value字段。 - 修正泛型实例初始化逻辑,确保传入
UnmarshalTo的是非nil的有效消息指针。
修改后的代码如下:
import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" ) // 注意去掉泛型约束前的~,proto.Message是接口类型不需要~约束 func getData[T proto.Message](data []byte) ([]T, error) { var resp ArrayRespone // 用protojson完成反序列化 if err := protojson.Unmarshal(data, &resp); err != nil { return nil, err } var items []T for _, result := range resp.Results { // 初始化非nil的对应类型实例 var zero T item := proto.Clone(zero).ProtoReflect().New().Interface().(T) if err := result.UnmarshalTo(item); err != nil { return nil, err } items = append(items, item) } return items, nil }
方案2:绕开Any类型,直接用泛型结构体解析(推荐无@type场景使用)
如果你的API返回的results是普通JSON结构,没有携带Protobuf Any要求的@type字段,完全没必要硬用google.protobuf.Any,直接定义泛型响应结构用标准JSON解析即可,代码更简单也不会有类型解析问题:
import "encoding/json" // 自定义泛型响应结构,标签对应API返回的JSON字段名 type ArrayResponse[T any] struct { Count int32 `json:"count"` NextUrl string `json:"next_url"` RequestId string `json:"request_id"` Results []T `json:"results"` Status string `json:"status"` } func getData[T any](data []byte) ([]T, error) { var resp ArrayResponse[T] if err := json.Unmarshal(data, &resp); err != nil { return nil, err } return resp.Results, nil }
内容的提问来源于stack exchange,提问作者Woody1193
相关产品推荐
相关产品推荐

