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方法。

 

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函数。

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
  }
}
其中,
var (
    marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
    textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
)
Marshaler是个接口,包含MarshalJSON方法,而TextMarshaler也是个接口,包含MarshalText方法。这两个方法,就是我们常见的自定义序列化需要实现的方法。
newTypeEncoder方法依次判断是否实现了这两个接口,如果有自定义实现,就返回自定义方法。如果没有,就获取一下Type的Kind(),这里因为是reflect.map,所以返回newMapEncoder(t)。
 
newMapEncoder(t)函数
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
}
来了 ,拿到当前map的键(t.Key())的种类(Kind()),这里因为是map[interface {}]interface {},键的类型是interface{},Kind()也是interface{},所以会走default,但interface{}作为空接口,肯定是没有实现encoding.TextMarshaler接口的,所以就报错了。
 
假设可以通过,那么接下来,就是获取map的键值对的值(t.Elem())的编码方法了,t.Elem()返回的是interface{},而interface{}作为值,在typeEncoder --> newTypeEncoder里看到,是有interface这个分支的:
switch t.Kind() {
  ...
  case reflect.Interface:
    return interfaceEncoder
  ...
}
所以,至此,终于找到了map[interface{}]interface{}的编码方法,就是me.encode
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 缓冲池的使用可以借鉴。

 

posted on 2022-02-16 20:51  留校察看  阅读(2567)  评论(0编辑  收藏  举报

导航