当前位置: 首页 > news >正文

Golang 的 unmarshal 踩坑指南

文章目录

    • 1. 写在最前面
    • 2. 字段区分出空字段还是未设置字段
      • 2.1 问题描述
      • 2.2 解决
    • 3. 字段支持多种类型 & 按需做不同类型处理
      • 3.1 问题描述
      • 3.2 解决
    • 4. 碎碎念
    • 5. 参考资料

1. 写在最前面

笔者最近在实现将内部通知系统的数据定义转化为产品定义的对外提供的数据结构。

  • 举例说明内部系统数据定义返回的结构定义有如下几种:

    • {"magName": "uploader", "status": 0}

    • {"magName": "uploaded", "fileList": ["testa", "testb"]}

    • {"magName": "upload_start", "fileList": "test"}

    • {"magName": "uploading", "progress": 1000}

在将如上数据结构转换的时候有两种思路

  • 内部通知系统的数据结构 1:1 跟产品外部定义结构进行映射

  • 内部通知系统的数据结构 n:1 跟产品外部定义的结构进行映射

在进一步拆解问题,在做 n:1 映射的时候,需要解决的是如何定义一个通用的产品定义结构,能够按需根据产品定义进行进行映射。该结构需要解决如下两件事情:

  • fileList 字段:可以设置两种不同类型,并且支持对改不同类型的返回进行补充

  • status 字段:可以区分出空字段还是未设置字段

2. 字段区分出空字段还是未设置字段

2.1 问题描述

在配置了 omitempty tag 的字段设置的值为默认值时,对该字段做 marshal 的时候,该字段会被忽略。

例子:

package mainimport ("encoding/json""fmt"
)type Message struct {Status  int    `json:"status,omitempty"`MsgName string `json:"msgName,omitempty"`
}func main() {//1. status 为默认值 0 时,做 unmarshal 可以解析出该字段test := `{"status": 0, "msgName": "hello"}`var m Messageerr := json.Unmarshal([]byte(test), &m)if err != nil {fmt.Println(err)}fmt.Printf("%v\n", m)//2. status 为默认值 0 时,做 marshal 时不会输出该字段data, err := json.Marshal(m)if err != nil {fmt.Println(err)}fmt.Printf("%s\n", data)
}

输出:

$> go run main.go
unmarshal: {0 hello}
marshal: {"msgName":"hello"}

2.2 解决

将可能为默认值的字段设置为指针类型,比如 int 设置为 *int

package mainimport ("encoding/json""fmt"
)type Message struct {Status  *int   `json:"status,omitempty"`MsgName string `json:"msgName,omitempty"`
}func main() {//1. status 为默认值 0 时,做 unmarshal 可以解析出该字段test := `{"status": 0, "msgName": "hello"}`var m Messageerr := json.Unmarshal([]byte(test), &m)if err != nil {fmt.Println(err)}fmt.Printf("unmarshal: %v\n", m)//2. status 为默认值 0 时,做 marshal 会输出该字段data, err := json.Marshal(m)if err != nil {fmt.Println(err)}fmt.Printf("marshal: %s\n", data)
}

输出:

$> go run main.go
unmarshal: {0x14000110108 hello}
marshal: {"status":0,"msgName":"hello"}

注:设置了指针类型的字段,如果原始的字段不存在时,则结构体字段为空(nil)

3. 字段支持多种类型 & 按需做不同类型处理

3.1 问题描述

某个指定字段支持配置两种类型,同时需要不同类型做产品指定的加工处理。

package mainimport ("encoding/json""fmt"
)type Message struct {Status   *int   `json:"status,omitempty"`MsgName  string `json:"msgName,omitempty"`FileList any    `json:"fileList,omitempty"`
}func fixFileList(fileList any) any {switch fileList.(type) {case string:fileList = fmt.Sprintf("string type [%s]", fileList.(string))return fileListfmt.Printf("fileList: %v\n", fileList)case []interface{}:fileListArr := fileList.([]interface{})for i, _ := range fileListArr {fileListArr[i] = fmt.Sprintf("array type [%s]", fileListArr[i])}return fileListArr}return "unknown"
}func main() {//1. FileList 字段为 string 类型test := `{"status": 0, "msgName": "hello", "fileList": "world"}`var m Messageerr := json.Unmarshal([]byte(test), &m)if err != nil {fmt.Println(err)}m.FileList = fixFileList(m.FileList)fmt.Printf("unmarshal 1: %v\n", m)//2. FileList 字段为 array 类型test = `{"status": 0, "msgName": "hello", "fileList": ["world", "world2"]}`err = json.Unmarshal([]byte(test), &m)if err != nil {fmt.Println(err)}m.FileList = fixFileList(m.FileList)fmt.Printf("unmarshal 2: %v\n", m)}

输出:

$> go run main.go
unmarshal 1: {0x1400000e220 hello string type [world]}
unmarshal 2: {0x1400000e220 hello [array type [world] array type [world2]]}

3.2 解决

解决方案分为两个步骤:

  • 将该字段设置为 any 或者 insterface 类型

  • 然后在根据断言的类型不同做不同的产品展示结果补充

4. 碎碎念

以上就是关于某次处理脏业务逻辑的包装记录,本来还打算把晚上学到的 json inline 用法记录说明一下,但是由于本人还没有实践过,那就后面用的时候在继续记录吧。

  • 18岁很好,28岁也不错,38岁可能会更好,只要皱纹不长进心里,我们就永远风华正茂。

  • 一个女人最重要的能力,不是你把自己打扮得多么漂亮,也不是你挣钱有多厉害,而是无论发生任何事情,你都有快乐起来的能力。

  • 当你越来越优秀时,你开始明白,其实每个人都没有好坏之分,没有对错,只有频率不同,做出了不同的选择。有个好心态,路就会走的更宽。

5. 参考资料

  • Golang 的 “omitempty” 关键字略解

  • Golang 中使用 JSON 时如何区分空字段和未设置字段?

http://www.lryc.cn/news/350121.html

相关文章:

  • Linux的常用指令 和 基础知识穿插巩固(巩固知识必看)
  • MP3解码入门(基于libhelix)
  • Oracle 中索引与完整性(SQL)
  • 【Linux深度学习笔记5.13(Apache)】
  • 汇编语言入门:探索 x86 架构
  • [ffmpeg处理指令]
  • 测试之路 - 精准而优雅
  • Java基础篇常见面试问题总结
  • Spring、SpringMVC
  • 【传知代码】VRT: 关于视频修复的模型(论文复现)
  • 不用投稿邮箱,怎样向各大新闻媒体投稿?
  • NAT技术总结与双向NAT配置案例
  • mysql的explain
  • SpringBoot+Vue实现图片滑块和文字点击验证码
  • 每日复盘-20240515
  • 【Android】Apk图标的提取、相同目录下相同包名提取的不同图标apk但是提取结果相同的bug解决
  • 高校普法|基于SSM+vue的高校普法系统的设计与实现(源码+数据库+文档)
  • pytest教程-47-钩子函数-pytest_sessionfinish
  • 如何使用Python下载哔哩哔哩(Bilibili)视频字幕
  • IP代理网络协议介绍
  • 渗透相关面试+流量分析
  • Shell之高效文本处理命令
  • u3d的ab文件注意事项
  • Go微服务开源框架kratos的依赖注入关系总结
  • Linux 第三十二章
  • 手机号码的正则表达式
  • 机器学习入门介绍
  • 一文说通用户故事点数是什么?
  • GAME101-Lecture07学习
  • 【一步一步了解Java系列】:了解Java与C语言的运算符的“大同小异”