作者:张富春(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.

如何在proto3中用上golang对应的interface{}类型的更多相关文章

  1. 如何在Drupal7中用代码批量创建节点、评论和分类

    最近,我忙于一个网站迁移工作.网站是使用某个老式CMS建立的,有一定数量的文章.不同的分类数据和用户评论.我的团队被雇来把这些数据从这个浪费人力物力的老式CMS上完整的迁移到功能更现代的开源Drupa ...

  2. 如何在vscode中用standard style 风格去验证 vue文件

    1 JavaScript Standard Style简介 本工具通过以下三种方式为你(及你的团队)节省大量时间: 无须配置. 史上最便捷的统一代码风格的方式,轻松拥有. 自动代码格式化. 只需运行 ...

  3. [译]How to Install Node.js on Ubuntu 14.04 如何在ubuntu14.04上安装node.js

    原文链接为 http://www.hostingadvice.com/how-to/install-nodejs-ubuntu-14-04/ 由作者Jacob Nicholson 发表于October ...

  4. 如何在iOS地图上高效的显示大量数据

    2016-01-13 / 23:02:13 刚才在微信上看到这篇由cocoachina翻译小组成员翻译的文章,觉得还是挺值得参考的,因此转载至此,原文请移步:http://robots.thought ...

  5. CentOS6.5上golang环境配置

    CentOS6.5上golang环境配置 一.下载和解压go环境包 >>cd /usr/local/src/ >>wget -c http://golangtc.com/sta ...

  6. GPT分区基础知识及如何在GPT分区上安装WIN7

    大硬盘和WIN8系统,让我们从传统的BIOS+MBR模式升级到UEFI+GPT模式,现在购买的主流电脑,都是预装WIN8系统,为了更好的支持2TB硬盘,更快速的启动win8,预装系统都采取了GPT分区 ...

  7. 如何在Windows系统上用抓包软件Wireshark截获iPhone等网络通讯数据

    http://www.jb51.net/os/windows/189090.html 今天给大家介绍一种如何在Windows操作系统上使用著名的抓包工具软件Wireshark来截获iPhone.iPa ...

  8. 如何在Ubuntu Unity上修改应用程序图标

    转自如何在Ubuntu Unity上修改应用程序图标 这篇文章将教大家在Ubuntu Unity上修改应用程序图标,这个教程适合于Ubuntu 14.04, Ubuntu 13.10, Ubuntu ...

  9. 如何在CentOS 7上修改主机名

    如何在CentOS 7上修改主机名 在CentOS中,有三种定义的主机名:静态的(static),瞬态的(transient),和灵活的(pretty).“静态”主机名也称为内核主机名,是系统在启动时 ...

  10. 如何在 Android 手机上实现抓包?

    如何在 Android 手机上实现抓包? http://www.zhihu.com/question/20467503 我想知道某个应用究竟在数据提交到哪里,提交了什么.网上的教程太复杂,不想麻烦.有 ...

随机推荐

  1. 火山引擎在行为分析场景下的ClickHouse JOIN优化

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 背景 火山引擎增长分析DataFinder基于ClickHouse来进行行为日志的分析,ClickHouse的主要 ...

  2. Flutter如何有效地退出程序

    今天博主来谈一个开发Flutter App的小技巧--怎样有效地退出程序. 这种方法典型的应用场景就是用户许可协议的同意与否.从用户的角度讲,虽然大部分人都会无脑点击"同意",但我 ...

  3. 脑机接口 | 面向步态&神经电生理研究的非人灵长类模型与系统

    近期,海南大学生物医学工程学院脑机芯片神经工程团队在Frontiers in Neuroscience期刊上发表了题为<面向步态&神经电生理研究的非人灵长类模型与系统>的学术论文. ...

  4. CentOS7系统上安装升级Vim8

    基本步骤 1.卸载旧版vim yum remove vim* -y 2. 到Vim官方Github仓库下载目前最新的Vim Release版本 git clone https://github.com ...

  5. 【HZERO】消息发送

    消息发送 https://open.hand-china.com/community/detail/625843016338378752 新建模板 @Override public String sh ...

  6. #1495:非常可乐(BFS+数论)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1495 BFS解法 题目 给三个数字 s n m s=n+m s在1到100之间 就是个倒水问题可以从第 ...

  7. 若依封装的request.js

    import axios from 'axios' import { Notification, MessageBox, Message } from 'element-ui' import stor ...

  8. el-menu菜单过长,显示不全问题

  9. B3637-DP【橙】

    这题我用sort的时候大意了,从1开始使用的下标但是用sort时没加1导致排序错误,排了半天错才发现. 另外,这道题我似乎用了一种与网络上搜到了做法截然不同的自己的瞎想出来的做法,我的这个做法需要n^ ...

  10. shell脚本(9)-流程控制for

    一.循环介绍 for循环叫做条件循环,或者for i in,可以通过for实现流程控制 二.for语法 1.for语法一:for in for var in value1 value2 ...... ...