map[string]interface{}

通过同一通道交换多种信息的时候,我们经常需要 JSON 具有动态的,或者更合适的参数内容。首先,让我们来讨论一下消息封装(message envelopes),JSON 在这里看起来就像这样:

{
 "type": "this part tells you how to interpret the message",
 "msg": ...the actual message is here, in some kind of json...
}

通过不同的消息类型生成 JSON

interface{}
{
 "type": "sound",
 "msg": {
  "description": "dynamite",
  "authority": "the Bruce Dickinson"
 }
}

{
 "type": "cowbell",
 "msg": {
  "more": true
 }
}

我们可以使用这些 Go 类型:

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)
}

输出的结果是:

{"Type":"sound","Msg":{"Description":"dynamite","Authority":"the Bruce Dickinson"}}
{"Type":"cowbell","Msg":{"More":true}}

这些并没有什么特殊的。

解析 JSON 到动态类型

EnvelopeMsgmap[string]interface{}
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{}
}

func main() {
 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)
}

输出:

dynamite

明确的解析方式

Envelope
type Envelope {
 Type string
 Msg  *json.RawMessage
}
[]byte
MsgTypeTypeMsgEnvelopeInEnvelopeOutEnvelopeOutMsg interface{}
*json.RawMessageinterface{}
interface{}*json.RawMessage
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)
 }
}

输出:

dynamite

如何把所有数据都放在最外层(顶层)

虽然我极其推荐你将动态可变的部分放在一个单独的 key 下面,但是有时你可能需要处理一些预先存在的数据,它们并没有用这样的方式进行格式化。

如果可以的话,请使用文章前面提到的风格。

{
 "type": "this part tells you how to interpret the message",
 ...the actual message is here, as multiple keys...
}

我们可以通过解析两次数据的方式来解决。

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)
 }
}

dynamite

译者:

校对:

本文由 原创编译, 荣誉推出