You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 17:33:25