rabbitmq 解封送到接口{}中,然后执行类型Assert

enyaitl3  于 2023-05-06  发布在  RabbitMQ
关注(0)|答案(3)|浏览(196)

我通过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的形式完美地通过,并打印在另一面(某个服务器)
到目前为止,一切正常。现在我尝试将它们转换回它们的结构并Assert类型。
第一次尝试是:

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:
}

这不起作用。当打印input的类型时,我得到map[string]interface{}(?!?)
更奇怪的是,map键是我得到的字符串,而map值是空的。
我做了一些其他的尝试,比如:

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:
}

并且还试着写开关,就像在这个答案中解释的那样:

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

还是没有成功
有什么想法吗

wribegjk

wribegjk1#

json包解组到的默认类型显示在Unmarshal函数文档中

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,并尝试将其解组为你想要的每种类型。
你甚至可以将它们打包到一个 Package 器结构中来为你进行解封:

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

7ajki6be

7ajki6be2#

您遇到了一个典型的json vs类型化语言问题!由于json是无类型和无模式的,因此不可能在不实际解码的情况下推断出“字符串下”的数据。
因此,您唯一的选择是解组为interface{},它始终生成map[string]interface{}。您可以在这里使用一些反射魔法来构建最终的结构体,但这需要大量的手工操作,而且容易出错。以下是一些可能的解决方案:

快速和肮脏

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 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
}

使用类型化序列化格式

oiopk7p5

oiopk7p53#

我喜欢这种风格的“消息”类型,它可以解组任何预期的消息。
几个好处:

  • 它在更大的json结构中作为子类型很好,因为它实现了UnmarshalJSON。这使得它更可重复使用。
  • 消息总是被解析为相同的类型,所以我们是静态类型的,但是类型字段告诉你它是什么类型的消息
  • Somthing 1/Somthing 2字段是强类型的,但只填充正确的字段。

我更希望顶层消息有一个显式类型。比如{"messageType":"Somthing1", "messageData":{...}}。这将把试错方面从解析中去掉。但您不能总是控制数据源,可能需要求助于它。

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

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

type Message struct{
  Type string // enum type here would be nice, but string for brevity

  // pointers, because only one of these will be populated, and the other will be nil. Can add as many as you want as you add message types.
  Somthing1 *Somthing1
  Somthing2 *Somthing2
}

func (m *Message) UnmarshalJSON(b []byte) error {
  var s1 Somthing1
  err := json.Unmarshal(b, &s1)
  if err == nil {
    // this line is some sort of check s1 is a valid Somthing1. Will depend on use case/data model
    if s1.Thing != "" {
      m.Type = "Somthing1"
      m.Somthing1 = &s1
      return nil
    }
  }

  var s2 Somthing2
  err = json.Unmarshal(b, &s2)
  if err == nil {
    // this line is some sort of check s2 is a valid Somthing2. Will depend on use case/data model
    if s2.Croc > 0 {
      m.Type = "Somthing2"
      m.Somthing2 = &s2
      return nil
    }
  }

  return errors.New("Invalid message")
}

示例(基于JimB的样本):https://go.dev/play/p/vQfY--lSGmh

相关问题