web-dev-qa-db-ja.com

インターフェイスへのアンマーシャリング{}とその後の型アサーションの実行

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

これは動作しません。アンマーシャリング後に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:非インターフェイス値にスイッチを入力できません

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

まだ成功していない...

何か案は?

18
Blue Bot

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パッケージは、Something1およびSomething2を認識しません。 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

23
JimB

典型的なJSON対型付き言語の問題が発生しました! jsonは型付けされておらず、スキーマがないため、実際にデコードせずに「文字列の下」にあるデータを推測することはできません。

したがって、唯一のオプションは、常に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 TypedJson struct{
  Type string 
  Data json.RawMessage
}

元Mar:

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
}

型付きシリアル化形式を使用する

9
fl0cke