Go-json源码解析

发布时间 2023-04-07 10:46:35作者: 望权栈

代码例子如下:

type Student struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func main() {
    stu := Student{
        Name: "张三",
        Age:  21,
    }
    buf := bytes.NewBuffer(make([]byte, 0))  // 新建一个缓冲区,用于存放编码过的内容
    enc := json.NewEncoder(buf)              // 实例化一个编码器
    if err := enc.Encode(&stu); err != nil { // 编码 Student 结构体
        log.Fatal(err)
    }
    p, _ := ioutil.ReadAll(buf) // 实际上为 io.ReadAll(r)
    fmt.Println(string(p))      // 输出缓冲区内容

    buf.Reset()                 // 将缓冲区清空
    buf.Write(p)                // 写入json数据
    dec := json.NewDecoder(buf) // 实例化一个解码器
    var stu2 Student = Student{}
    if err := dec.Decode(&stu2); err != nil { // 开始解码
        log.Fatal(err)
    }
    fmt.Println(stu2)
}

这里我们可以发现,编码过程与解码过程极其相似,故我们拿编码进行讨论(解码相似,就不再过多重复讨论)。显而易见的是,json.NewEncoder(buf)该函数是实例化一个编码器,其中代码也极其简单: return &Encoder{w: w, escapeHTML: true}//返回一个Encoder结构体
接下来我们将要讨论enc.Encode(&stu)这个编码函数,首先我们来看一下Encoder的结构:

// An Encoder writes JSON values to an output stream.
type Encoder struct {
	w          io.Writer // 编码结果
	err        error     // 错误集
	escapeHTML bool      // escapeHTML将< > & " '转成字符实体

	indentBuf    *bytes.Buffer // 内置buf
	indentPrefix string        // 前缀
	indentValue  string        // 前缀后值,一般为'\t'缩进
}

接下来我们来看看Encode这个函数的流程:

func (enc *Encoder) Encode(v any) error {
	if enc.err != nil { // 判断编码器是否存在错误
		return enc.err
	}
	e := newEncodeState()                                    // 创建一个 状态器,这里采用sync.Pool来实现
	err := e.marshal(v, encOpts{escapeHTML: enc.escapeHTML}) // 进行解析json
	if err != nil {
		return err
	}

	
	e.WriteByte('\n') // 末尾换行 执行体的是内置缓冲区

	b := e.Bytes()                                       // 将内置buffer赋值于b
	if enc.indentPrefix != "" || enc.indentValue != "" { // 如果前缀及值存在
		if enc.indentBuf == nil { // 如果内置缓冲区为空,则实例化一个缓冲区
			enc.indentBuf = new(bytes.Buffer)
		}
		enc.indentBuf.Reset()                                             // 重置缓冲区
		err = Indent(enc.indentBuf, b, enc.indentPrefix, enc.indentValue) // 调整json格式的缩进
		if err != nil {
			return err
		}
		b = enc.indentBuf.Bytes() // 重新取出enc内置缓冲区
	}
	if _, err = enc.w.Write(b); err != nil { // 将结果写入缓冲区
		enc.err = err
	}
	encodeStatePool.Put(e) // 将 状态器 交还给状态池
	return err
}

同样的,在json之中存在着json.Marshal(&stu)函数与上述函数完成功能相同,并且更加简便,其中的流程其实与上述没有太大区别(不过该解析函数之中没有了Indent处理缩进了)

func Marshal(v any) ([]byte, error) {
	e := newEncodeState() // 实例化一个 状态器

	err := e.marshal(v, encOpts{escapeHTML: true}) // 解析数据到json,关键函数
	if err != nil {
		return nil, err
	}
	buf := append([]byte(nil), e.Bytes()...)

	encodeStatePool.Put(e) // 归还 状态器

	return buf, nil
}

