JSON(JavaScript Object Notation)是一种简单的数据交换格式。在语法上,它类似于 JavaScript 的对象和列表。它最常用于 Web 后端和浏览器中运行的 JavaScript 程序之间的通信,但它也用于许多其他地方。它的主页json.org提供了一个非常清晰和简洁的标准定义。
使用json 包,可以轻而易举地从 Go 程序中读取和写入 JSON 数据。
为了编码 JSON 数据,我们使用该Marshal函数。
Marshal
func Marshal(v interface{}) ([]byte, error)
给定 Go 数据结构Message,
Message
type Message struct { Name string Body string Time int64 }
和一个实例 Message
m := Message{"Alice", "Hello", 1294706395881547000}
我们可以使用以下命令编组 m 的 JSON 编码版本json.Marshal:
json.Marshal
b, err := json.Marshal(m)
如果一切顺利,err将成为nil和b将成为一个[]byte包含此JSON数据:
err
nil
b
[]byte
b == []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
只有可以表示为有效 JSON 的数据结构才会被编码:
map[string]T
T
json 包仅访问结构类型的导出字段(以大写字母开头的字段)。因此,只有结构的导出字段才会出现在 JSON 输出中。
要解码 JSON 数据,我们使用该Unmarshal函数。
Unmarshal
func Unmarshal(data []byte, v interface{}) error
我们必须首先创建一个地方来存储解码后的数据
var m Message
并调用json.Unmarshal,向它传递一个[]byteJSON 数据和一个指向m
json.Unmarshal
m
err := json.Unmarshal(b, &m)
如果b包含有效的JSON,适合在m后电话err将nil与从数据b将被存储在结构m,仿佛像一个任务:
m = Message{ Name: "Alice", Body: "Hello", Time: 1294706395881547000, }
如何Unmarshal识别存储解码数据的字段?对于给定的 JSON key "Foo", Unmarshal将查看目标结构的字段以查找(按优先顺序):
"Foo"
"FOO"
"FoO"
当 JSON 数据的结构与 Go 类型不完全匹配时会发生什么?
b := []byte(`{"Name":"Bob","Food":"Pickle"}`) var m Message err := json.Unmarshal(b, &m)
Unmarshal将只解码它可以在目标类型中找到的字段。在这种情况下,只会填充 m 的 Name 字段,而忽略 Food 字段。当您希望从大型 JSON blob 中仅选择几个特定字段时,此行为特别有用。这也意味着目标结构中任何未导出的字段都不会受到Unmarshal.
但是如果您事先不知道 JSON 数据的结构怎么办?
的interface{}(空接口)类型描述了具有零种方法的接口。每个 Go 类型至少实现零个方法,因此满足空接口。
interface{}
空接口用作通用容器类型:
var i interface{} i = "a string" i = 2011 i = 2.777
类型断言访问底层的具体类型:
r := i.(float64) fmt.Println("the circle's area", math.Pi*r*r)
或者,如果底层类型未知,则类型开关确定类型:
switch v := i.(type) { case int: fmt.Println("twice i is", v*2) case float64: fmt.Println("the reciprocal of i is", 1/v) case string: h := len(v) / 2 fmt.Println("i swapped by halves is", v[h:]+v[:h]) default: // i isn't one of the types above }
json 包使用map[string]interface{}和 []interface{}值来存储任意 JSON 对象和数组;它会很高兴地将任何有效的 JSON blob 解组为一个普通 interface{}值。默认的具体 Go 类型是:
map[string]interface{}
[]interface{}
bool
float64
string
考虑存储在变量中的这个 JSON 数据b:
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
在不知道此数据结构的情况下,我们可以将其解码为一个interface{}值Unmarshal:
var f interface{} err := json.Unmarshal(b, &f)
此时 Go 中的值f将是一个映射,其键是字符串,其值本身存储为空接口值:
f
f = map[string]interface{}{ "Name": "Wednesday", "Age": 6, "Parents": []interface{}{ "Gomez", "Morticia", }, }
要访问这些数据,我们可以使用类型断言来访问f的底层map[string]interface{}:
m := f.(map[string]interface{})
然后我们可以使用 range 语句遍历 map 并使用类型开关来访问它的值作为它们的具体类型:
for k, v := range m { switch vv := v.(type) { case string: fmt.Println(k, "is string", vv) case float64: fmt.Println(k, "is float64", vv) case []interface{}: fmt.Println(k, "is an array:") for i, u := range vv { fmt.Println(i, u) } default: fmt.Println(k, "is of a type I don't know how to handle") } }
通过这种方式,您可以处理未知的 JSON 数据,同时仍然享受类型安全的好处。
让我们定义一个 Go 类型来包含上一个示例中的数据:
type FamilyMember struct { Name string Age int Parents []string } var m FamilyMember err := json.Unmarshal(b, &m)
将这些数据解组为一个FamilyMember值可以按预期工作,但是如果我们仔细观察,我们会发现发生了一件了不起的事情。通过 var 语句,我们分配了一个FamilyMember结构体,然后将指向该值的指针提供给Unmarshal,但当时该Parents字段是一个nil切片值。要填充该Parents字段,请Unmarshal在幕后分配一个新切片。这是Unmarshal使用受支持的引用类型(指针、切片和映射)的典型方式。
FamilyMember
Parents
考虑解组到这个数据结构中:
type Foo struct { Bar *Bar }
如果BarJSON 对象中有一个字段,Unmarshal则会分配一个新字段 Bar并填充它。如果没有,Bar将作为nil指针留下。
Bar
由此产生了一个有用的模式:如果您的应用程序接收几种不同的消息类型,您可以定义“接收器”结构,如
type IncomingMessage struct { Cmd *Command Msg *Message }
并且发送方可以填充Cmd字段和/或Msg顶级 JSON 对象的字段,具体取决于他们想要通信的消息类型。 Unmarshal, 将 JSON 解码为IncomingMessage结构体时,只会分配 JSON 数据中存在的数据结构。要知道这消息的过程中,程序员需要简单地测试,要么Cmd或Msg不是nil。
Cmd
Msg
IncomingMessage
json 包提供Decoder和Encoder类型来支持读写 JSON 数据流的常用操作。的NewDecoder和NewEncoder功能包裹io.Reader 和io.Writer接口类型。
Decoder
Encoder
NewDecoder
NewEncoder
io.Reader
io.Writer
func NewDecoder(r io.Reader) *Decoder func NewEncoder(w io.Writer) *Encoder
这是一个示例程序,它从标准输入读取一系列 JSON 对象,Name从每个对象中删除除字段之外的所有对象,然后将对象写入标准输出:
Name
package main import ( "encoding/json" "log" "os" ) func main() { dec := json.NewDecoder(os.Stdin) enc := json.NewEncoder(os.Stdout) for { var v map[string]interface{} if err := dec.Decode(&v); err != nil { log.Println(err) return } for k := range v { if k != "Name" { delete(v, k) } } if err := enc.Encode(&v); err != nil { log.Println(err) } } }
由于 Readers 和 Writers 无处不在,这些Encoder和Decoder类型可用于广泛的场景,例如读写 HTTP 连接、WebSockets 或文件。
原文链接:https://www.cnblogs.com/egmkang/p/13934225.html