假如要設計一個統計的json解析模塊,json格式爲git
{ "type": "用來識別不一樣的json數據", "msg": "嵌套的實際數據" }
代碼github
package main import ( "encoding/json" "fmt" "log" ) type Envelope struct { Type string Msg interface{} // 接受任意的類型 } type Sound struct { Description string Authority string } type Cowbell struct { More bool } func main() { s := Envelope{ Type: "sound", Msg: Sound{ Description: "dynamite", Authority: "the Bruce Dickinson", }, } buf, err := json.Marshal(s) if err != nil { log.Fatal(err) } fmt.Printf("%s\n", buf) c := Envelope{ Type: "cowbell", Msg: Cowbell{ More: true, }, } buf, err = json.Marshal(c) if err != nil { log.Fatal(err) } fmt.Printf("%s\n", buf) }
咱們定義Msg類型爲interface{},用來接受任意的類型。接下來試着解析msg中的字段golang
const input = ` { "type": "sound", "msg": { "description": "dynamite", "authority": "the Bruce Dickinson" } } ` var env Envelope if err := json.Unmarshal([]byte(input), &env); err != nil { log.Fatal(err) } // for the love of Gopher DO NOT DO THIS var desc string = env.Msg.(map[string]interface{})["description"].(string) fmt.Println(desc)
有更好的寫法,使用*json.RawMessage, 將msg字段延遲解析json
type Envelope { Type string Msg *json.RawMessage }
結合interface{}和*json.RawMessage的完整例子app
package main import ( "encoding/json" "fmt" "log" ) const input = ` { "type": "sound", "msg": { "description": "dynamite", "authority": "the Bruce Dickinson" } } ` type Envelope struct { Type string Msg interface{} } type Sound struct { Description string Authority string } func main() { var msg json.RawMessage env := Envelope{ Msg: &msg, } if err := json.Unmarshal([]byte(input), &env); err != nil { log.Fatal(err) } switch env.Type { case "sound": var s Sound if err := json.Unmarshal(msg, &s); err != nil { log.Fatal(err) } var desc string = s.Description fmt.Println(desc) default: log.Fatalf("unknown message type: %q", env.Type) } }
第一部分結束了,接下來還有來個地方能夠提高.net
//go:generate jsonenums -type=Kind type Kind int const ( sound Kind = iota cowbell )
定義完上述內容後,執行命令翻譯
jsonenums -type=Pill
這個模塊會自動生成一個*_jsonenums.go的文件,裏面定義好了設計
func (t T) MarshalJSON() ([]byte, error) func (t *T) UnmarshalJSON([]byte) error
這樣,就幫咱們把自定義的Kind和json type裏的序列化和反序列化都作好了code
var kindHandlers = map[Kind]func() interface{}{ sound: func() interface{} { return &SoundMsg{} }, cowbell: func() interface{} { return &CowbellMsg{} }, }
type App struct { // whatever your application state is } // Action is something that can operate on the application. type Action interface { Run(app *App) error } type CowbellMsg struct { // ... } func (m *CowbellMsg) Run(app *App) error { // ... } type SoundMsg struct { // ... } func (m *SoundMsg) Run(app *App) error { // ... } var kindHandlers = map[Kind]func() Action{ sound: func() Action { return &SoundMsg{} }, cowbell: func() Action { return &CowbellMsg{} }, } func main() { app := &App{ // ... } // process an incoming message var raw json.RawMessage env := Envelope{ Msg: &raw, } if err := json.Unmarshal([]byte(input), &env); err != nil { log.Fatal(err) } msg := kindHandlers[env.Type]() if err := json.Unmarshal(raw, msg); err != nil { log.Fatal(err) } if err := msg.Run(app); err != nil { // ... } }
接下來是另一種設想,加入定義的json字段都放在最外層,即沒有了嵌套的msg字段ip
{ "type": "用來識別不一樣的json數據", ... }
那須要umarshal兩次json,第一次比對type字段,針對不一樣的type字段來unmarsh一次
package main import ( "encoding/json" "fmt" "log" ) const input = ` { "type": "sound", "description": "dynamite", "authority": "the Bruce Dickinson" } ` type Envelope struct { Type string } type Sound struct { Description string Authority string } func main() { var env Envelope buf := []byte(input) if err := json.Unmarshal(buf, &env); err != nil { log.Fatal(err) } switch env.Type { case "sound": var s struct { Envelope Sound } if err := json.Unmarshal(buf, &s); err != nil { log.Fatal(err) } var desc string = s.Description fmt.Println(desc) default: log.Fatalf("unknown message type: %q", env.Type) } }
本文是下述博客的翻譯和整理,僅供參考