21 JSON and Go

JSON and Go

25 January 2011

Introduction

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.

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

Encoding

To encode JSON data we use the Marshal function.

func Marshal(v interface{}) ([]byte, error)

Given the Go data structure, Message,

type Message struct {
    Name string
    Body string
    Time int64
}

and an instance of Message

m := Message{"Alice", "Hello", 1294706395881547000}

we can marshal a JSON-encoded version of m using json.Marshal:

b, err := json.Marshal(m)

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

Only data structures that can be represented as valid JSON will be encoded:

  • 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).
  • Channel, complex, and function types cannot be encoded.
  • Cyclic data structures are not supported; they will cause Marshal to go into an infinite loop.
  • Pointers will be encoded as the values they point to (or ‘null‘ if the pointer is nil).

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

To decode JSON data we use the Unmarshal function.

func Unmarshal(data []byte, v interface{}) error

We must first create a place where the decoded data will be stored

var m Message

and call json.Unmarshal, passing it a []byte of JSON data and a pointer to m

err := json.Unmarshal(b, &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,
}

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

  • An exported field with a tag of "Foo" (see the Go spec for more on struct tags),
  • An exported field named "Foo", or
  • An exported field named "FOO" or "FoO" or some other case-insensitive match of "Foo".

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)

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.

But what if you don‘t know the structure of your JSON data beforehand?

Generic JSON with 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)

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
}

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 for JSON booleans,
  • float64 for JSON numbers,
  • string for JSON strings, and
  • nil for JSON null.

Decoding arbitrary data

Consider this JSON data, stored in the variable b:

b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)

Without knowing this data‘s structure, we can decode it into an interface{} value with Unmarshal:

var f interface{}
err := json.Unmarshal(b, &f)

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

To access this data we can use a type assertion to access f‘s underlying map[string]interface{}:

m := f.(map[string]interface{})

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

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)

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
}

If there were a Bar field in the JSON object, Unmarshal would allocate a new Bar and populate it. If not, Barwould 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
}

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

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.

func NewDecoder(r io.Reader) *Decoder
func NewEncoder(w io.Writer) *Encoder

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

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

Related articles

原文地址:https://www.cnblogs.com/kaid/p/9698503.html

时间: 2024-08-30 12:48:50

21 JSON and Go的相关文章

Struts的JSON机制

Struts的JSON帮助我们自动将对象解析为JSON对象,不用我门借助第三方进行JSON的解析 .具体的使用机制如下: 1.Action类 package StrutsJSON; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import com.opensymphony.xwork2.Action; import freemark

Windows Phone 六、JSON

JSON序列化 1 public class Person 2 { 3 public int Id { get; set; } 4 public string Name { get; set; } 5 public int Age { get; set; } 6 public int Gender { get; set; } 7 } Person 1 Person zhang = new Person(); 2 zhang.Id = 1; 3 zhang.Name = "letter zhang

初识JSON

大致介绍 JSON(JavaScript Object Notation  JavaScript对象表示法),JSON是一种数据格式,不是一种编程语言.虽然它的名字中有JavaScript但是它却不属于JavaScript,就像Java和JavaScript的关系一样.而且,并不是只有JavaScript才使用它,毕竟 JSON 只是一种数据格式.很多编程语言都有针对 JSON 的解析器和序列化器. JSON是由Douglas Crockford在2001年提出,为了取代XML ▓▓▓▓▓▓ 语

基于Json序列化和反序列化通用的封装

1. 最近项目已经上线了 ,闲暇了几天 想将JSON的序列化以及反序列化进行重新的封装一下本人定义为JSONHelp,虽然Microsoft 已经做的很好了.但是我想封装一套为自己开发的项目使用.方便后期的扩展以及开发使用. 2. 什么是 JSON ? JSON:JavaScript 对象表示法(JavaScript Object Notation).JSON 是存储和交换文本信息的语法.类似 XML.JSON 比 XML 更小.更快,更易解析.  现在开发Web应用程序 JSON 是 必不可少

Json与类对象转换

Json在js,jquery中可以直接使用,比如下串: { "from":"en" ,"to":"zh" ,"trans_result": [ {"src":"today","dst":"\u4eca\u5929"} ] } 可以直接使用data.from即可获取到"en". 但是在c#中不可以.要获取值,有

[项目构建 五]babasport ajax图片上传及FastDFS入门案例.

今天来开始写图片上传的功能, 现在的图片上传都讲求 上传完成后立刻回显且页面不刷新, 这里到底是怎么做的呢? 当然是借助于ajax了, 但是ajax又不能提交表单, 这里我们还要借助一个插件: jquery.form.js剩下的一个是FastDFS, 那么什么是FastDFS呢? FastDFS是一个开源的轻量级分布式文件系统,由跟踪服务器(tracker server).存储服务器(storage server)和客户端(client)三个部分组成,主要解决了海量数据存储问题,特别适合以中小文

ajax批量删除数据

做网页经常要选择批量删除数据,基本都是异步请求批量删除,用到更多的是ajax批量删除.思路是前端ajax请求,传入ids(要删除对象id的字符串数组)到后台. 后台再遍历id,调用删除接口,删除数据.返回json给前台. 代码例子如下: 1 function deleteSaleChance() { 2 var selectedRows = $("#dg").datagrid("getSelections"); 3 if(selectedRows.length==0

微信公共平台开发5 .net

每次在于微信交互时,都要用到access_token,但是这个值限制的是有时间的,但是access_token,在以后的高级功能里面会经常用到,所以这里不得不这里对前面所讲解的access_token改造一下. 另外需要说明的是access_token是变化的,有自己的周期,官方解释为:"有效期为7200秒",这就要求我们把获得的access_token存入一个物理文件或者Application中,请求到过期后修改这些内容,需要用的时候读出来.或者是存入数据库,到期时修改改access

AngularJS的Filter用法详解

Filter简介 Filter是用来格式化数据用的. Filter的基本原型( '|' 类似于Linux中的管道模式): {{ expression | filter }} Filter可以被链式使用(即连续使用多个filter): {{ expression | filter1 | filter2 | ... }} Filter也可以指定多个参数: {{ expression | filter:argument1:argument2:... }} AngularJS内建的Filter Angu