我们跟入到marshal函数:(最外层实际上仅仅是捕捉异常,进入的函数是e.reflectValue(reflect.ValueOf(v), opts)

func (e *encodeState) marshal(v any, opts encOpts) (err error) {
	defer func() { // 捕获异常
		if r := recover(); r != nil {
			if je, ok := r.(jsonError); ok {
				err = je.error
			} else {
				panic(r)
			}
		}
	}()
	e.reflectValue(reflect.ValueOf(v), opts)
	return nil
}

继续跟进查看:

func (e *encodeState) reflectValue(v reflect.Value, opts encOpts) {
	valueEncoder(v)(e, v, opts) // valueEncoder(v)返回一个函数指针,并执行f(e, v, opts)
}

func valueEncoder(v reflect.Value) encoderFunc { // 跟入查看返回值encoderFunc
	if !v.IsValid() {
		return invalidValueEncoder
	}
	return typeEncoder(v.Type()) // 关键函数typeEncoder(v.Type())
}

var encoderCache sync.Map // map[reflect.Type]encoderFunc
func typeEncoder(t reflect.Type) encoderFunc { // 利用sync.Map来实现查找对应的encode
	if fi, ok := encoderCache.Load(t); ok { // 如果存在直接返回
		return fi.(encoderFunc)
	}

	// To deal with recursive types, populate the map with an
	// indirect func before we build it. This type waits on the
	// real func (f) to be ready and then calls it. This indirect
	// func is only used for recursive types.
	var (
		wg sync.WaitGroup
		f  encoderFunc
	)
	wg.Add(1)
	fi, loaded := encoderCache.LoadOrStore(t, encoderFunc(func(e *encodeState, v reflect.Value, opts encOpts) { // 进行储存到sync.Map之中
		wg.Wait()
		f(e, v, opts)
	}))
	if loaded {
		return fi.(encoderFunc)
	}

	// Compute the real encoder and replace the indirect func with it.
	f = newTypeEncoder(t, true) // 关键函数,返回encoderFunc,实际上实现
	wg.Done()
	encoderCache.Store(t, f)
	return f
}

我们进行跟踪后发现实际上的关键函数为newTypeEncoder(t, true),同时该函数返回的函数指针如下(大体相同,根据不同的输入返回不同的处理函数)

func (pe ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
	if v.IsNil() {
		e.WriteString("null")
		return
	}
	if e.ptrLevel++; e.ptrLevel > startDetectingCyclesAfter {
		// We're a large number of nested ptrEncoder.encode calls deep;
		// start checking if we've run into a pointer cycle.
		ptr := v.Interface()
		if _, ok := e.ptrSeen[ptr]; ok {
			e.error(&UnsupportedValueError{v, fmt.Sprintf("encountered a cycle via %s", v.Type())})
		}
		e.ptrSeen[ptr] = struct{}{}
		defer delete(e.ptrSeen, ptr)
	}
	pe.elemEnc(e, v.Elem(), opts) // 解析json数据
	e.ptrLevel--
}

而该函数实际上也是一层封装,具体执行函数体是pe.elemEnc(e, v.Elem(), opts),经过具体调试,发现最终处理函数为:

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.Pointer {
				if fv.IsNil() {
					continue FieldLoop
				}
				fv = fv.Elem()
			}
			fv = fv.Field(i)
		}

		if f.omitEmpty && isEmptyValue(fv) {
			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('}')
	}
}

最终,我来解释一下这么长的流程吧: json.Marshal(&stu) --> e.marshal(v, encOpts{escapeHTML: true}) --> e.reflectValue(reflect.ValueOf(v), opts) --> valueEncoder(v)(e, v, opts) --> typeEncoder(v.Type()) --> newTypeEncoder(t, true) --> newPtrEncoder(t) --> ptrEncoder{typeEncoder(t.Elem())} --> newStructEncoder(t) --> func (se structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts);可以看出来,层层封装,不过其中的newTypeEncoder(t, true)起到了一个中转作用,首先返回了ptrEncoder{typeEncoder(t.Elem())}结构体,但其中结构体的t.Elem()重新调用了newTypeEncoder函数,指向了newStructEncoder(t)所返回的处理函数;

对应的,我们来简单看一下Unmarshal函数:

func Unmarshal(data []byte, v any) error {
	// Check for well-formedness.
	// Avoids filling out half a data structure
	// before discovering a JSON syntax error.
	var d decodeState  //  与Encode相似,同样的一个 解码状态器
	err := checkValid(data, &d.scan)
	if err != nil {
		return err
	}

	d.init(data)          // 将data注册到decodeState之中的内置切片
	return d.unmarshal(v) // 解析json数据
}