package main
import (
"encoding/json"
"fmt"
"reflect"
)
func main() {
var request = `{"id":7044144249855934983,"name":"demo"}`
var test interface{}
err := json.Unmarshal([]byte(request), &test)
if err != nil {
fmt.Println("error:", err)
}
obj := test.(map[string]interface{})
dealStr, err := json.Marshal(test)
if err != nil {
fmt.Println("error:", err)
}
id := obj["id"]
fmt.Println(string(dealStr))
fmt.Printf("%+v\n", reflect.TypeOf(id).Name())
fmt.Printf("%+v\n", id.(float64))
}
打印结果
{"id":7044144249855935000,"name":"demo"}
float64
7.044144249855935e+18
原因
解决方案
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
var request = `{"id":7044144249855934983}`
var test interface{}
decoder := json.NewDecoder(strings.NewReader(request))
decoder.UseNumber()
err := decoder.Decode(&test)
if err != nil {
fmt.Println("error:", err)
}
objStr, err := json.Marshal(test)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(objStr))
}
为什么float64可能出现精度缺失,就必须要搞清楚二进制科学计算法和IEEE754标准的基本原理。
func main() {
u1 := UserInfo{Name: "Rolle", Age: 18}
b, _ := json.Marshal(&u1)
var m map[string]interface{}
_ = json.Unmarshal(b, &m)
for k, v := range m{
fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
}
}
// key:name value:Rolle value type:string
// key:age value:18 value type:float64
看起来没什么问题,但其实这里是有一个“坑”的。那就是Go语言中的json包在序列化空接口存放的数字类型(整型、浮点型等)都会序列化成float64类型。
也就是上面例子中m["age"]现在底层是一个float64了,不是个int了
解决办法、反射
// ToMap 结构体转为Map[string]interface{}
func ToMap(in interface{}, tagName string) (map[string]interface{}, error){
out := make(map[string]interface{})
v := reflect.ValueOf(in)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct { // 非结构体返回错误提示
return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v)
}
t := v.Type()
// 遍历结构体字段
// 指定tagName值为map中key;字段值为map中value
for i := 0; i < v.NumField(); i++ {
fi := t.Field(i)
if tagValue := fi.Tag.Get(tagName); tagValue != "" {
out[tagValue] = v.Field(i).Interface()
}
}
return out, nil
}
是否遇到过在无法准确确定json层级关系的情况下对json进行解析的需求
具体的,返回结果有两种情况
第一种
{
"return": "0",
"result":[
{
"goods_id": 37278077211,
....
"shop_name": "xxxxx",
}
]
}
第二种
{
"return": "0",
"result": {
"data": [
{
"goods_id": 58054798450,
......
"shop_name": "xxxxxxxx"
}
]
}
}
由于在解析前我们并不能确定result到底是一个struct还是一个Slice,因此我们也无法直接利用json.Unmarshal一步解出对应的struct对象。好在我们知道所有json都可以直接解析成map[string]interface{}的结构,因此我们可以将json先转化为map,然后根据结构名key去决定后续的转换流程,具体代码如下:
解决方案
将json直接解析为map
var object interface{}
var data interface{}
err := json.Unmarshal([]byte(jsonStr),&object)
if err != nil{
fmt.Printf("unmarshal %s error: %s\n",jsonStr,err.Error())
}
//判断returnCode
ret := object.(map[string]interface{})["return"]
if ret != 0{
fmt.Println("the response of http error")
}
//判断result是何种类型
result := object.(map[string]interface{})["result"]
resultType := reflect.TypeOf(result)
if resultType.Kind() == reflect.Map{
data = result.(map[string]interface{})["data"]
}
if resultType.Kind() == reflect.Slice{
data = result
}
//解析goods_id
var skuList []int64
for _,v := range data.([]interface{}){
preSku := v.(map[string]interface{})["goods_id"].(float64)
skuList = append(skuList,int64(preSku))
}
fmt.Printf("the skuLst = %+v\n",skuList)
这种方式的优点是只需要Unmarshal一次,缺点是每一级都需要显示的去做类型转化,书写起来比较繁琐。尤其是json本身结构复杂,其中只有一小部分需要确定具体类型的情况下,解析过程会更加繁琐复杂.
是否可以只解析确定部分,不确定的部分先保留[]byte的原始格式,按map解析
这时候就需要用到json.RawMessage字段类型
在解析json过程中,有时可能只需要解析json的某一部分数据,比如,当json中只有一部分是需要的数据,或者需要先解析一部分数据,才能根据解析的部分数据来决定剩余数据如何解析。继续以上面的需求为例。此时需要预先定义需要解析的部分
type RespStruct struct {
RetCode int `json:"return"`
Result json.RawMessage `json:"result"`
}
首先解析return字段。result字段内容将继续保持[]byte类型的状态。接下来继续解析剩余部分
var object RespStruct
err := json.Unmarshal([]byte(jsonStr2),&object)
if err != nil{
fmt.Printf("unmarshal %s error: %s\n",jsonStr,err.Error())
}
//判断returnCode
if object.RetCode != 0{
fmt.Println("the response of http error")
}
//判断result是何种类型
var data interface{}
err = json.Unmarshal(object.Result,&data)
if err != nil{
fmt.Printf("unmarshal %s error: %s\n",object.Result,err.Error())
}
resultType := reflect.TypeOf(data)
if resultType.Kind() == reflect.Map{
data = data.(map[string]interface{})["data"]
}
//解析goods_id
var skuList []int64
for _,v := range data.([]interface{}){
preSku := v.(map[string]interface{})["goods_id"].(float64)
skuList = append(skuList,int64(preSku))
}
fmt.Printf("the skuLst = %+v\n",skuList)
json.Number类型的使用
goods_id字段的类型先由interface{}类型转为float64,然后才被转换为需要的int64呢?
这是因为在 json 中是没有整型和浮点型之分的,当利用json 包中的 Unmarshal 方法将数字类型解析为interface{}时,它就会将把所有数字类型全部转换为和规范最接近的float64类型。如果希望更加方便的将数字类型准换为指定的类型,就需要用到json.Number这个类型。具体如下:
var object RespStruct
err := json.Unmarshal([]byte(jsonStr),&object)
if err != nil{
fmt.Printf("unmarshal %s error: %s\n",jsonStr,err.Error())
}
//判断returnCode
if object.RetCode != 0{
fmt.Println("the response of http error")
}
//判断result是何种类型
var data interface{}
decoder := json.NewDecoder(bytes.NewReader(object.Result))
decoder.UseNumber()
decoder.Decode(&data)
resultType := reflect.TypeOf(data)
if resultType.Kind() == reflect.Map{
data = data.(map[string]interface{})["data"]
}
//解析goods_id
var skuList []int64
for _,v := range data.([]interface{}){
preSku,err := v.(map[string]interface{})["goods_id"].(json.Number).Int64()
if err != nil{
fmt.Printf("get goods_id error")
}
skuList = append(skuList,preSku)
}
fmt.Printf("the skuLst = %+v\n",skuList)
json.Number本身是string类型,只是在json包中被定义了别名,然后通过封装的三个方法,实现了将string转换为int64和float64类型的方法。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。