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 緩沖池的使用可以借鑒。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM