假如要设计一个统计的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) } }
本文是下述博客的翻译和整理,仅供参考