JSON decoding in Go

1. 忽略某个字段
go 复制代码
type Person struct {
 Name   string `json:"name"` // 指定json序列化/反序列化时使用小写name
 Age    int64
 Weight float64 `json:"-"` // 指定json序列化/反序列化时忽略此字段
}
2. 忽略空值字段
go 复制代码
// 在tag中添加omitempty忽略空值
// 注意这里hobby,omitempty合起来是json tag值,中间用英文逗号分隔
type Person struct {
 Name   string `json:"name"` 
 Age    int64
 Weight float64 `json:"hobby,omitempty"` 
}
3. 嵌套匿名结构体

匿名嵌套 Login 时序列化后的 json 串为单层

go 复制代码
type User struct {
 Name  string `json:"name"`
 Email string `json:"email,omitempty"`
 Login
}

type Login struct {
 Account  string `json:"account"`
 Password string `json:"password"`
}

func main() {
 u := User{
  Name: "小王子",
 }
 b, err := json.Marshal(u)
 if err != nil {
  fmt.Printf("json.Marshal failed, err:%v\n", err)
  return
 }
 fmt.Printf("%s\n", b)  // {"name":"小王子","account":"","password":""}
}

想要变成嵌套的 json 串,需要改为具名嵌套或定义字段 tag:

lua 复制代码
type User struct {
 Name  string `json:"name"`
 Email string `json:"email,omitempty"`
 Login `json:"login"`
}
type Login struct {
 Account  string `json:"account"`
 Password string `json:"password"`
}

// {"name":"小王子","login":{"account":"","password":""}}

想要在嵌套的结构体为空值时,忽略该字段,仅添加 omitempty 是不够的:

lua 复制代码
type User struct {
 Name  string `json:"name"`
 Email string `json:"email,omitempty"`
 Login `json:"login,omitempty"`
}
// {"name":"小王子","login":{"account":"","password":""}}

还需要使用嵌套的结构体指针:

go 复制代码
type User struct {
 Name   string `json:"name"`
 Email  string `json:"email,omitempty"`
 *Login `json:"login,omitempty"`
}
// {"name":"小王子"}
4. 不修改原结构体忽略某个字段
go 复制代码
type User struct {
 Name  string `json:"name"`
 Email string `json:"email"`
}

type UserOmitEmail struct {
 *User
 Email *struct{} `json:"email,omitempty"`
}

func main() {
 u := User{
  Name:  "小王子",
  Email: "APTX4869",
 }

 u2 := UserOmitEmail{User: &u}

 b, err := json.Marshal(u2)
 if err != nil {
  fmt.Printf("json.Marshal failed, err:%v\n", err)
  return
 }
 fmt.Printf("%s\n", b) // {"name":"小王子"}
}
5. 处理字符串格式的数字

有时候,前端在传递来的 json 数据中可能会使用字符串类型的数字,这个时候可以在结构体 tag 中添加 string 来告诉 json 包从字符串中解析相应字段的数据:

go 复制代码
type Card struct {
 ID    int64   `json:"id,string"`    // 添加string tag
 Scope float64 `json:"scope,string"` // 添加string tag
}

func main() {
 jsonStr := `{"id": "1234567","scope": "88.50"}`
 var c Card
 if err := json.Unmarshal([]byte(jsonStr), &c); err != nil {
  fmt.Printf("json.Unmarsha jsonStr failed, err:%v\n", err)
  return
 }
 fmt.Printf("%+v\n", c) // {ID:1234567 Scope:88.5}
}
6. 整型变浮点型

将 JSON 中的数字解码为 interface 类型 to unmarshal JSON into an interface value 之后会成为 float64 类型。

go 复制代码
func main() {
 var data = []byte(`{"status": 200}`)
 var result map[string]interface{}
 if err := json.Unmarshal(data, &result); err != nil {
  log.Fatalln(err)
 }
 fmt.Printf("%T\n", result["status"]) // float64
}

因为使用的类型是空接口:interface{},在 json 包有解释,interface{} 在 json unmarshal 时,如果 json 数据为 number 类型,则会使用 float64

arduino 复制代码
// To unmarshal JSON into an interface value,
// Unmarshal stores one of these in the interface value:
//
// bool, for JSON booleans
// float64, for JSON numbers
// string, for JSON strings
// []interface{}, for JSON arrays
// map[string]interface{}, for JSON objects
// nil for JSON null
7. 使用匿名结构体添加字段

使用内嵌结构体能够扩展结构体的字段,但有时候我们没有必要单独定义新的结构体,可以使用匿名结构体简化操作:

go 复制代码
type User struct {
 Name  string `json:"name"`
 Email string `json:"email,omitempty"`
}

