Movatterモバイル変換


[0]ホーム

URL:


The Go Blog

JSON and Go

Andrew Gerrand
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 JavaScriptprograms 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 thejson package it’s asnap to read and write JSON data from your Go programs.

Encoding

To encode JSON data we use theMarshal 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 ofMessage

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

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

b, err := json.Marshal(m)

If all is well,err will benil andb 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 formmap[string]T (whereTis any Go type supported by the json package).

  • Channel, complex, and function types cannot be encoded.

  • Cyclic data structures are not supported; they will causeMarshal to go into an infinite loop.

  • Pointers will be encoded as the values they point to (or ’null’ if the pointer isnil).

The json package only accesses the exported fields of struct types (thosethat begin with an uppercase letter).Therefore only the exported fields of a struct will be present in the JSON output.

Decoding

To decode JSON data we use theUnmarshal 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 calljson.Unmarshal, passing it a[]byte of JSON data and a pointer tom

err := json.Unmarshal(b, &m)

Ifb contains valid JSON that fits inm,after the callerr will benil and the data fromb will have beenstored in the structm,as if by an assignment like:

m = Message{    Name: "Alice",    Body: "Hello",    Time: 1294706395881547000,}

How doesUnmarshal 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 (inorder of preference):

  • An exported field with a tag of"Foo" (see theGo specfor 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 Messageerr := 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 specificfields out of a large JSON blob.It also means that any unexported fields in the destination struct willbe unaffected byUnmarshal.

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

Generic JSON with interface

Theinterface{} (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)

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 usesmap[string]interface{} and[]interface{} values to store arbitrary JSON objects and arrays;it will happily unmarshal any valid JSON blob into a plaininterface{} 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 variableb:

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

Without knowing this data’s structure, we can decode it into aninterface{} value withUnmarshal:

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

At this point the Go value inf would be a map whose keys are stringsand 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 accessf’s underlyingmap[string]interface{}:

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

We can then iterate through the map with a range statement and use a typeswitch 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 FamilyMembererr := json.Unmarshal(b, &m)

Unmarshaling that data into aFamilyMember value works as expected,but if we look closely we can see a remarkable thing has happened.With the var statement we allocated aFamilyMember struct,and then provided a pointer to that value toUnmarshal,but at that time theParents field was anil slice value.To populate theParents field,Unmarshal allocated a new slice behind the scenes.This is typical of howUnmarshal works with the supported reference types(pointers, slices, and maps).

Consider unmarshaling into this data structure:

type Foo struct {    Bar *Bar}

If there were aBar field in the JSON object,Unmarshal would allocate a newBar and populate it.If not,Bar would be left as anil pointer.

From this a useful pattern arises: if you have an application that receivesa few distinct message types,you might define “receiver” structure like

type IncomingMessage struct {    Cmd *Command    Msg *Message}

and the sending party can populate theCmd field and/or theMsg fieldof the top-level JSON object,depending on the type of message they want to communicate.Unmarshal, when decoding the JSON into anIncomingMessage struct,will only allocate the data structures present in the JSON data.To know which messages to process, the programmer need simply test thateitherCmd orMsg is notnil.

Streaming Encoders and Decoders

The json package providesDecoder andEncoder types to support the commonoperation of reading and writing streams of JSON data.TheNewDecoder andNewEncoder functions wrap theio.Readerandio.Writer interface types.

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

Here’s an example program that reads a series of JSON objects from standard input,removes all but theName 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)        }    }}

Due to the ubiquity of Readers and Writers,theseEncoder andDecoder 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 thejson package documentation.For an example usage of json see the source files of thejsonrpc package.

Next article:Go becomes more stable
Previous article:Go Slices: usage and internals
Blog Index

go.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic.Learn more.

[8]ページ先頭

©2009-2025 Movatter.jp