文章目录
一、序列化
我们经常需要把 Json Object 反序列化到 struct,然而官方库总有各种限制。
为了灵活我们可以用 json.Marshaler
实现,但他增加了复杂性(要写很多代码)、而且要避免申请太多内存。
1.1 用 *time.Time 避免序列化得到null
数据库的 create_time、update_time 通常建议用 *time.Time 类型,而不用 time.Time 类型
值方式:
func main() {
type tm struct {
T1 time.Time
T2 time.Time
}
now := time.Now()
t := &tm{
T1: now,
}
s, _ := json.Marshal(t)
print(string(s))
signal.WaitForExit()
}
// 结果如下:
{"T1":"2022-10-11T17:15:10.11307+08:00","T2":null} // 这样通常前端可以解析,因为js的也有null的类型
指针方式:
func main() {
type tm struct {
T1 *time.Time
T2 *time.Time
}
now := time.Now()
t := &tm{
T1: &now,
}
s, _ := json.Marshal(t)
print(string(s))
signal.WaitForExit()
}
// 结果如下:
{"T1":"2022-10-11T17:15:10.11307+08:00","T2":"0001-01-01T00:00:00Z"} // 这样通常如果希望前端 hard code 用 "0001-01-01T00:00:00Z" 来判断是够为空时间,就太苟了
1.2 jsoniter 字符串和数字互转
golang 的 jsoniter 容忍字符串和数字互为转换,通常用雪花算法做数据库的主键,但前端的 js 语言层面的 Number 类型无法解析(雪花算法的 id 超出了 number 类型的值域),这就造成了矛盾:前端 js 只能用 String 解析,而后端需要用 BigInt 存数据库。
为了解决此问题,前端和数据库之间的 web后端,需要对 golang 的 string 和 int64 做转换,可通过 jsoniter 库实现此功能,就是在属性添加 json:",string"
的标签即可,代码如下:
class model struct {
ID int64 `json:",string"`
Name string `json:"name"`
}
1.3 json 只解析需要的字段
json 解析,会只解析需要的字段。例如下例 struct S 只有 2个字段,b1 有2个字段,而 b2 有3个字段,但 b1 和 b2 都可以 Unmarshal 成功:
package main
import (
"github.com/datager/codes/gocodes/dg/utils/json"
"github.com/datager/codes/gocodes/dg/utils/signal"
)
type S struct {
A string
B int64
}
func main() {
b1 := `{"A":"1", "B":2}`
b2 := `{"A":"1", "B":2, "C": 3}`
v1 := &S{}
v2 := &S{}
if err := json.Unmarshal([]byte(b1), v1); err != nil {
panic(err)
}
if err := json.Unmarshal([]byte(b2), v2); err != nil {
panic(err)
}
signal.WaitForExit()
}
二、merge 两个 json
Go语言中合并两个JSON对象的一种常用方法是:将它们都解码为Go语言的map[string]interface{}类型,然后遍历后一个map并将它的每个键/值对添加到前一个map中。
最终,合并后的JSON字节数组将被返回。如果在解码或编码过程中出现错误,则返回相应的错误。
// base from bx, merge by to it
// if exist same key, overwrite value
// if not exist same key, add new value
func mergeJSON(bx, by []byte) ([]byte, error) {
mx, my := make(map[string]any), make(map[string]any)
if err := json.Unmarshal(bx, &mx); err != nil {
return nil, err
}
if err := json.Unmarshal(by, &my); err != nil {
return nil, err
}
for k, v := range my {
mx[k] = v
}
return json.MarshalIndent(mx, "", "\t")
}
三、常见库
3.1 gjson、sjson
sjson.Set(str, k, v)
v := gjson.Get(str, k)
3.2 viper
viper 为了支持环境变量,内部全部使用小写风格,使得生成的 json 文件也是小写风格的,容易造成不兼容。(虽然 viper 读取、使用、写入都是不区分大小写的)