func main() {
 u := User{
  Name: "小王子",
 }
 b, err := json.Marshal(struct {
  *User
  Account string `json:"account"`
 }{
  &u,
  "12138",
 })
 if err != nil {
  fmt.Printf("json.Marshal failed, err:%v\n", err)
  return
 }
 fmt.Printf("%s\n", b) // {"name":"小王子","account":"12138"}
}
8. 可以使用 struct 将数值类型映射为 json.RawMessage 原生数据类型
css 复制代码
// 状态名称可能是 int 也可能是 string,指定为 json.RawMessage 类型
func main() {
 records := [][]byte{
  []byte(`{"status":200, "tag":"one"}`),
  []byte(`{"status":"ok", "tag":"two"}`),
 }

 for idx, record := range records {
  var result struct {
   StatusCode uint64
   StatusName string
   Status     json.RawMessage `json:"status"`
   Tag        string          `json:"tag"`
  }

  if err := json.NewDecoder(bytes.NewReader(record)).Decode(&result); err != nil{
   log.Fatal(err)
  }

  var name string
  err := json.Unmarshal(result.Status, &name)
  if err == nil {
   result.StatusName = name
  }

  var code uint64
  err = json.Unmarshal(result.Status, &code)
  if err == nil {
   result.StatusCode = code
  }

  fmt.Printf("[%v] result => %+v\n", idx, result)
 }
}
9. omitempty 忽略空值,但是已经赋值为0值情况下不忽略,需传该字段指针类型
css 复制代码
type Student struct {
 Name string `json:"name"`
 Age  *int   `json:"age,omitempty"`
}

func main() {
 stu1 := Student{
  Name: "Tom",
 }

 a := 0
 stu2 := Student{
  Name: "LiLy",
  Age:  &a,
 }

 b1, _ := json.Marshal(&stu1)
 b2, _ := json.Marshal(&stu2)

 fmt.Println("stu1:", string(b1))
 fmt.Println("stu2:", string(b2))
}

// stu1: {"name":"Tom"}
// stu2: {"name":"LiLy","age":0}
10. json marshal json string 时的转义问题
go 复制代码
func main() {
 s, err := marshalResponse(0, "ok", `{"name": "tony", "city": "shenyang"}`)
 if err != nil {
  fmt.Println(err)
  return
 }
 fmt.Println(s)
}

func marshalResponse(code int, msg string, result interface{}) (string, error) {
 m := map[string]interface{}{
  "code":   code,
  "msg":    msg,
  "result": result,
 }

 b, err := json.Marshal(&m)
 if err != nil {
  return "", err
 }

 return string(b), nil
}

输出:

ruby 复制代码
{"code":0,"msg":"ok","result":"{"name": "tony", "city": "shenyang"}"}

输出带有转义反斜线,怎么解决掉这个问题呢?json 提供了一种 RawMessage 类型,本质上就是 []byte,我们将json string 转换成 RawMessage 后再传给 json.Marshal 就可以解决掉这个问题了。

go 复制代码
func main() {
 s, err := marshalResponse(0, "ok", `{"name": "tony", "city": "shenyang"}`)
 if err != nil {
  fmt.Println(err)
  return
 }
 fmt.Println(s)
}

func marshalResponse(code int, msg string, result interface{}) (string, error) {
 m := map[string]interface{}{
  "code": code,
  "msg":  msg,
 }

 s, ok := result.(string)
 if ok {
  rawData := json.RawMessage(s)
  m["result"] = rawData
 } else {
  m["result"] = result
 }

 b, err := json.Marshal(&m)
 if err != nil {
  return "", err
 }

 return string(b), nil
}

输出:

css 复制代码
{"code":0,"msg":"ok","result":{"name":"tony","city":"shenyang"}}
相关推荐
Go_error3 小时前
Go 变长参数函数
go
爱分享的阿Q3 小时前
技术饱和度视角下的编程语言选择:一场关于供需博弈的深度思考
java·python·go
tyung1 天前
一个 main.go 搞定协作白板:你画一笔,全世界都看见
后端·go
ZHENGZJM1 天前
架构总览:Monorepo 结构与容器化部署
架构·go·react·全栈开发
我叫黑大帅1 天前
如何设计应用层 ACK 来补充 TCP 的不足?
后端·面试·go
ZHENGZJM1 天前
认证增强:图形验证码、邮箱验证与账户安全
安全·react.js·go·gin
人间打气筒(Ada)2 天前
「码动四季·开源同行」go语言:如何使用 ELK 进行日志采集以及统一处理?
开发语言·分布式·elk·go·日志收集·分布式日志系统
王码码20355 天前
Go语言中的数据库操作:从sqlx到ORM
后端·golang·go·接口
小羊在睡觉5 天前
Go与MySQL锁:高并发开发实战指南
数据库·后端·mysql·go