解组为接口{},然后执行类型断言

Unmarshaling Into an Interface{} and Then Performing Type Assertion

我通过 rabbitmq 消息系统得到了 string。发送前,

我使用json.Marshal,将结果转换为string并发送 rabbitmq.

我转换和发送的结构可以是:(更改了结构的名称和大小,但应该无关紧要)

type Somthing1 struct{
   Thing        string    `json:"thing"`
   OtherThing   int64     `json:"other_thing"`
}

type Somthing2 struct{
   Croc        int       `json:"croc"`
   Odile       bool      `json:"odile"`
}

消息作为 string 完美传递并打印 在另一边(一些服务器)

到目前为止一切正常。 现在我正在尝试将它们转换回它们的结构并断言类型。

第一次尝试是:

func typeAssert(msg string) {

 var input interface{}

 json.Unmarshal([]byte(msg), &input)

 switch input.(type){
 case Somthing1:
    job := Somthing1{}
    job = input.(Somthing1)
    queueResults(job)

  case Somthing2:
    stats := Somthing2{}
    stats = input.(Somthing2)
    queueStatsRes(stats)
 default:
}

这不起作用。 Unmarshaling 后打印 input 的类型时 我得到 map[string]interface{} (?!?)

更奇怪的是,映射键是我得到的字符串,映射值是空的。

我做了一些其他尝试,例如:

 func typeAssert(msg string) {

  var input interface{}

  json.Unmarshal([]byte(msg), &input)

  switch v := input.(type){
  case Somthing1:
    v = input.(Somthing1)
    queueResults(v)

   case Somthing2:
    v = input.(Somthing2)
    queueStatsRes(v)
  default:
}

并且还尝试像这个答案中解释的那样编写开关: Golang: cannot type switch on non-interface value

switch v := interface{}(input).(type)

仍然没有成功...

有什么想法吗?

json 包解组到的默认类型显示在 Unmarshal function documentation

bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null

由于您要解组为 interface{},返回的类型将仅来自该集合。 json 包不知道 Something1Something2。您需要从 json 对象被解组到的 map[string]interface{} 进行转换,或者直接解组到您想要的结构类型。

如果您不想从通用接口解压缩数据,或者不想以某种方式标记数据以便您知道期望的类型,您可以迭代地获取 json 并尝试将其解组为每一种你想要的。

您甚至可以将它们打包到包装器结构中来为您解组:

type Something1 struct {
    Thing      string `json:"thing"`
    OtherThing int64  `json:"other_thing"`
}

type Something2 struct {
    Croc  int  `json:"croc"`
    Odile bool `json:"odile"`
}

type Unpacker struct {
    Data       interface{}
}

func (u *Unpacker) UnmarshalJSON(b []byte) error {
    smth1 := &Something1{}
    err := json.Unmarshal(b, smth1)

    // no error, but we also need to make sure we unmarshaled something
    if err == nil && smth1.Thing != "" {
        u.Data = smth1
        return nil
    }

    // abort if we have an error other than the wrong type
    if _, ok := err.(*json.UnmarshalTypeError); err != nil && !ok {
        return err
    }

    smth2 := &Something2{}
    err = json.Unmarshal(b, smth2)
    if err != nil {
        return err
    }

    u.Data = smth2
    return nil
}

http://play.golang.org/p/Trwd6IShDW

您遇到了典型的 json vs 类型化语言问题! 由于 json 是无类型和无模式的,因此在不实际解码的情况下无法推断出 "under the string" 是什么数据。

所以你唯一的选择是解编成一个 interface{},它总是产生一个 map[string]interface{}。你可以在这里做一些反射魔术来构建最终的结构,但这是大量的手动工作并且容易出错。 以下是一些可能的解决方案:

快'n'脏

json 包做反射的事情。尝试解组为每个预期类型:

func typeAssert(msg string) {

 var thing1 Something1

 err := json.Unmarshal([]byte(msg), &thing1)
 if err == nil{
    // do something with thing1
    return
 }    

 var thing2 Something2

 err = json.Unmarshal([]byte(msg), &thing2)
 if err == nil{
    // do something with thing2
    return
 }    

 //handle unsupported type

}

在 json

的基础上构建您自己的 "type system"

推迟编码直到你知道里面是什么。使用此结构作为数据的中间表示:

type TypedJson struct{
  Type string 
  Data json.RawMessage
}

元帅:

thing := Something1{"asd",123}
tempJson, _ := json.Marshal(thing)

typedThing := TypedJson{"something1", tempJson}
finalJson, _ := json.Marshal(typedThing)

解组:

func typeAssert(msg string) {

  var input TypedJson  
  json.Unmarshal([]byte(msg), &input)

  switch input.Type{
  case "something1":
    var thing Something1
    json.Unmarshal(input.Data, &thing)
    queueStatsRes(thing)   
   case "something2":
    var thing Something2
    json.Unmarshal(input.Data, &thing)
    queueStatsRes(thing)
  default:
    //handle unsupported type
}

使用类型化序列化格式