Golang 的 unmarshal 踩坑指南

2024-05-16 03:36
文章标签 golang 指南 unmarshal

本文主要是介绍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 时如何区分空字段和未设置字段?

这篇关于Golang 的 unmarshal 踩坑指南的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/993761

相关文章

PostgreSQL中rank()窗口函数实用指南与示例

《PostgreSQL中rank()窗口函数实用指南与示例》在数据分析和数据库管理中,经常需要对数据进行排名操作,PostgreSQL提供了强大的窗口函数rank(),可以方便地对结果集中的行进行排名... 目录一、rank()函数简介二、基础示例:部门内员工薪资排名示例数据排名查询三、高级应用示例1. 每

SpringBoot结合Docker进行容器化处理指南

《SpringBoot结合Docker进行容器化处理指南》在当今快速发展的软件工程领域,SpringBoot和Docker已经成为现代Java开发者的必备工具,本文将深入讲解如何将一个SpringBo... 目录前言一、为什么选择 Spring Bootjavascript + docker1. 快速部署与

golang程序打包成脚本部署到Linux系统方式

《golang程序打包成脚本部署到Linux系统方式》Golang程序通过本地编译(设置GOOS为linux生成无后缀二进制文件),上传至Linux服务器后赋权执行,使用nohup命令实现后台运行,完... 目录本地编译golang程序上传Golang二进制文件到linux服务器总结本地编译Golang程序

创建Java keystore文件的完整指南及详细步骤

《创建Javakeystore文件的完整指南及详细步骤》本文详解Java中keystore的创建与配置,涵盖私钥管理、自签名与CA证书生成、SSL/TLS应用,强调安全存储及验证机制,确保通信加密和... 目录1. 秘密键(私钥)的理解与管理私钥的定义与重要性私钥的管理策略私钥的生成与存储2. 证书的创建与

Python包管理工具pip的升级指南

《Python包管理工具pip的升级指南》本文全面探讨Python包管理工具pip的升级策略,从基础升级方法到高级技巧,涵盖不同操作系统环境下的最佳实践,我们将深入分析pip的工作原理,介绍多种升级方... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

PowerShell中15个提升运维效率关键命令实战指南

《PowerShell中15个提升运维效率关键命令实战指南》作为网络安全专业人员的必备技能,PowerShell在系统管理、日志分析、威胁检测和自动化响应方面展现出强大能力,下面我们就来看看15个提升... 目录一、PowerShell在网络安全中的战略价值二、网络安全关键场景命令实战1. 系统安全基线核查

Java操作Word文档的全面指南

《Java操作Word文档的全面指南》在Java开发中,操作Word文档是常见的业务需求,广泛应用于合同生成、报表输出、通知发布、法律文书生成、病历模板填写等场景,本文将全面介绍Java操作Word文... 目录简介段落页头与页脚页码表格图片批注文本框目录图表简介Word编程最重要的类是org.apach

golang版本升级如何实现

《golang版本升级如何实现》:本文主要介绍golang版本升级如何实现问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录golanwww.chinasem.cng版本升级linux上golang版本升级删除golang旧版本安装golang最新版本总结gola

golang中reflect包的常用方法

《golang中reflect包的常用方法》Go反射reflect包提供类型和值方法,用于获取类型信息、访问字段、调用方法等,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值... 目录reflect包方法总结类型 (Type) 方法值 (Value) 方法reflect包方法总结

Python设置Cookie永不超时的详细指南

《Python设置Cookie永不超时的详细指南》Cookie是一种存储在用户浏览器中的小型数据片段,用于记录用户的登录状态、偏好设置等信息,下面小编就来和大家详细讲讲Python如何设置Cookie... 目录一、Cookie的作用与重要性二、Cookie过期的原因三、实现Cookie永不超时的方法(一)