先来看一下 : JSON number的那些事
golang 数据类型和json类型对照关系表:
bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null
json中没有int64类型的,反序列化的时候,如果直接用json.Unmarshal 可能会精度丢失
可以参考这篇文章:json 反序列化数字精度丢失
原理:
解析 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(®ular)
if err != nil {
return nil, err
}
return ®ular, 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
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)