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)
まだ成功していない...
何か案は?
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
}
典型的なJSON対型付き言語の問題が発生しました! 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
}
内容がわかるまでエンコードを延期します。この構造体をデータの中間表現として使用します。
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
}