字段缺失_区分Protobuf 3中缺失值和默认值

59a7e573d61a0a21ff84118e7523bc2b.png
来自公众号:新世界杂货铺

这两天翻了翻以前的项目,发现不同项目中关于Protobuf 3缺失值和默认值的区分居然有好几种实现。今天笔者冷饭新炒,结合项目中的实现以及切身经验共总结出如下六种方案。

增加标识字段

众所周知,在Go中数字类型的默认值为0(这里仅以数字类型举例),这在某些场景下往往会引起一定的歧义。

is_show字段为例,如果没有该字段表示不更新DB中的数据,如果有该字段且值为0则表示更新DB中的数据为不可见,如果有该字段且值为1则表示更新DB中的数据为可见。

上述场景中,实际要解决的问题是如何区分默认值和缺失字段。增加标识字段是通过额外增加一个字段来达到区分的目的。

例如:增加一个has_show_field字段标识is_show是否为有效值。如果has_show_fieldtrueis_show为有效值,否则认为is_show未设置值。

此方案虽然直白,但每次设置is_show的值时还需设置has_show_field的值,甚是麻烦故笔者十分不推荐。

字段含义和默认值区分

字段含义和默认值区分即不使用对应类型的默认值作为该字段的有效值。接着前面的例子继续描述,is_show为1时表示展示,is_show为2时表示不展示,其他情况则认为is_show未设置值。

此方案笔者还是比较认可的,唯一问题就是和开发者的默认习惯略微不符。

使用oneof

oneof 的用意是达到 C 语言 union 数据类型的效果,但是诸多大佬还是发现它可以标识缺失字段。

message Status {oneof show {int32 is_show = 1;}
}
message Test {int32 bar = 1;Status st = 2;
}

上述proto文件生成对应go文件后,Test.StStatus的指针类型,故通过此方案可以区分默认值和缺失字段。但是笔者认为此方案做json序列化时十分不友好,下面是笔者的例子:

// oneof to json
ot1 := oneof.Test{Bar: 1,St: &oneof.Status{Show: &oneof.Status_IsShow{IsShow: 1,},},
}
bts, err := json.Marshal(ot1)
fmt.Println(string(bts), err)
// json to oneof failed
jsonStr := `{"bar":1,"st":{"Show":{"is_show":1}}}`
var ot2 oneof.Test
fmt.Println(json.Unmarshal([]byte(jsonStr), &ot2))

上述输出结果如下:

{"bar":1,"st":{"Show":{"is_show":1}}} <nil>
json: cannot unmarshal object into Go struct field Status.st.Show of type oneof.isStatus_Show

通过上述输出知,oneof的json.Marshal输出结果会额外多一层,而json.Unmarshal还会失败,因此使用oneof时需谨慎。

使用wrapper类型

这应该是google官方提出的解决方案,我们看看下面的例子:

import "google/protobuf/wrappers.proto";
message Status {google.protobuf.Int32Value is_show = 1;
}
message Test {int32 bar = 1;Status st = 2;
}

使用此方案需要引入google/protobuf/wrappers.proto。此方案生成对应go文件后,Test.St也是Status的指针类型。同样,我们也看一下它的json序列化效果:

wra1 := wrapper.Test{Bar: 1,St: &wrapper.Status{IsShow: wrapperspb.Int32(1),},
}
bts, err = json.Marshal(wra1)
fmt.Println(string(bts), err)
jsonStr = `{"bar":1,"st":{"is_show":{"value":1}}}`
// 可正常转json
var wra2 wrapper.Test
fmt.Println(json.Unmarshal([]byte(jsonStr), &wra2))

上述输出结果如下:

{"bar":1,"st":{"is_show":{"value":1}}} <nil>
<nil>

和oneof方案相比wrapper方案的json反序列化是没问题的,但是json.Marshal的输出结果也会额外多一层。另外,经笔者在本地试验,此方案无法和gogoproto一起使用。

允许proto3使用optional标签

前面几个方案估计在实践中还是不够尽善尽美。于是2020年5月16日protoc v3.12.0发布,该编译器允许proto3的字段也可使用 optional修饰。

下面看看例子:

message Status {optional int32 is_show = 1;
}
message Test {int32 bar = 1;Status st = 2;
}

此方案需要使用新版本的protoc且必须使用--experimental_allow_proto3_optional开启此特性。protoc升级教程见https://github.com/protocolbuffers/protobuf#protocol-compiler-installation。下面继续看看该方案的json序列化效果

var isShow int32 = 1
p3o1 := p3optional.Test{Bar: 1,St:  &p3optional.Status{IsShow: &isShow},
}
bts, err = json.Marshal(p3o1)
fmt.Println(string(bts), err)
var p3o2 p3optional.Test
jsonStr = `{"bar":1,"st":{"is_show":1}}`
fmt.Println(json.Unmarshal([]byte(jsonStr), &p3o2))

