如何在proto3中用上golang对应的interface{}类型

作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!


首先,我希望所有golang中用于http请求响应的结构,都使用proto3来定义。
麻烦的是,有的情况下某个字段的类型可能是动态的,对应的JSON类型可能是number/string/boolean/null中的其中一种。

一开始我尝试用proto.Any类型,就像这样:

import "google/protobuf/any.proto";

message MyRequest{
    google.protobuf.Any user_input = 1;  // 用户可能输入 number / string / boolean / null 中的其中一种
}

使用protoc生成代码后,发现这玩意儿完全没办法做json的encode/decode。
理想的办法是让生成golang代码中的 user_input 成为 interface{} 类型。但如何才能让proto3生成golang的interface类型呢?

尝试后发现可以用下面的办法解决:

1.使用gogo proto的扩展语法

import "google/protobuf/descriptor.proto";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";

message MyRequest{
    bytes user_input = 1[(gogoproto.customtype) = "InterfaceType"];  // 使用一个叫做 InterfaceType 的自定义类型
}

注意:InterfaceType 直接写成 interface{} 是不行的。因为 interface{} 类型没有实现序列化的接口。

执行protoc后生成了如下代码:

type MyRequest struct {
	UserInput []InterfaceType `protobuf:"bytes,4,rep,name=user_input,proto3,customtype=InterfaceType" json:"user_input,omitempty"`
}

2. 编写 InterfaceType 类型对应的序列化代码

// interface_type.go
// 放在xxx.pb.go的同一目录下
package proto

import (
	"encoding/json"
	"errors"
)

type InterfaceType struct {
	Value interface{}
}

func (t InterfaceType) Marshal() ([]byte, error) {
	return nil, errors.New("not implement")
}
func (t *InterfaceType) MarshalTo(data []byte) (n int, err error) {
	return 0, errors.New("not implement")
}
func (t *InterfaceType) Unmarshal(data []byte) error {
	return errors.New("not implement")
}
func (t *InterfaceType) Size() int {
	return -1
}

// 因为只做JSON的序列化,所以只实现这两个方法就行了
func (t InterfaceType) MarshalJSON() ([]byte, error) {
	return json.Marshal(t.Value)
}
func (t *InterfaceType) UnmarshalJSON(data []byte) error {
	return json.Unmarshal(data, &t.Value)
}

3.测试一下

// my_request.pb_test.go
package proto

import (
	"encoding/json"
	"testing"
)

func Test_MyRequest(t *testing.T) {
	j := `{"user_input":123}`
	inst := &MyRequest{}
	err := json.Unmarshal([]byte(j), inst)
	if err != nil {
		t.Errorf("json decode error, err=%+v", err)
		return
	}
	t.Logf("%+v", MyRequest)
	str, err := json.Marshal(inst)
	if err != nil {
		t.Errorf("json encode error, err=%+v", err)
		return
	}
	t.Logf("json=%s", string(str))
}

序列化和反序列化的结果一致。


具体细节请参考这个链接:https://github.com/gogo/protobuf/blob/master/custom_types.md

have fun. 😃

0 条评论
请不要发布违法违规有害信息,如发现请及时举报或反馈
还没有人评论呢,速度抢占沙发!
相关文章
  • 今天这篇笔记重点讲goroutine 首先怎么定义goroutine 很简单,在方法前面加上go就可以了 func main() { go sayHello() } func sayHello()...

  • 概述建议先阅读 goroutine 小节。Go 箴言: 不要通过共享内存来通信,而要通过通信来共享内存。goroutine 是 Go 程序并发执行的实体,channel (通道) 则是它们之间的连接,...

  • 自定义类型关键字 type, 主要用来对同一种类型进行抽象。语法规则type 自定义类型名称 具体类型 # 例子 type Number int同时定义多个自定义类型package ma...

  • 注释 给别人看的,机器并不会执行这行语句 1.单行注释 // 我是单行注释 2.多行注释 /* 我是多行注释 我是多行注释 我是多行注释 我是多行注释 */ // 这是一个main函数,这个是go...

  • 概述函数 是将一个或者一类问题包装为一个代码块,可以被多次调用,提高代码重用性。Go 函数中声明、定义、参数、返回值这些基础概念,和其他编程语言中的一致,这里不再赘述。语法规则Go 函数支持单个返回值...

  • 一、前言很多人入门的go使用的第一个框架有可能就是go-micro,特别是go-micro/config库,已经成为了,很多其他框架写config配置的默认参考结构,今天我们就来学习一下,如何使用go...

  • 1 实验问题描述 设计程序模拟先进先出FIFO,最佳置换OPT和最近最久未使用LRU页面置换算法的工作过程。假设内存中分配给每个进程的最小物理块数为m,在进程运行过程中要访问的页面个数为n,页面访问序...

  • Go作为Google2009年推出的语言,其被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。 对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着...

  • dongle 是一个轻量级、语义化、对开发者友好的 Golang 编码解码和加密解密库Dongle 已被 awesome-go 收录, 如果您觉得不错,请给个 star 吧github.com/gol...

  • 前言 作为强类型的静态语言,golang的安全属性从编译过程就能够避免大多数安全问题,一般来说也唯有依赖库和开发者自己所编写的操作漏洞,才有可能形成漏洞利用点,在本文,主要学习探讨一下golang的一...

  • 概述当一个变量使用 var 进行声明后并未进行初始化 (变量后面没有赋值符 =) 操作,会默认分配一个零值 (zero value)。不同类型对应的零值类型零值boolfalseint0float0s...

  • 一 JWT介绍 JWT 英文名是 Json Web Token ,是一种用于通信双方之间传递安全信息的简洁的、URL安全的表述性声明规范,经常用在跨域身份验证。 JWT 以 JSON 对象的形式安全传...

  • 概述init() 函数 是一个特殊的函数,一般称为初始化函数,不能被调用。 在每个文件里面,当程序启动或者文件被作为包引用的时候,init() 函数就会自动执行,一般用来做一些包的初始化操作。语法规则...

  • 包包名给包及其目录命名时,应该使用简洁、清晰且全小写的名字包的默认导入优先从go的安装目录查找,然后去go path找,找到既停,没找到报错工具go build执行编译操作,有main包的情况下生成可...

  • 最近做的一个项目是采用前后端分离模式写前端,后端是fabric区块链,提供接口,需要使用post方法进行访问。如上一章注册用户,就是需要把用户名、账户信息转换成json形式 使用post方法传给后端区...

  • 我的客服系统使用的Golang+ Gin作为后端服务,所以下面的代码是演示demo 在 Go 语言中使用 Gin 框架实现 WebSocket 的方法如下: 安装 gin-gonic/websoc...

  • 前言Go官方团队在2022.12.08发布了Go 1.20 rc1(release candidate)版本,Go 1.20的正式release版本预计会在2023年2月份发布。让我们先睹为快,看看G...

  • 一 jaeger链路追踪介绍 什么是链路追踪: 分布式链路追踪就是将一次分布式请求还原成调用链路,将一次分布式请求的调用情况集中展示,比如各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的...

  • 所有人都听过这样一个歌谣:从前有座山,山里有座庙,庙里有个和尚在讲故事:从前有座山。。。。,虽然这个歌谣并没有一个递归边界条件跳出循环,但无疑地,这是递归算法最朴素的落地实现,本次我们使用Golang...

  • 1.Java VS Go语言Java,从源代码到编译成可运行的代码上图已经展示了这个过程:从Java的源代码编译成jar包或war包(字节码),最终运行在JVM中。我们把Java源代码编译后的jar包...