go json.Marshal报错 unsupported type: map[interface {}]interface {}
在用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 缓冲池的使用可以借鉴。