上述输出结果如下:

{"bar":1,"st":{"is_show":1}} <nil>
<nil>

据上述结果知,此方案与oneof以及wrapper方案的json序列化相比更加符合预期,同样,经笔者在本地试验,此方案无法和gogoproto一起使用。

proto2和proto3结合使用

作为一个gogoproto的忠实用户,笔者希望在能区分默认值和缺失值的同时还可以继续使用gogoproto的特性。于是便产生了proto2和proto3结合使用的野路子。

// proto2
message Status {optional int32 is_show = 2;
}
// proto3
message Test {int32 bar = 1 [(gogoproto.moretags) = 'form:"more_bar"', (gogoproto.jsontag) = 'custom_tag'];p3p2.Status st = 2;
}

需要区分缺失字段和默认值的message定义在语法为proto2的文件中,proto3通过import导入proto2的message以达区分目的。

optional修饰的字段在Go中会生成指针类型,因此区分缺失值和默认值就变的十分容易了。下面看看此方案的json序列化效果:

// p3p2 to json
p3p21 := p3p2.Test{Bar: 1,St:  &p3p2.Status{IsShow: &isShow},
}
bts, err = json.Marshal(p3p21)
fmt.Println(string(bts), err)
var p3p22 p3p2.Test
jsonStr = `{"custom_tag":1,"st":{"is_show":1}}`
fmt.Println(json.Unmarshal([]byte(jsonStr), &p3p22))

上述输出结果如下:

{"custom_tag":1,"st":{"is_show":1}} <nil>
<nil>

根据上述结果知,此方案不仅能够活用gogoproto的各种tag,其结果也和在proto3中直接使用optional效果一致。虽然笔者已经在自己的项目中使用了此方案,但是仍然要提醒一句:“写本篇文章时,笔者特意去github看了gogoproto的发布日志,gogoproto最新一个版本发布时间为2019年10月14日,笔者大胆预言gogoproto以后不会再更新了,所以此方案还请大家酌情使用”。

最后,衷心希望本文能够对各位读者有一定的帮助。

注:
1. 文中笔者所用go版本为:go1.15.2
2. 文中笔者所用protoc版本为:3.14.0
3. 文章中所用完整例子:https://github.com/Isites/go-coder/blob/master/pbjson/main.go

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/326736.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

当初怎么就入了VS的坑

使用Turbo C和Turbo Pascal开始学习编程貌似也是一代人的集体记忆。这方面我也没什么忌讳&#xff0c;毕竟Borland Turbo C在九十年代末的PC上面还是一个运行效率不错的开发工具。然而启蒙老师的水平限制&#xff0c;那个时候所学习的编写和调试技术都不过关&#xff0c;一个超…

ssh(Spring+Spring mvc+hibernate)——hibernate.cfg.xml

<?xml version1.0 encodingUTF-8?> <!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Configuration DTD 3.0//EN""http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"><!-- Generated by MyEclipse H…

生成赫夫曼编码表

package com.atguigu.huffmancode;import com.sun.org.glassfish.external.statistics.CountStatistic; import com.sun.org.glassfish.external.statistics.StringStatistic;import java.util.*;/*** 创建人 wdl* 创建时间 2021/3/27* 描述*/ public class HuffmanCode {publi…

Mysql截取字段中 用逗号分割 省市县镇街道社区小区

Mysql截取字段中 用逗号分割 省市县镇街道社区小区 select id , name, substring_index(name,,,0) as province0, substring_index(name,,,1) as province1, substring_index(name,,,2) as province2, substring_index(name,,,3) as province3, substring_index(name,,,4) as…

如何导出Navicat的数据库连接配置

如何导出Navicat的数据库连接配置 如何导出Navicat的数据库连接配置_石头wang的博客-CSDN博客_navicat 导出配置 如何导出Navicat的数据库连接配置 背景 我们可能会在不同的电脑安装Navicat&#xff0c;这些连接的配置如何从一台电脑迁移到另外一条&#xff1f;自行备份是比较…

Spring BeanFactory 容器

转载自 Spring BeanFactory 容器 Sping 的 BeanFactory 容器 这是一个最简单的容器&#xff0c;它主要的功能是为依赖注入 &#xff08;DI&#xff09; 提供支持&#xff0c;这个容器接口在 org.springframework.beans.factory.BeanFactory 中被定义。 BeanFactory 和相关的…

C#开发人员应该知道的13件事情

本文讲述了C#开发人员应该了解到的13件事情&#xff0c;希望对C#开发人员有所帮助。 1. 开发过程 开发过程是错误和缺陷开始的地方。使用工具可以帮助你在发布之后&#xff0c;解决掉一些问题。 编码标准 遵照编码标准可以编写出更多可维护的代码&#xff0c;特别是在由多个开发…

充电提示音_iPhone如何自定义充电提示音,图文教程

昨天给小伙伴们分享了几个iOS14的充电提示音和设置方法&#xff0c;不过有的小伙伴可能需要设置自己喜欢的声音为充电提示音&#xff0c;所以今天就来教大家自定义喜欢的iOS14充电提示音。教程由于比较仔细&#xff0c;看起来比较长&#xff0c;其实很简单&#xff0c;主要分为…

Scanner获取用户输入

next()和hasNext() public class Demo02_Scanner {public static void main(String[] args) {//创建一个扫描器对象&#xff0c;用于接收键盘数据Scanner scanner new Scanner(System.in);System.out.println("使用next方式接收: ");//判断用户有没有输入字符串if …

idea Lombok插件

此文提供对于Lombok的简单使用 1、Lombok简介 Lombok能通过注解的方式&#xff0c;在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。 2、Lombok插件安装 想要使用 Lombok&#xff0c;必须要安装一个 Lombok 插件&#xff0c;不然编辑器无法识…

复变函数画图cosz图像_用matlab绘制x=(1+cosu)cosv,y=(1+cosu)sinv,z=sinu图像?

用matlab绘制x(1cosu)cosv,y(1cosu)sinv,zsinu图像&#xff1f;234游戏网友 提出于 2019-07-19 07:04:26u-2*pi:0.01:2*pi;v-2*pi:0.01:2*pi;x(1cosu).*cosv;y(1cosu).*sinv;zsinu;polt3(x,y,z)xlabel(x);ylabel(y);zlabel(z);grid但是出现了&#xff1a;Errorusingplot3Vector…

赫夫曼编码字节数组

思路分析 代码实现 package com.atguigu.huffmancode;import com.sun.org.glassfish.external.statistics.CountStatistic; import com.sun.org.glassfish.external.statistics.StringStatistic;import java.util.*;/*** 创建人 wdl* 创建时间 2021/3/27* 描述*/ public class…

Spring ApplicationContext 容器

转载自 Spring ApplicationContext 容器 Spring ApplicationContext 容器 Application Context 是 spring 中较高级的容器。和 BeanFactory 类似&#xff0c;它可以加载配置文件中定义的 bean&#xff0c;将所有的 bean 集中在一起&#xff0c;当有请求的时候分配 bean。 另…

.NET Core 工具中的新内容

Visual Studio 2017 RC最近一个版本更新包括一套更新的.NET Core工具箱。这个版本带来了几项改进&#xff0c;包括改变了模版化、dotnet网络命令&#xff0c;以及许多缺陷修复。微软的Rich Lander发表了一份更新说明&#xff0c;陈述了.NET Core开发人员可以预期在.NET Core RC…

python binascii array('c')_详解Python中的array数组模块相关使用

初始化array实例化可以提供一个参数来描述允许那种数据类型&#xff0c;还可以有一个初始的数据序列存储在数组中。import arrayimport binasciis This is the array.a array.array(c, s)print As string:, sprint As array :, aprint As hex :, binascii.hexlify(a)数组配置…

你还可以续命几次?回顾DockPanel Suite项目的发展史

微软在设计Visual Studio.NET的时候汲取了之前IDE版本的各种教训&#xff0c;开始考虑怎样让开发人员能够快速调整多窗口的布局&#xff0c;灵活构建自己喜欢的日常界面。这种基于菜单条和悬浮面板的新界面也让Windows平台其他软件开发者豁然开朗&#xff0c;开始各种借鉴。微软…

利用赫夫曼编码进行数据解压

基本概念 代码实现 package com.atguigu.huffmancode;import com.sun.org.glassfish.external.statistics.CountStatistic; import com.sun.org.glassfish.external.statistics.StringStatistic;import java.security.AlgorithmConstraints; import java.util.*;/*** 创建人 w…

Spring Bean 定义

转载自 Spring Bean 定义 Bean 定义 被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的。bean 是一个被实例化&#xff0c;组装&#xff0c;并通过 Spring IoC 容器所管理的对象。这些 bean 是由用容器提供的配置元数据创建的&#xff0c;例如&#xff…

mysql5.7安装

MySQL 5.7 安装教程&#xff08;全步骤、保姆级教程&#xff09;_普通网友的博客-CSDN博客_mysql5.7的安装教程 Index of /mysql/downloads/MySQL-5.7/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror This application requires Visual Studio 2013 x64 Redistribut…