我通过rabbitmq消息系统获得string
。在发送之前,
我使用json.Marshal
,将结果转换为string
并通过发送兔子。
我转换和发送的结构可以是:(更改了结构的名称和大小,但这无关紧要)
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{}
(?!?)
键是我得到的字符串,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:
}
并且还尝试编写开关,就像这个答案中解释的那样: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
}
http://play.golang.org/p/Trwd6IShDW
您遇到了典型的 json 与类型语言问题!由于 json 是非类型化和无模式的,因此如果不实际解码它,就不可能推断出哪些数据"在字符串下"。
因此,您唯一的选择是解组到始终产生map[string]interface{}
的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
}
元帅:
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
}
使用类型化序列化格式
- Go 自己的 gob 编码
- 协议缓冲区
- 还有更多...
我喜欢这种"消息"类型的风格,它可以取消任何预期的消息。
几个好处:
- 它非常适合作为更大的 json 结构中的子类型,因为它实现了
UnmarshalJSON
.这使得它更可重用。
消息 - 始终解析为相同的类型,因此我们是静态类型的,但类型字段会告诉您它是什么类型的消息
- Somthing1/Somthing2 字段是强类型字段,但只填充正确的字段。
我希望顶级消息具有显式类型。像{"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