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 form
map[string]T
(whereT
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 (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"
, orAn 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, andnil
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.Reader
andio.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
[8]ページ先頭