在用beego寫服務時,用hprose-golang調用某個異構rpc服務,再返回json到調用方時,報錯了:json: unsupported type: map[interface {}]interface {}
controller示例代碼:
查看代碼
package controllers
import (
"github.com/hprose/hprose-golang/rpc"
beego "github.com/beego/beego/v2/server/web"
)
type MainController struct {
beego.Controller
}
// 獲取服務器信息
func (this *MainController) GetServersInfo() {
// client := rpc.NewClient("http://192.168.1.114:23000/")
// var stub *rcpClient.ClientStub
// client.UseService(&stub)
// res := stub.Server_getServerInfo() // 返回的Type是map[interface{}]interface{}
res := map[interface{}]interface{}{
11: 22,
"kk": "tt",
}
this.Data["json"] = res
this.ServeJSON()
}
問題其實不是beego,而是在go原生的encoding/json庫的Marshal(序列化)方法。
示例代碼:
package main
import (
"encoding/json"
"fmt"
"reflect"
)
func main() {
var data = map[interface{}]interface{}{
"tt": "werwer",
"kk":1,
}
content, err := json.Marshal(data)
fmt.Println("tt")
if err != nil {
panic(err)
}
fmt.Println(string(content))
}
輸出:
$ go run kk.go
tt
panic: json: unsupported type: map[interface {}]interface {}
goroutine 1 [running]:
main.main()
/Users/demon/Desktop/test/beegosource/quickstart/ttttt/kk.go:91 +0x168c
exit status 2
看起來是不支持map[interface {}]interface {}這個類型。
json.Marshal源碼小探
func Marshal(v interface{}) ([]byte, error) {
e := newEncodeState() // 從池里取,沒有才新建
err := e.marshal(v, encOpts{escapeHTML: true})
if err != nil {
return nil, err
}
buf := append([]byte(nil), e.Bytes()...)
encodeStatePool.Put(e)
return buf, nil
}
上面的代碼,newEncodeState()方法優先從池中取已緩存的對象取,沒有才新建一個。用完之后,再放進池里。
接下來調用的e.marshal方法。
func (e *encodeState) marshal(v interface{}, opts encOpts) (err error) {
defer func() {
if r := recover(); r != nil {
if je, ok := r.(jsonError); ok {
err = je.error
} else {
panic(r)
}
}
}()
// v = map[interface{}]interface{} ValueOf: map[kk:1 werewr:werwer]
e.reflectValue(reflect.ValueOf(v), opts)
return nil
}
反射來了,獲取一個空接口的動態值的反射Value,傳到reflectValue方法。
func (e *encodeState) reflectValue(v reflect.Value, opts encOpts) {
valueEncoder(v)(e, v, opts)
}
func valueEncoder(v reflect.Value) encoderFunc {
if !v.IsValid() {
return invalidValueEncoder
}
// v: map[kk:1 werewr:werwer] v.Type(): map[interface {}]interface {}
return typeEncoder(v.Type())
}
從上面代碼看到,接着調用到typeEncoder方法,傳入了map具體值的抽象類型表示reflect.Type:map[interface {}]interface {}。
typeEncoder方法
查看代碼
func typeEncoder(t reflect.Type) encoderFunc {
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) {
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)
wg.Done()
encoderCache.Store(t, f)
return f
}
encoderCache是一個sync.Map,優先從encoderCache里取這個Type對應的編碼方法,如果沒有的話,存一個新的(LoadOrStore)注意這里有一個閉包,encoderCache里存放Type對應的值是個func,而這個func里引入了外面的f變量,當typeEncoder方法執行完返回時,當前typeEncoder的內部變量不會銷毀,從后面可以看到,func里引用f變量,相當於先引用,后賦值。
再往下,進入newTypeEncoder函數。
查看代碼
func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
// If we have a non-pointer value whose type implements
// Marshaler with a value receiver, then we're better off taking
// the address of the value - otherwise we end up with an
// allocation as we cast the value to an interface.
if t.Kind() != reflect.Ptr && allowAddr && reflect.PtrTo(t).Implements(marshalerType) {
return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false))
}
if t.Implements(marshalerType) {
return marshalerEncoder
}
if t.Kind() != reflect.Ptr && allowAddr && reflect.PtrTo(t).Implements(textMarshalerType) {
return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false))
}
if t.Implements(textMarshalerType) {
return textMarshalerEncoder
}
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:
// 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
}
}
func newMapEncoder(t reflect.Type) encoderFunc {
// t: map[interface {}]interface {}
switch t.Key().Kind() {
case reflect.String,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
default:
if !t.Key().Implements(textMarshalerType) {
return unsupportedTypeEncoder
}
}
// t.Elem(): interface {}
me := mapEncoder{typeEncoder(t.Elem())} // 這里是要拿到key對應value的編碼器
return me.encode
}
switch t.Kind() {
...
case reflect.Interface:
return interfaceEncoder
...
}
me := mapEncoder{typeEncoder(t.Elem())} // 這里是要拿到key對應value的編碼器
return me.encode
func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
if v.IsNil() {
e.WriteString("null")
return
}
// 寫buffer
e.WriteByte('{')
// Extract and sort the keys.
keys := v.MapKeys() // map的鍵對應的Value
sv := make([]reflectWithString, len(keys))
for i, v := range keys {
sv[i].v = v
if err := sv[i].resolve(); err != nil {
e.error(fmt.Errorf("json: encoding error for type %q: %q", v.Type().String(), err.Error()))
}
}
sort.Slice(sv, func(i, j int) bool { return sv[i].s < sv[j].s })
// 遍歷key, 遞歸寫入 key: value 字符串
for i, kv := range sv {
if i > 0 {
e.WriteByte(',')
}
e.string(kv.s, opts.escapeHTML)
e.WriteByte(':')
// 遍歷value的編碼方法,傳入key對應的value
me.elemEnc(e, v.MapIndex(kv.v), opts)
}
e.WriteByte('}')
}
遍歷map的key,寫入key:value到e的buffer。其中value的編碼方法獲取也是跟前面一樣,優先從緩沖里取。
總結
1、要熟悉reflect的基本概念:Type、Value、Type與Kind區別、Elem()意義等。
2、var encodeStatePool sync.Pool 緩沖池的使用可以借鑒。