去除 protobuf Any 类型中的 @type 字段 2 months ago
去除 protobuf Any 类型中的 @type 字段
在 Protocol Buffers 的 Any 类型中,@type
字段是默认包含的类型标识信息,用于反序列化时识别原始类型。如果你想去掉这个字段,有以下几种解决方案:
方案1:使用自定义 JSON 序列化(推荐)
修改你的 ResponseMessage
定义,添加自定义 JSON 序列化选项:
import "google/protobuf/any.proto";
import "google/protobuf/struct.proto";
message ResponseMessage {
int32 code = 1;
string message = 2;
oneof data {
google.protobuf.Any any_data = 3; // 保留原始 Any 功能
google.protobuf.Value json_data = 4; // 使用更灵活的 JSON 结构
}
}
然后在代码中:
func (s *UserService) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.ResponseMessage, error) {
// ...业务逻辑
// 使用 Value 而不是 Any
data, _ := structpb.NewValue(map[string]interface{}{
"id": u.ID,
"username": u.Username,
})
return &pb.ResponseMessage{
Code: 200,
Message: "创建用户成功",
Data: &pb.ResponseMessage_JsonData{
JsonData: data,
},
}, nil
}
方案2:使用自定义结构体直接序列化
修改 ResponseMessage
定义:
message ResponseMessage {
int32 code = 1;
string message = 2;
bytes data = 3; // 存储原始 JSON 数据
}
实现代码:
func (s *UserService) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.ResponseMessage, error) {
// ...业务逻辑
jsonData, _ := json.Marshal(map[string]interface{}{
"id": u.ID,
"username": u.Username,
})
return &pb.ResponseMessage{
Code: 200,
Message: "创建用户成功",
Data: jsonData,
}, nil
}
方案3:在网关层转换(适用于有 API 网关的场景)
如果你使用 API 网关,可以在网关层对响应进行转换:
// 网关中间件
func ResponseTransformer() middleware.Middleware {
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (interface{}, error) {
resp, err := handler(ctx, req)
if err != nil {
return nil, err
}
if msg, ok := resp.(*pb.ResponseMessage); ok {
return transformResponse(msg), nil
}
return resp, nil
}
}
}
func transformResponse(msg *pb.ResponseMessage) map[string]interface{} {
result := map[string]interface{}{
"code": msg.Code,
"message": msg.Message,
}
if msg.Data != nil {
var data map[string]interface{}
_ = json.Unmarshal([]byte(msg.Data.Value), &data)
result["data"] = data
}
return result
}
方案4:修改 proto 定义(最简单)
如果你可以接受修改 proto 定义,最简单的方法是直接包含响应结构:
message ResponseMessage {
int32 code = 1;
string message = 2;
UserCreateResponse data = 3; // 直接使用具体类型
}
注意事项
- 去除
@type
会影响客户端反序列化能力,确保客户端不需要类型信息 - 方案1和方案2保持了最大的灵活性,同时去除了类型信息
- 方案4最简单但灵活性最低,适合响应结构固定的场景
我推荐使用方案1,它既保持了 protobuf 的类型安全,又提供了灵活的 JSON 输出控制。