Golang将通用JSON对象解码为多种格式之一

时间:2015-01-19 20:52:56

标签: json go unmarshalling

我正在研究golang中基于JSON的通用消息传递协议。我想要做的是BaseMessage有一般信息,如Typetimestamp等。但同时我希望能够定义更具体的消息结构某些类型的数据。

例如:

type Message struct {
    Type      string `json:type`
    Timestamp string `json:timestamp`

}

type EventMessage struct {
    Message
    EventType string
    EventCreator string
    EventData interface{}
}

我有一组处理程序,并确定哪个处理程序应该处理消息我首先将JSON解码为常规Message类型以检查Type字段。对于此示例,我将获得与“事件”消息类型相关联的处理程序。

当我想在结构上声明EventMessage类型时,我遇到了问题。

以下代码非常粗略,但希望它显示了我如何处理消息的一般概念。

type Handler func(msg Message) Message
handlers := make(map[string]Handler)

var msg Message
decoder.Decode(&msg)
handler := handlers[msg.Type]
handler(msg)

我曾尝试使用interface{}但是JSON解码器只是创建了一个地图,然后我无法断言任何一个类型。我已经找到了可行的解决方法,但它非常难看,可能效率不高,而且很可能容易出错。我希望简单明了,这样就可以轻松维护这些代码。

是否有一种在Golang中处理通用JSON对象的方法,以便解码的JSON可以是多种结构格式之一?

我还想过在主Data interface{}结构中的Message中有更多具体信息的想法,但后来我遇到了无法将任何类型断言到接口。必须有一种更好的方法来处理我刚刚缺少的JSON格式。

1 个答案:

答案 0 :(得分:15)

处理此问题的一种方法是使用json.RawMessage字段为消息的固定部分定义结构,以捕获消息的变体部分。将json.RawMessage解码为特定于变体的类型:

type Message struct {
  Type      string `json:type`
  Timestamp string `json:timestamp`
  Data      json.RawMessage
}  

type Event struct {
   Type    string `json:type`
   Creator string `json:creator`
}


var m Message
if err := json.Unmarshal(data, &m); err != nil {
    log.Fatal(err)
}
switch m.Type {
case "event":
    var e Event
    if err := json.Unmarshal([]byte(m.Data), &e); err != nil {
        log.Fatal(err)
    }
    fmt.Println(m.Type, e.Type, e.Creator)
default:
    log.Fatal("bad message type")
}

playground example