0%

Go Struct 不指定 JSON tag 时的默认规则

Golang 在序列化和反序列化一个 Struct 时,如果指定了 JSON tag 会严格按照指定的 tag 内容来执行,在没有指定 tag 或 tag 大小写不精准时,会有一些默认规则。

序列化

序列化的情况比较简单:

  • 指定了 tag 的可导出字段,按照 tag 的命名进行序列化
  • 没有指定 tag 的但可以导出的字段(首字母大写)会完全按照变量命名来进行序列化
1
2
3
4
5
6
7
8
9
10
11
12
type A struct {
Case int
casE int
Cas_E int
CaSE int `json:"ok"`
}

func main() {
a := A{1, 2, 3, 4}
s, _ := json.Marshal(&a)
mt.Println(string(s))
}

上边这段代码输出:

1
{"Case":1,"Cas_E":3,"ok":4}
  • casE 这个字段没有输出,原因是因为他是个不可导出的私有字段,即使设置了 tag 也不可序列化。
  • CaSE 序列话后的 key 为 ok 是因为我们给它指定了 tag
  • 其余字段都是按照我们原本的拼写格式进行的输出

反序列化

序列化的情况稍微有点复杂,其整体的优先级为:

  • 先按 tag 匹配,后按字段名匹配
  • 有 tag 的仅匹配 tag,没有tag 的可参与字段名匹配
  • 先精确匹配,后模糊匹配
  • 多个模糊匹配的按照声明在前的匹配

我们看几个例子:

情况1,带 tag 的两个字段都无法匹配上(精准匹配+模糊匹配),不带 tag 的两个字段都可以模糊匹配上,优先赋值给前边声明的字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type B struct {
Case int `json:"a"`
CaSE int `json:"b"`
CasE int
CaSe int
}

func main() {
s := []byte(`{"CAsE":2}`)
var b B
json.Unmarshal(s, &b)
fmt.Printf("%#v\\n", b)
}
// 输出:main.B{Case:0, CaSE:0, CasE:2, CaSe:0}

情况2,带 tag 的其中一个字段可以模糊匹配上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type B struct {
Case int `json:"case"`
CaSE int `json:"b"`
CasE int
CaSe int
}

func main() {
s := []byte(`{"CAsE":2}`)
var b B
json.Unmarshal(s, &b)
fmt.Printf("%#v\\n", b)
}
// 输出:main.B{Case:2, CaSE:0, CasE:0, CaSe:0}

情况3,带 tag 的两个字段都可以匹配上,第一个模糊匹配,第二个精准匹配: