前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Go Json 坑

Go Json 坑

原创
作者头像
Michel_Rolle
发布于 2024-06-08 10:25:31
发布于 2024-06-08 10:25:31
2.9K00
代码可运行
举报
文章被收录于专栏:golang分享golang分享
运行总次数:0
代码可运行

Unmarshal精度丢失

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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))
}

打印结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{"id":7044144249855935000,"name":"demo"}
float64
7.044144249855935e+18

原因

  • 在json的规范中,对于数字类型是不区分整形和浮点型的。
  • 在使用json.Unmarshal进行json的反序列化的时候,如果没有指定数据类型,使用interface{}作为接收变量,其默认采用的float64作为其数字的接受类型
  • 当数字的精度超过float能够表示的精度范围时就会造成精度丢失的问题

解决方案

  • 将id改为string传递
  • 使用json.number 类型来避免对float64的使用
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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标准的基本原理。

结构体转map[string]interface{} 类型发生变化

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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了

解决办法、反射

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 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层级关系的情况下对json进行解析的需求

具体的,返回结果有两种情况

第一种

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  "return": "0",
  "result":[
    {
      "goods_id": 37278077211,
      ....
      "shop_name": "xxxxx",
    }
  ]
}

第二种

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  "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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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中只有一部分是需要的数据,或者需要先解析一部分数据,才能根据解析的部分数据来决定剩余数据如何解析。继续以上面的需求为例。此时需要预先定义需要解析的部分

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type RespStruct struct {
    RetCode int `json:"return"`
    Result json.RawMessage `json:"result"`
}

首先解析return字段。result字段内容将继续保持[]byte类型的状态。接下来继续解析剩余部分

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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这个类型。具体如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 删除。

评论
登录后参与评论
暂无评论
推荐阅读
音频格式的汇总及压缩比较
数字音源,也就是数字音频格式,最早指的是CD,CD经过压缩之后,又衍生出多种适于在随身听上播放的格式,这些压缩过的格式,我们可以分为两大类:有损压缩的和无损压缩的。这里所说的压缩,是指把PCM编码的或者是WAV格式的音频流经过特殊的压缩处理,转换成其他格式,从而达到减小文件体积的效果。有损/无损,是指经过压缩过后,新文件所保留的声音信号相对于原来的PCM/WAV格式的信号是否有所削减。
ZONGLYN
2019/08/08
10.9K0
AudFree Audio Converter 2.9.0 Mac音频格式文件转换器
AudFree Audio Converter是一款功能强大的音频转换软件,该软件的最大特点是支持多种加密音频格式的转换,如DRM M4P、M4B、AA、AAX等,可以帮助用户轻松转换iTunes购买的音频、亚马逊音乐等流媒体平台下载的音频。
用户1517359
2023/05/20
6310
AudFree Audio Converter 2.9.0 Mac音频格式文件转换器
直播系统开发中视频采集的技术分析
采集是整个视频推流过程中的第一个环节,它从系统的采集设备中获取原始视频数据,将其输出到下一个环节。直播系统开发中视频的采集涉及两方面数据的采集:音频采集和图像采集,它们分别对应两种完全不同的输入源和数据格式。
布谷安妮
2019/09/23
1.1K0
直播系统开发中视频采集的技术分析
NCH Switch Plus Mac直装版(音频转换器)
喜欢的歌曲手机上不支持这种格式播放怎么办?今天小编今天分享的NCH Switch Plus mac版是Macos上一款音频转换工具,可以快速转换各种音频格式,使用便捷,还能在转换之前播放曲目。
Mac知识分享
2022/09/28
5840
PCDJ DEX 3 for Mac(dj混音软件)
PCDJ DEX是一款专业的DJ软件,可以帮助用户进行音乐混音和创作。它支持多种音频格式,包括MP3、WAV、AAC和FLAC等,并且可以使用外部DJ设备进行控制。PCDJ DEX还提供了多种特效和样式,可以让用户创造出自己独特的音乐风格。
皮西歪
2023/04/27
9890
PCDJ DEX 3 for Mac(dj混音软件)
Sidify Apple Music Converter mac(Apple音乐转换器)
Sidify Apple Music Converter是一款全功能,功能强大的专业Apple音乐转换器,不仅可以转换Apple音乐文件,还可以转换所有可在iTunes中播放的音乐,包括iTunes M4A / M4B,Audible AA / AAX有声读物和iTunes M4P音乐,转换时可以选择最大20倍的转换速度。
Mac小小
2022/08/26
1.4K0
会声会影2023安装注意事项和具体下载安装步骤
会声会影2023视频编辑软件是一款全面的视频编辑软件,集成了视频编辑、屏幕录制、格式转换这三大功能。会声会影2023在视频编辑功能上,用户可自由选择剪切、裁剪、添加背景音乐、字幕、特效等多种编辑工具。会声会影2023的视频编辑界面简洁,操作步骤十分简单,即使新手也能快速上手。会声会影2022视频编辑软件支持导入和输出几乎所有的主流视频/音频格式,具有广泛的格式支持。对于电脑用户来说,会声会影2023视频编辑软件无疑是最好的选择。通过 会声会影2023剪辑软件,您可以在电脑上查看、编辑和共享影片。会声会影2023剪辑软件是一款基础视频编辑软件,专为非专业用户使用。软件界面简约,您可快速对视频进行剪辑、添加标题、特效、音乐、字幕等编辑操作。该软件支持大多数流行的音频/视频格式和编解码器,甚至包括 4K UHD,3D和VR 360度视频的高分辨率视频素材。会声会影2023剪辑软件的基础版是免费的,部分高级功能需购买专业版才能使用。会声会影2023很多玩家在下载之后不知道怎么安装?有些甚至会出现安装失败的情况,所以小编将安装注意事项和具体的安装步骤分享给大家,步骤很详细,可参考。
用户7442547
2023/01/02
2.1K0
Wondershare Recoverit for Mac(万兴数据恢复软件)
Wondershare Recoverit for Mac是一款mac数据恢复套件,能够帮助用户恢复因为意外删除,格式化,系统崩溃等原因所失去的视频,照片,信息,邮件等各种文件。
Mac软件分享
2022/07/26
8461
Wondershare Recoverit for Mac(万兴数据恢复软件)
VideoSolo Video Converter Ultimate Mac(视频转换器)
VideoSolo Video Converter Ultimate Mac是一款强大而简单的视频转换器,videosolo video converter ultimate for mac集视频转换、屏幕截取、网络视频下载、DVD刻录、影片编辑等功能于一体,你可以对视频进行剪辑,水印和效果等编辑。
Mac知识分享
2022/09/21
2.7K0
Avdshare Audio Converter for Mac(音频转换器) 7.5.0激活版
Avdshare Audio Converter for Mac音频转换器分享给大家,Avdshare Audio Converter是一款功能全面、界面简洁的音频转换器。Avdshare Audio Converter官方版能够帮助用户进行音频转换可以将各类音频转换成常见的MP3,WAV,OGG等格式,Avdshare Audio Converter最新版功能强劲还可以支持批量转换,而且转换后音质基本不会受到影响。
一小朵
2023/02/17
8730
Avdshare Audio Converter for Mac(音频转换器) 7.5.0激活版
Bigasoft Total Video Converter for Mac(视频转换器) v5.6.4中文版
Bigasoft Total Video Converter Mac中文版是一款专业和易于使用的Mac视频转换器,是专为Mac用户可以轻松地各种视频格式,包括MP4,MOV,MKV,AVI,MPEG,Xvid,MPEG之间轻松转换,DIVX,H.264,3GP转换之间,WMV,FLV,MOD,TOD等,各种音频格式,包括APE,MP3,AAC,AC3,WAV,WMA之间的裂口,甚至从中提取视频文件的音频。
皮西歪
2022/11/28
1.6K0
Bigasoft Total Video Converter for Mac(视频转换器) v5.6.4中文版
强大的Mac端视频播放器工具推荐,Elmedia Player Pro下载
Elmedia Player Pro Mac端是一款强大的视频播放器,不仅仅只能单纯的播放视频,还可以做到高级视频回放,您可以便捷的调整回放速度;循环播放视频或者音频的任意部分;甚至创建并管理书签,对音视频标记最喜欢的位置,并且还可以打开在线视频,直接在应用中打开YouTube、Vimeo和DAIlymotion视频,可免除任何广告,非常方便。
啾咪啾咪
2023/02/09
1.5K0
4Easysoft Video Converter for Mac(视频转换器)
4Easysoft Video Converter for Mac 是一款专业的视频转换软件,它可以将多种视频格式转换为其他常见的视频格式,如 MP4、AVI、WMV、MOV 等,并且支持将视频转换为在各种设备上播放所需的格式,如 iPhone、iPad、Android 等移动设备。
Mac小小心
2023/04/26
2K0
4Easysoft Video Converter for Mac(视频转换器)
Allavsoft Mac(专业视频下载工具)中文直装版
Allavsoft for Mac是一款MacOS系统上的专业视频下载工具,支持任何视频文件格式的转换,支持从Facebook,Yahoo Video,Google Video,DailyMotion,eHow等各种视频分享网站下载视频。Allavsoft支持将视频下载和转换为流行的视频格式。
Mac知识分享
2022/08/18
1K0
任意格式相互转换
相信大家在平常生活中一定会用到格式直接的相互转换,比如视频格式转换,有些时候在网上下载下来的视频,放在一些播放器里播放没有用,所以这就需要格式转换,相信MP4格式是大家最熟悉的视频格式了,这种格式在每个播放器里都可以播放的。当然不仅仅是视频,图片的格式也可以转换,所以说这个软件功能强大。
java后端指南
2021/05/13
1K0
任意格式相互转换
AnyMP4 Blu-ray Ripper Mac(蓝光视频格式转换器)
在寻找视频格式转换器吗?为您带来AnyMP4 Blu-ray Ripper for Mac蓝光视频格式转换器,它能够帮助用户将蓝光翻录为MOV,M4V,MP4,AVI,WMV,MKV,FLV,SWF,WebM等流行视频格式。除转换功能外,AnyMP4 Blu-ray Ripper mac还具有视频编辑功能。
Mac知识分享
2022/08/29
3.6K0
Infuse for Mac(强大的mac视频播放器)
Infuse for Mac是一款强大的mac视频播放器,可以在iPhone、iPad、Apple TV 和 Mac 上观看几乎任何视频格式的美妙方式。无需转换文件!Infuse 针对 macOS 11 进行了优化,具有强大的流媒体选项、Trakt 同步以及无与伦比的 AirPlay 和字幕支持。华丽的界面。精确控制。和如丝般流畅的播放。
Mac软件分享
2022/08/09
1.4K0
Infuse for Mac(强大的mac视频播放器)
相关推荐
音频格式的汇总及压缩比较
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档