go標準庫(源碼版本go1.9)提供了一個將JSON的過程叫編組(marshaling)成slice函數;編組通過調用json.Marshal()函數完成:
package main
import (
"encoding/json"
"fmt"
"log"
)
func main() {
type Movie struct {
Title string
Year int `json:"released"`
Color bool `json:"color,omitempty"`
Actors []string
}
var movies = []Movie{
{Title: "Casablanca", Year: 1942, Color: false,
Actors: []string{"Humphrey Bogart", "Ingrid Bergman"}},
{Title: "Cool Hand Luke", Year: 1967, Color: true,
Actors: []string{"Paul Newman"}},
{Title: "Bullitt", Year: 1968, Color: true,
Actors: []string{"Steve McQueen", "Jacqueline Bisset"}},
}
data, err := json.Marshal(movies)
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)
}
運行結果:
[{"Title":"Casablanca","released":1942,"Actors":["Humphrey Bogart","Ingrid Bergman"]},{"Title":"Cool Hand Luke","released":1967,"color":true,"Actors":["Paul Newman"]},{"Title":"Bullitt","released":1968,"color":true,"Actors":["Steve McQueen","Jacqueline Bisset"]}]
可見經過編組之後,如果是slice 加入了”[“和”]” 符號、string 加入了“” 符號……
那我們看看這個函數是咋處理的:
func Marshal(v interface{}) ([]byte, error) {
//存儲v的編組之後的bytes.Buffer對象(見下 具體數據結構)
e := &encodeState{}
//調用編組對象的marshal方法,encOpts 對象:編碼過程的配置
err := e.marshal(v, encOpts{escapeHTML: true})
if err != nil {
return nil, err
}
return e.Bytes(), nil
}
//編碼格式化
type encOpts struct {
quoted bool
escapeHTML bool
}
//存儲v的編組之後的bytes.Buffer對象
type encodeState struct {
bytes.Buffer // accumulated output
scratch [64]byte
}
繼續調用encodeState的marshal方法
func (e *encodeState) marshal(v interface{}, opts encOpts) (err error) {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
panic(r)
}
if s, ok := r.(string); ok {
panic(s)
}
err = r.(error)
}
}()
//繼續調用relectValue方法
e.reflectValue(reflect.ValueOf(v), opts)
return nil
}
reflectValue 函數
func (e *encodeState) reflectValue(v reflect.Value, opts encOpts) {
//調用valueEncoder函數生成 encoderFunc 然後執行(e,v,opts)
valueEncoder(v)(e, v, opts)
}
生成valueEncoder
func valueEncoder(v reflect.Value) encoderFunc {
//判斷reflect.value對象是否是代表一個值
if !v.IsValid() {
return invalidValueEncoder
}
//繼續調用typeEncoder函數
return typeEncoder(v.Type())
}
typeEncoder
func typeEncoder(t reflect.Type) encoderFunc {
//encoderCache 是個sync.Map 緩存處理的encoderFunc函數,如果能找到,直接使用
if fi, ok := encoderCache.Load(t); ok {
return fi.(encoderFunc)
}
var (
wg sync.WaitGroup
f encoderFunc
)
wg.Add(1)
fi, loaded := encoderCache.LoadOrStore(t, encoderFunc(func(e *encodeState, v reflect.Value, opts encOpts) {
wg.Wait()
f(e, v, opts)
}))
if loaded {
return fi.(encoderFunc)
}
//Cache對象裏沒有,則自己創建
f = newTypeEncoder(t, true)
wg.Done()
//將創建好的 encoderFunc 對象以type:encoderFunc 的形式存入緩存map中
encoderCache.Store(t, f)
return f
}
newTypeEncoer(reflect.Type,bool)函數:該函數作用根據反射,確定類型函數,進而進行編碼
func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
if t.Implements(marshalerType) {
return marshalerEncoder
}
if t.Kind() != reflect.Ptr && allowAddr {
if reflect.PtrTo(t).Implements(marshalerType) {
return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false))
}
}
if t.Implements(textMarshalerType) {
return textMarshalerEncoder
}
if t.Kind() != reflect.Ptr && allowAddr {
if reflect.PtrTo(t).Implements(textMarshalerType) {
return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false))
}
}
switch t.Kind() {
case reflect.Bool:
return boolEncoder
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return intEncoder
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return uintEncoder
case reflect.Float32:
return float32Encoder
case reflect.Float64:
return float64Encoder
case reflect.String:
return stringEncoder
case reflect.Interface:
return interfaceEncoder
case reflect.Struct:
return newStructEncoder(t)
case reflect.Map:
return newMapEncoder(t)
case reflect.Slice:
return newSliceEncoder(t)
case reflect.Array:
return newArrayEncoder(t)
case reflect.Ptr:
return newPtrEncoder(t)
default:
return unsupportedTypeEncoder
}
}
拿其中一個函數示例:intEncoder
func intEncoder(e *encodeState, v reflect.Value, opts encOpts) {
//轉成[]byte
b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
if opts.quoted {
e.WriteByte('"')
}
//寫入bytes.Buffer對象裏
e.Write(b)
if opts.quoted {
e.WriteByte('"')
}
}