go语言博客之JSON和Go

qii · · 951 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

简介(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 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


有疑问加站长微信联系(非本文作者)

本文来自:开源中国博客

感谢作者:qii

查看原文:go语言博客之JSON和Go

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

951 次点击  
加入收藏 微博
上一篇:go语言学习
下一篇:go 结构体
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传