golang struct json tag的使用及深入理解_go struct json tag

29,988次阅读
没有评论

共计 7300 个字符,预计需要花费 19 分钟才能阅读完成。

Field int json:"-," //“-”:0

此项与忽略的区别在于多了个”,“。

(7)“string”opt

以上提到的用法都是常见的,这个比较特殊。

“string” 仅适用于字符串、浮点、整数或布尔类型,表示的意思是:将字段的值转换为字符串;解析时,则是将字符串解析为指定的类型。主要用于与 javascript 通信时数据的转换。

注意:
仅且仅有 ”string”,没有 int、number 之类的 opt。即带 ”string” opt 的字段,编码时仅能将字符串、浮点、整数或布尔类型转换为 string 类型,反之则不然;解码时可以将 string 转换为其他类型,反之不然。因为 ”string” 有限制。

Int64String int64 json:",string" //“Int64String”:“0”

“string”opt 的使用可以在 Marshal/Unmarshal 时自动进行数据类型的转换,减少了手动数据转换的麻烦,但是一定要注意使用的范围,对不满足的类型使用,是会报错的。

猜下对 string 使用 ”string” opt 的结果会是如何呢?

Int64String string json:",string"

我们在了解源码后解答。

二、源码角度的设计处理过程

一切的使用方式肯定在设计时就已限定,我们现在看看源码中的处理过程。
在看实现的过程中,可以思考下使用的方式对不对,还有要注意的地方吗?
对某些地方非常好的实现思路,我们也可以借鉴下,对以后的编程学习大有裨益。

此处为了简洁,具体调用过程略过不讲,直接查看核心代码部分,有兴趣的话,可以查看下完整过程。

1.typeFields

在 typeFields 中详细的对上面提到的各种用法的 tag 做了处理,处理后的数据存入 fileds,最后在进行编码。

