這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
簡介(Introduction)
JSON(JavaScript Object Notation)是一種簡單的資料交換格式。文法上和JAVASCRIPT的對象及列表相似。這是web前後端和JavaScript程式最常用的資料傳遞方式,並且其他很多地方也使用JSON。在JSON的首頁(json.org)上,對JSON的標準做了非常清晰,簡潔的定義。
JSON (JavaScript Object Notation) is a simple data interchange format. Syntactically it resembles the objects and lists of JavaScript. It is most commonly used for communication between web back-ends and JavaScript programs running in the browser, but it is used in many other places, too. Its home page, json.org, provides a wonderfully clear and concise definition of the standard.
通過JSON包,我們可以非常容易的使用Go對JSON資料進行讀寫操作。
With the json package it's a snap to read and write JSON data from your Go programs.
編碼(Encoding)
我們使用Marshal方法對JSON進行編碼。
To encode JSON data we use the Marshal
function.
func Marshal(v interface{}) ([]byte, error)
定義一個結構體,Message,並初始化。
Given the Go data structure, Message
,and an instance of Message
type Message struct { Name string Body string Time int64}m := Message{"Alice", "Hello", 1294706395881547000}
我們可以使用變數m方法來對json.Marshal的結果進行整理。
we can marshal a JSON-encoded version of m using json.Marshal
:
b, err := json.Marshal(m)
如果編碼正確的話,err將返回nil,b將包含一個[]byte類型的JSON資料
If all is well, err
will be nil
and b
will be a []byte
containing this JSON data:
b == []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
僅當資料結構能表示JSON的時候,才能被正確的編碼。
Only data structures that can be represented as valid JSON will be encoded:
- JSON對象的索引值僅支援字串(string)類型;也就是說,被編碼的資料類型必須是map[string]T(T可以是go的任意被json包支援的任何資料類型)
- JSON objects only support strings as keys; to encode a Go map type it must be of the form
map[string]T
(where T
is any Go type supported by the json package).
- 通道類型(chan),複合類型和函數不能被編碼
- Channel, complex, and function types cannot be encoded.
- 不支援迴圈的資料結構類型;這會導致
Marshal方法死迴圈。
- Cyclic data structures are not supported; they will cause
Marshal
to go into an infinite loop.
- 指標類型將被編碼為指標指向的止(如果是null 指標“null”,指標的值為nil)
- Pointers will be encoded as the values they point to (or 'null' if the pointer is
nil
).
json包僅僅能夠訪問結構體的公有欄位(已大寫字母開頭的欄位)。因此,僅僅是公有欄位才會出現在JSON的輸出中。
The json package only accesses the exported fields of struct types (those that begin with an uppercase letter). Therefore only the the exported fields of a struct will be present in the JSON output.
解碼(Decoding)
使用Unmarshal方法解碼JSON資料。
To decode JSON data we use the Unmarshal
function.
func Unmarshal(data []byte, v interface{}) error
我們需要建立一個變數來儲存我們解碼JSON資料,然後調用json.Unmarshal方法,並把需要解碼的資料和這個變數的指標,傳遞給該函數。
We must first create a place where the decoded data will be stored,and call json.Unmarshal
, passing it a []byte
of JSON data and a pointer to m
err := json.Unmarshal(b, &m)
假如b包含了一段合法的JSON資料,並且結構與m匹配,那麼err將返回nil,解碼後的資料會別儲存在m上,如下這樣:
If b
contains valid JSON that fits in m
, after the call err
will be nil
and the data from b
will have been stored in the struct m
, as if by an assignment like:
m = Message{ Name: "Alice", Body: "Hello", Time: 1294706395881547000,}
Unmarshal
方法是如何識別欄位是他要儲存的呢?對於給定的鍵"Foo",Unmarshal將會檢查給定的結構體的欄位(按順序優先)
How does Unmarshal
identify the fields in which to store the decoded data? For a given JSON key "Foo"
, Unmarshal
will look through the destination struct's fields to find (in order of preference):
- 一個公有的欄位標記:“Foo”(參見Go spec)
- An exported field with a tag of
"Foo"
(see the Go spec for more on struct tags),
- 一個公有的欄位名稱“Foo",或者
- An exported field named
"Foo"
, or
- 形如“FOO",”FoO"的,大小寫不敏感的匹配Foo的公有欄位
- An exported field named
"FOO"
or "FoO"
or some other case-insensitive match of "Foo"
.
當JSON資料和結構體不能完全符合時,會發生什嗎?
What happens when the structure of the JSON data doesn't exactly match the Go type?
b := []byte(`{"Name":"Bob","Food":"Pickle"}`)var m Messageerr := json.Unmarshal(b, &m)//m和b中,都有Name,m中沒有Food,b中沒有Body和Time
Unmarshal
僅會解碼在結構體中能夠找到的欄位。在這種情況下,只有“Name”欄位會被m儲存,Food欄位將被忽略。當你想從一個非常龐大的JSON資料中提取幾個特定的欄位時,這種手段就變得非常有用。換句話說,結構體中的非公有欄位將不會被解出。
Unmarshal
will decode only the fields that it can find in the destination type. In this case, only the Name field of m will be populated, and the Food field will be ignored. This behavior is particularly useful when you wish to pick only a few specific fields out of a large JSON blob. It also means that any unexported fields in the destination struct will be unaffected by Unmarshal
.
但是,假如你事先並不知道JSON資料的結構呢?
But what if you don't know the structure of your JSON data beforehand?
(通用的JSON類型interface{})Generic JSON with interface{}
interface{}(空介面)描述了一個沒有任何方法的介面類型。任何一個Go類型都是由interface{}來的。
The interface{}
(empty interface) type describes an interface with zero methods. Every Go type implements at least zero methods and therefore satisfies the empty interface.
空介面類型是通用的容器類型:
The empty interface serves as a general container type:
var i interface{}i = "a string"i = 2011i = 2.777
類型斷言能夠轉換潛在資料類型到具體的資料類型
A type assertion accesses the underlying concrete type:
r := i.(float64)fmt.Println("the circle's area", math.Pi*r*r)
假如潛在類型是未知的,那麼可以使用switch測試類型來決定資料類型。
Or, if the underlying type is unknown, a type switch determines the type:
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資料都能夠存入interface{},具體的資料類型如下:
The json package uses map[string]interface{}
and []interface{}
values to store arbitrary JSON objects and arrays; it will happily unmarshal any valid JSON blob into a plain interface{}
value. The default concrete Go types are:
- 布爾對象轉換為bool
bool
for JSON booleans,
- 數字為float64
float64
for JSON numbers,
- 字串為string
string
for JSON strings, and
- NULL為nil
nil
for JSON null.
解碼任意資料(Decoding arbitrary data)
假設JSON資料存在在變數b上:
Consider this JSON data, stored in the variable b
:
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
我們並不知道它的資料結構,我們可以將它解碼到interface{}上。
Without knowing this data's structure, we can decode it into an interface{}
value with Unmarshal
:
var f interface{}err := json.Unmarshal(b, &f)
在這種情況下,f的值是一個鍵為string類型,值為儲存在interface{}中的map類型。
At this point the Go value in f
would be a map whose keys are strings and whose values are themselves stored as empty interface values:
f = map[string]interface{}{ "Name": "Wednesday", "Age": 6, "Parents": []interface{}{ "Gomez", "Morticia", },}
我們可以使用f的潛在類型(map[string]interface{}
)來訪問這些資料
To access this data we can use a type assertion to access `f`'s underlying map[string]interface{}
:
m := f.(map[string]interface{})
我們可以通過迭代map,用switch來測試這些資料類型來確定這些值的資料類型。
We can then iterate through the map with a range statement and use a type switch to access its values as their concrete types:
for k, v := range m { switch vv := v.(type) { case string: fmt.Println(k, "is string", vv) case int: fmt.Println(k, "is int", 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資料,並且保證資料的型別安全。
In this way you can work with unknown JSON data while still enjoying the benefits of type safety.
參考型別(Reference Types)
定義一個資料結構包含上一個例子中的資料類型。
Let's define a Go type to contain the data from the previous example:
type FamilyMember struct { Name string Age int Parents []string} var m FamilyMember err := json.Unmarshal(b, &m)
將資料解碼到FamilyMember中,得到的資料符合預期,但是如果我們進一步觀察就會注意到一件異常的事情。在申明語句中,我們分配了一個FamilyMember的結構,然後將他做完指標傳遞給Unmarshal方法,但是這時,欄位Parents是一個空(nil)的切片。為了填充Parents欄位,在這之後,Unmarshal
方法又分配了一個新的切片給Parents。這是Unmarshal
典型的如何支援類型引用(指標,切片,字典)的方式。
Unmarshaling that data into a FamilyMember
value works as expected, but if we look closely we can see a remarkable thing has happened. With the var statement we allocated a FamilyMember
struct, and then provided a pointer to that value to Unmarshal
, but at that time the Parents
field was a nil
slice value. To populate the Parents
field, Unmarshal
allocated a new slice behind the scenes. This is typical of how Unmarshal
works with the supported reference types (pointers, slices, and maps).
考慮一下把資料解碼到這個資料結構:
Consider unmarshaling into this data structure:
type Foo struct { Bar *Bar}
加入Bar是一個JSON對象的欄位,Unmarshal
方法將會分配一個新的Bar來存放它,如果不這麼做,Bar將會指向一個空的指標。
If there were a Bar
field in the JSON object, Unmarshal
would allocate a new Bar
and populate it. If not, Bar
would be left as a nil
pointer.
在探討這個模式出現:假如你的應用只是接收一些簡單的不重複的訊息,你可能會像這樣定義你的資料結構:
From this a useful pattern arises: if you have an application that receives a few distinct message types, you might define "receiver" structure like
type IncomingMessage struct { Cmd *Command Msg *Message}
發送方可以儲存Cmd欄位和/或者Msg欄位作為json對象的頂層資料,作為他們之間想要傳遞的資料。當Unmarshal方法將資料解碼傳入IncomingMessage中時,將會分配一個資料結構來儲存JSON資料。為了知道那個message是被處理的,程式需要評鑑的測試來儲存Cmd和Msg不是空的(nil).
and the sending party can populate the Cmd
field and/or the Msg
field of the top-level JSON object, depending on the type of message they want to communicate. Unmarshal
, when decoding the JSON into an IncomingMessage
struct, will only allocate the data structures present in the JSON data. To know which messages to process, the programmer need simply test that either Cmd
or Msg
is not nil
.
(流編碼器與解碼器)Streaming Encoders and Decoders
json包提供了對JSON資料流的編碼(Decoder
)與解碼(Decoder
)支援。NewDecoder
和NewEncoder
函數包含了io.Reader
和 io.Writer介面類型。
func NewDecoder(r io.Reader) *Decoderfunc NewEncoder(w io.Writer) *Encoder
The json package provides Decoder
and Encoder
types to support the common operation of reading and writing streams of JSON data. The NewDecoder
and NewEncoder
functions wrap the io.Reader
and io.Writer
interface types.
下面這個例子示範了從表中輸入中讀取一些JSON資料,並移除除了Name之外的其他欄位,然後寫入標準輸出中。
Here's an example program that reads a series of JSON objects from standard input, removes all but the Name
field from each object, and then writes the objects to standard output:
package mainimport ( "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,WebSocket或者檔案中讀取和寫入JSON資料。
Due to the ubiquity of Readers and Writers, these Encoder
and Decoder
types can be used in a broad range of scenarios, such as reading and writing to HTTP connections, WebSockets, or files.
引用References
For more information see the json package documentation. For an example usage of json see the source files of the jsonrpc package.
By Andrew Gerrand