go語言博客之JSON和Go

簡介(Introduction)css

JSON(JavaScript Object Notation)是一種簡單的數據交換格式。語法上和JAVASCRIPT的對象及列表類似。這是web先後端和JavaScript程序最經常使用的數據傳遞方式,而且其餘不少地方也使用JSON。在JSON的主頁(json.org)上,對JSON的標準作了很是清晰,簡潔的定義。ios

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.golang

經過JSON包,咱們能夠很是容易的使用Go對JSON數據進行讀寫操做。web

With the json package it's a snap to read and write JSON data from your Go programs.json

編碼(Encoding)c#

咱們使用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 Tis 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」,指針的值爲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"Unmarshalwill 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 Message
err := 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 = 2011
i = 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 Parentsfield, 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 IncomingMessagestruct, 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) *Decoder
func 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 Namefield from each object, and then writes the objects to standard output:

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,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

相關文章
相關標籤/搜索