// typeFields returns a list of fields that JSON should recognize for the given type.
// The algorithm is breadth-first search over the set of structs to include – the top struct
// and then any reachable anonymous structs.
func typeFields(t reflect.Type) structFields {
// Anonymous fields to explore at the current level and the next.
current := []field{}
next := []field{{typ: t}}

// Count of queued names for current level and the next.
var count, nextCount map[reflect.Type]int

// Types already visited at an earlier level.
visited := map[reflect.Type]bool{}

// Fields found.
var fields []field

// Buffer to run HTMLEscape on field names.
var nameEscBuf bytes.Buffer

for len(next) > 0 {
current, next = next, current[:0]
count, nextCount = nextCount, map[reflect.Type]int{}

for _, f := range current {
if visited[f.typ] {// 已处理的过类型跳过
continue
}
visited[f.typ] = true

// Scan f.typ for fields to include.
for i := 0; i sf := f.typ.Field(i)
isUnexported := sf.PkgPath !=“”
if sf.Anonymous {// 内嵌类型的处理
t := sf.Type
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if isUnexported && t.Kind() != reflect.Struct {
// Ignore embedded fields of unexported non-struct types.
continue// 非 struct 结构的不能导出的 key 直接跳过
}
// Do not ignore embedded fields of unexported struct types
// since they may have exported fields.
} else if isUnexported {
// Ignore unexported non-embedded fields.
continue// 不能导出的 key 直接跳过
}
tag := sf.Tag.Get(“json”)
if tag ==“-”{
continue//tag 为 ”-“ 直接跳过
}
name, opts := parseTag(tag)
if !isValidTag(name) {
name =“”// 包含特殊字符的无效 name
}
index := make([]int, len(f.index)+1)
copy(index, f.index)
index[len(f.index)] = i

ft := sf.Type
if ft.Name() ==“”&& ft.Kind() == reflect.Ptr {
// Follow pointer.
ft = ft.Elem()
}

// Only strings, floats, integers, and booleans can be quoted.
quoted := false
if opts.Contains(“string”) {// 此处为 ”string” opt 的特殊处理,支持的类型如下:
switch ft.Kind() {
case reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Float32, reflect.Float64,
reflect.String:
quoted = true
}
}

// Record found field and index sequence.
if name !=“”|| !sf.Anonymous || ft.Kind() != reflect.Struct {
tagged := name !=“”
if name ==“”{
name = sf.Name// 未指定或者指定 name 无效的使用原 field 的 name
}
field := field{
name: name,
tag: tagged,
index: index,
typ: ft,
omitEmpty: opts.Contains(“omitempty”),//omitempty 确认
quoted: quoted,// 是否支持 ”string” opt
}
field.nameBytes = []byte(field.name)
field.equalFold = foldFunc(field.nameBytes)

// Build nameEscHTML and nameNonEsc ahead of time.
// 两种格式的构建
nameEscBuf.Reset()
nameEscBuf.WriteString(")
HTMLEscape(&nameEscBuf, field.nameBytes)
nameEscBuf.WriteString(":)
field.nameEscHTML = nameEscBuf.String()
field.nameNonEsc = " + field.name + ":

fields = append(fields, field)// 存入 fields
if count[f.typ] > 1 {
// If there were multiple instances, add a second,
// so that the annihilation code will see a duplicate.
// It only cares about the distinction between 1 or 2,
// so don’t bother generating any more copies.
fields = append(fields, fields[len(fields)-1])
}
continue
}

// Record new anonymous struct to explore in next round.
nextCount[ft]++
if nextCount[ft] == 1 {
next = append(next, field{name: ft.Name(), index: index, typ: ft})
}
}
}
}

for i := range fields {
f := &fields[i]
f.encoder = typeEncoder(typeByIndex(t, f.index))// 设置 fields 的 encoder
}
nameIndex := make(map[string]int, len(fields))
for i, field := range fields {
nameIndex[field.name] = i
}
return structFields{fields, nameIndex}
}

2.encode

func newStructEncoder(t reflect.Type) encoderFunc {
se := structEncoder{fields: cachedTypeFields(t)}
return se.encode
}

func (se structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
next := byte(‘{’)
FieldLoop:
for i := range se.fields.list {
f := &se.fields.list[i]

// Find the nested struct field by following f.index.
fv := v
for _, i := range f.index {
if fv.Kind() == reflect.Ptr {
if fv.IsNil() {
continue FieldLoop
}
fv = fv.Elem()
}
fv = fv.Field(i)
}

if f.omitEmpty && isEmptyValue(fv) {//“omitempty” 的忽略处理,需要值为零值
continue
}
e.WriteByte(next)
next =‘,’
if opts.escapeHTML {
e.WriteString(f.nameEscHTML)
} else {
e.WriteString(f.nameNonEsc)
}
opts.quoted = f.quoted
f.encoder(e, fv, opts)// 根据具体类型的编码处理
}
if next ==‘{’{
e.WriteString(”{}”)
} else {
e.WriteByte(‘}’)
}
}

以下以 int 类型 intEncoder 为例:

func intEncoder(e *encodeState, v reflect.Value, opts encOpts) {
b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
if opts.quoted {// 带有 ”string” opt 添加引号
e.WriteByte(‘”’)
}
e.Write(b)
if opts.quoted {
e.WriteByte(‘”’)
}
}

对于数字类型,如果带有 **“string”** 则在写入正式值前后添加引号。

对于字符串类型,如果带有 **“string”**,原 string 值再编码时会添加引号,再对结果添加引号,则格式异常,因此需要先对原值进行编码。

func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {
if v.Type() == numberType {
numStr := v.String()
// In Go1.5 the empty string encodes to“0”, while this is not a valid number literal
// we keep compatibility so check validity after this.
if numStr ==“”{
numStr =“0”// Number’s zero-val
}
if !isValidNumber(numStr) {
e.error(fmt.Errorf(“json: invalid number literal %q”, numStr))
}
e.WriteString(numStr)
return
}
if opts.quoted {
sb, err := Marshal(v.String())// 注意此处处理
if err != nil {
e.error(err)
}
e.string(string(sb), opts.escapeHTML)
} else {
e.string(v.String(), opts.escapeHTML)
}
}

func (e *encodeState) string(s string, escapeHTML bool) {
e.WriteByte(‘”’)// 添加引号
start := 0
for i := 0; i if b := s[i]; b if htmlSafeSet[b] || (!escapeHTML && safeSet[b]) {
i++
continue
}
if start e.WriteString(s[start:i])
}
e.WriteByte(‘’)
switch b {
case‘’,‘”’:
e.WriteByte(b)
case‘n’:
e.WriteByte(‘n’)
case‘r’:
e.WriteByte(‘r’)
case‘t’:
e.WriteByte(‘t’)
default:
// This encodes bytes // If escapeHTML is set, it also escapes , and &
// because they can lead to security holes when
// user-controlled strings are rendered into JSON
// and served to some browsers.
e.WriteString(u00)
e.WriteByte(hex[b>>4])
e.WriteByte(hex[b&0xF])
}
i++
start = i
continue
}
c, size := utf8.DecodeRuneInString(s[i:])
if c == utf8.RuneError && size == 1 {
if start e.WriteString(s[start:i])
}
e.WriteString(ufffd)
i += size
start = i
continue
}
// U+2028 is LINE SEPARATOR.
// U+2029 is PARAGRAPH SEPARATOR.
// They are both technically valid characters in JSON strings,
// but don’t work in JSONP, which has to be evaluated as JavaScript,
// and can lead to security holes there. It is valid JSON to
// escape them, so we do so unconditionally.
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
if c ==‘u2028’|| c ==‘u2029’{
if start e.WriteString(s[start:i])
}

自我介绍一下,小编 13 年上海交大毕业,曾经在小公司待过,也去过华为、OPPO 等大厂,18 年进入阿里一直到现在。

深知大多数 Go 语言工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024 年 Go 语言全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
golang struct json tag 的使用及深入理解_go struct json tag
golang struct json tag 的使用及深入理解_go struct json tag
golang struct json tag 的使用及深入理解_go struct json tag
golang struct json tag 的使用及深入理解_go struct json tag
golang struct json tag 的使用及深入理解_go struct json tag

既有适合小白学习的零基础资料,也有适合 3 年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了 95% 以上 Golang 知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加 V 获取:vip1024b(备注 Go)
golang struct json tag 的使用及深入理解_go struct json tag

一个人可以走的很快,但一群人才能走的更远。不论你是正从事 IT 行业的老鸟或是对 IT 行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

[外链图片转存中…(img-xzdtyzHF-1712871989268)]

既有适合小白学习的零基础资料,也有适合 3 年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了 95% 以上 Golang 知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加 V 获取:vip1024b(备注 Go)
[外链图片转存中…(img-0AVMoMAF-1712871989268)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事 IT 行业的老鸟或是对 IT 行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

原文地址: golang struct json tag 的使用及深入理解_go struct json tag

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-09-30发表,共计7300字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)