通常struct的json tag要求与要解析json数据对应的key完全一致,如果只是大小写不一致会发生什么呢?
以一个例子来说明问题:
package main
import (
"encoding/json"
"fmt"
)
type Example struct {
ID int `json:"id"`
Name string `json:"NAME"`
}
type Example2 struct {
ID int `json:"id"`
Name string `json:"name"`
Name2 string `json:"NAME"`
}
type Example3 struct {
ID int `json:"id"`
Name2 string `json:"NAME"`
Name string `json:"name"`
}
func main() {
var example Example
json.Unmarshal([]byte(`{"id":1,"NAMe":"n2","name":"n1"}`), &example)
fmt.Println(example)
var example2 Example2
json.Unmarshal([]byte(`{"id":1,"NAMe":"n2","name":"n1"}`), &example2)
fmt.Println(example2)
var example3 Example3
json.Unmarshal([]byte(`{"id":1,"NAMe":"n2","name":"n1"}`), &example3)
fmt.Println(example3)
}
这个例子结果输出如下:
{1 n1}
{1 n1 }
{1 n2 n1}
为何会出现这样的结果?
我们先从源码找答案。
更多内容分享,欢迎关注公众号:Go开发笔记
json unmarshal关于object解析位于decode.go文件中的object func.
type field struct {
name string
nameBytes []byte // []byte(name)
equalFold func(s, t []byte) bool // bytes.EqualFold or equivalent
...
}
func (d *decodeState) object(v reflect.Value) error {
...
var f *field
if i, ok := fields.nameIndex[string(key)]; ok {
// Found an exact name match.
f = &fields.list[i]
} else {
// Fall back to the expensive case-insensitive
// linear search.
for i := range fields.list {
ff := &fields.list[i]
if ff.equalFold(ff.nameBytes, key) {
f = ff
break
}
}
}
...
}
fields.nameIndex
类型是map[string]int
,存储了struct filed的tag及对应的index,解析json时,会先查找json中的key是否存在于fields.nameIndex
中,如果存在则获取filed并设置其值;如果不存在,则会依照struct filed的顺序依次比较json中的key,然后获取filed并赋值。
equalFold调用的是bytes.EqualFold
// EqualFold reports whether s and t, interpreted as UTF-8 strings,
// are equal under Unicode case-folding, which is a more general
// form of case-insensitivity.
func EqualFold(s, t []byte) bool {
for len(s) != 0 && len(t) != 0 {
// Extract first rune from each.
var sr, tr rune
if s[0] < utf8.RuneSelf {
sr, s = rune(s[0]), s[1:]
} else {
r, size := utf8.DecodeRune(s)
sr, s = r, s[size:]
}
if t[0] < utf8.RuneSelf {
tr, t = rune(t[0]), t[1:]
} else {
r, size := utf8.DecodeRune(t)
tr, t = r, t[size:]
}
// If they match, keep going; if not, return false.
// Easy case.
if tr == sr {
continue
}
// Make sr < tr to simplify what follows.
if tr < sr {
tr, sr = sr, tr
}
// Fast check for ASCII.
if tr < utf8.RuneSelf {
// ASCII only, sr/tr must be upper/lower case
if 'A' <= sr && sr <= 'Z' && tr == sr+'a'-'A' {
continue
}
return false
}
// General case. SimpleFold(x) returns the next equivalent rune > x
// or wraps around to smaller values.
r := unicode.SimpleFold(sr)
for r != sr && r < tr {
r = unicode.SimpleFold(r)
}
if r == tr {
continue
}
return false
}
// One string is empty. Are both?
return len(s) == len(t)
}
EqualFold的比较原理是:
(1)依次比较两个[]byte的对应位置上的每个rune,
(2)先直接比较rune,相同则继续,不相同则判断是否忽略大小写时是否一致,相同则继续,否则返回false。
即EqualFold在比较时会忽略大小写。
所以当json中的key不存在于fields.nameIndex
中时,会依照struct filed的顺序忽略大小写依次进行比较,如果此时一致,则会获取对应的filed,然后赋值。
整体来说,json解析时,会按照如下规则顺序处理:
按照以上规则,可以回答文章开头的结果:
因此解析结果为{1 n1}
没有key解析至Name2,Name2值为空字符串,因此解析结果为{1 n1 }
因此解析结果为{1 n2 n1}
至此,你对json tag解析时的匹配规则了解了吗?
更多内容分享,欢迎关注公众号:Go开发笔记