博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Golang:JSON和int64、interface的那些事儿
阅读量:3953 次
发布时间:2019-05-24

本文共 4908 字,大约阅读时间需要 16 分钟。

先来看一下 :


golang 数据类型和json类型对照关系表:

bool, for JSON booleansfloat64, for JSON numbersstring, for JSON strings[]interface{
}, for JSON arraysmap[string]interface{
}, for JSON objectsnil for JSON null

json中没有int64类型的,反序列化的时候,如果直接用json.Unmarshal 可能会精度丢失

可以参考这篇文章:

原理

解析 JSON 的时候其实是对 JSON 串的遍历,其实所有遍历出来的值都是[]byte类型,然后根据识别目标解析对象字段类型,或者识别[]byte数据的内容转换格式。

比如,如果数据被解析到int上,把[]byte转换为int;

如果被解析到interface{}上,就只能通过[]byte的类型来转换了,
而且数字会被统一处理成float64,这个有个问题,就是会丢精度。
而通过Number解析时,值会被直接保存为字符串类型

正确做法:

func DecodeMyStruct(jsonStr string) (*MyStruct, error) {
var regular *MyStruct decoder := jsoniter.NewDecoder(strings.NewReader(jsonStr)) //使用UseNumber()可以使得json中的大整数转换时,不会转换成浮点数float64,但是要用decoder不能用json.Marshal decoder.UseNumber() err := decoder.Decode(&regular) if err != nil {
return nil, err } return &regular, nil}

其中 MyStruct是我们需要反序列化的类型,可以是struct,slice, map等等.

如果不知道应该反序列化成啥类型,可以是map[string]interface{},用的时候可以配合断言使用,

比如下面一种场景:需要对比两个json反序列化成map[string]interface{}的结构,

判断:filterAttrMap 是不是包含 conditionMap的所有键值对。

func CompareRecursiveV2(ctx context.Context, filterAttrMap, conditionMap map[string]interface{
}) bool {
for conditionKey, conditionValue := range conditionMap {
filterValue, filterOk := filterAttrMap[conditionKey] if !filterOk {
return false } // 断言判断值interface{}的类型 switch conditionValue.(type) {
case json.Number: // !!!!这里不能是int64 filterJson, ok1 := filterValue.(json.Number) conditionJson, ok2 := conditionValue.(json.Number) filter := filterJson.String() condition := conditionJson.String() if !ok1 || !ok2 || filter != condition {
return false } case string: filter, ok1 := filterValue.(string) condition, ok2 := conditionValue.(string) if !ok1 || !ok2 || filter != condition {
return false } case bool: filter, ok1 := filterValue.(bool) condition, ok2 := conditionValue.(bool) if !ok1 || !ok2 || filter != condition {
return false } default: conditionValueMap, ok1 := conditionValue.(map[string]interface{
}) if !ok1 {
logs.CtxFatal(ctx, "conf error, only support[int64, string, bool, map]") return false } filterValueMap, ok2 := filterValue.(map[string]interface{
}) if !ok1 || !ok2 {
return false } // value类型还是map[string]interface{},递归解析比较 return CompareRecursiveV2(ctx, filterValueMap, conditionValueMap) } } return true}

上面case的第一项不能是 int64, 因为两个比较的map是有json转来的,interface{} 里面数字只有number类型(实际上是string存储的),所以,要case number类型,并且转化成字符串判断两个value是不是一样。

这里和下面情况不一样,下面例子:updateStudent 也是 map[string]interface{}类型

但是updateStudent不是json转换的,是代码显式赋值的

var student_id int64 = 123456    updateStudent["student_id"]= student_id    id, ok := updateStudent["student_id"].(int64) // 可以用int64	if !ok {
fmt.print("断言失败, key:stedent_id 不是int64类型") } else {
fmt.printf(" key:stedent_id 是int64类型, 并且值为:%d", id) }

补充另外一种两个map[string]interface{}的比较,这里多了一个regular,也是map[string]interface{}类型的

是一个云上配置的json结构,我们可以动态的在云上配置这个regular,来定义两个map需要比较的字段和字段的类型(key和value都设置成了string类型,方便switch-case)
在每次需要对比的时候,就通过客户端从云上拉取这个json,转换成map[string]interface{},进行比较
比如:

{    "student_id":"int64",    "student_name":"string",    "genre":"int64",    "status":"int64",    "extra_struct":{        "father":"string",        "risk_rate":"int64",        "dynamic_struct":{            "yuwen_score":"int64",            "shuxue_score":"int64"        }    }}
func CompareRecursive(ctx context.Context, regular, filterAttrMap, conditionMap map[string]interface{
}) bool {
for regularKey, regularValue := range regular {
filterValue, filterOk := filterAttrMap[regularKey] conditionValue, conditionOk := conditionMap[regularKey] if filterOk && conditionOk {
if regularValue == "int64" {
filterJson, ok1 := filterValue.(json.Number) conditionJson, ok2 := conditionValue.(json.Number) filter := filterJson.String() condition := conditionJson.String() if !ok1 || !ok2 || filter != condition {
return false } } else if regularValue == "string" {
filter, ok1 := filterValue.(string) condition, ok2 := conditionValue.(string) if !ok1 || !ok2 || filter != condition {
return false } } else if regularValue == "bool" {
filter, ok1 := filterValue.(bool) condition, ok2 := conditionValue.(bool) if !ok1 || !ok2 || filter != condition {
return false } } else {
regularValueMap, ok := regularValue.(map[string]interface{
}) if !ok {
logs.CtxFatal(ctx, "%s conf error, support[int64, string, bool, map]", regularKey) return false } filterValueMap, ok1 := filterValue.(map[string]interface{
}) conditionValueMap, ok2 := conditionValue.(map[string]interface{
}) if !ok1 || !ok2 {
return false } return CompareRecursive(ctx, regularValueMap, filterValueMap, conditionValueMap) } } else if !filterOk && conditionOk {
return false } } return true}

转载地址:http://bikzi.baihongyu.com/

你可能感兴趣的文章
shell的dirname $0和readlink用法
查看>>
设计模式——设计模式三大分类以及六大原则
查看>>
Android开发——ListView局部刷新的实现
查看>>
Android开发——ListView的复用机制源码解析
查看>>
Android开发——架构组件LiveData源码解析
查看>>
IDEA常用快捷键整理
查看>>
【Vue】两个元素同一行显示
查看>>
XXL-Job使用
查看>>
如何在SwaggerAPI中添加统一授权认证
查看>>
多线程
查看>>
【Linux】Centos7 常用命令
查看>>
【Redis】Centos7下安装Redis
查看>>
【Redis】Centos7下搭建Redis集群
查看>>
【Redis】Centos7下搭建Redis集群——哨兵模式
查看>>
【Linux】本地ping不同VM虚拟机
查看>>
【SpringCloud】Hystrix
查看>>
乐观锁、悲观锁、公平锁、可重入锁
查看>>
快速阅读——《认知篇》
查看>>
【C#】返回值为DataTable的数据
查看>>
【Asp.net】基本概念
查看>>