aboutsummaryrefslogtreecommitdiff
path: root/content/json-and-go.article
diff options
context:
space:
mode:
Diffstat (limited to 'content/json-and-go.article')
-rw-r--r--content/json-and-go.article171
1 files changed, 86 insertions, 85 deletions
diff --git a/content/json-and-go.article b/content/json-and-go.article
index 8e4dfcd..4287cf8 100644
--- a/content/json-and-go.article
+++ b/content/json-and-go.article
@@ -1,25 +1,26 @@
-JSON and Go
+# JSON and Go
25 Jan 2011
Tags: json, technical
+Summary: 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](http://json.org), provides a wonderfully clear and concise definition of the standard.
Andrew Gerrand
-* Introduction
+## 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, [[http://json.org][json.org]],
+Its home page, [json.org](http://json.org),
provides a wonderfully clear and concise definition of the standard.
-With the [[https://golang.org/pkg/encoding/json/][json package]] it's a
+With the [json package](https://golang.org/pkg/encoding/json/) it's a
snap to read and write JSON data from your Go programs.
-* Encoding
+## Encoding
-To encode JSON data we use the [[https://golang.org/pkg/encoding/json/#Marshal][`Marshal`]] function.
+To encode JSON data we use the [`Marshal`](https://golang.org/pkg/encoding/json/#Marshal) function.
func Marshal(v interface{}) ([]byte, error)
@@ -33,11 +34,11 @@ Given the Go data structure, `Message`,
and an instance of `Message`
- m := Message{"Alice", "Hello", 1294706395881547000}
+ m := Message{"Alice", "Hello", 1294706395881547000}
we can marshal a JSON-encoded version of m using `json.Marshal`:
- b, err := json.Marshal(m)
+ b, err := json.Marshal(m)
If all is well, `err` will be `nil` and `b` will be a `[]byte` containing this JSON data:
@@ -45,62 +46,62 @@ If all is well, `err` will be `nil` and `b` will be a `[]byte` containing this J
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).
+ - 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.
+ - Channel, complex, and function types cannot be encoded.
-- Cyclic data structures are not supported; they will cause `Marshal` to go into an infinite loop.
+ - 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`).
+ - 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
+## Decoding
-To decode JSON data we use the [[https://golang.org/pkg/encoding/json/#Unmarshal][`Unmarshal`]] function.
+To decode JSON data we use the [`Unmarshal`](https://golang.org/pkg/encoding/json/#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
+ var m Message
and call `json.Unmarshal`, passing it a `[]byte` of JSON data and a pointer to `m`
- err := json.Unmarshal(b, &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,
- }
+ 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 [[https://golang.org/ref/spec#Struct_types][Go spec]]
- for more on struct tags),
+ - An exported field with a tag of `"Foo"` (see the [Go spec](https://golang.org/ref/spec#Struct_types)
+ for more on struct tags),
-- An exported field named `"Foo"`, or
+ - An exported field named `"Foo"`, or
-- An exported field named `"FOO"` or `"FoO"` or some other case-insensitive match of `"Foo"`.
+ - 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)
+ 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,
@@ -112,99 +113,99 @@ be unaffected by `Unmarshal`.
But what if you don't know the structure of your JSON data beforehand?
-* Generic JSON with interface{}
+## 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
+ 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)
+ 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
- }
+ 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,
+ - `bool` for JSON booleans,
-- `float64` for JSON numbers,
+ - `float64` for JSON numbers,
-- `string` for JSON strings, and
+ - `string` for JSON strings, and
-- `nil` for JSON null.
+ - `nil` for JSON null.
-* Decoding arbitrary data
+## Decoding arbitrary data
Consider this JSON data, stored in the variable `b`:
- b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
+ 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)
+ 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",
- },
- }
+ 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{})
+ 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")
+ 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
+## Reference Types
Let's define a Go type to contain the data from the previous example:
@@ -253,12 +254,12 @@ 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
+## 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 [[https://golang.org/pkg/io/#Reader][`io.Reader`]]
-and [[https://golang.org/pkg/io/#Writer][`io.Writer`]] interface types.
+The `NewDecoder` and `NewEncoder` functions wrap the [`io.Reader`](https://golang.org/pkg/io/#Reader)
+and [`io.Writer`](https://golang.org/pkg/io/#Writer) interface types.
func NewDecoder(r io.Reader) *Decoder
func NewEncoder(w io.Writer) *Encoder
@@ -300,7 +301,7 @@ 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
+## References
-For more information see the [[https://golang.org/pkg/encoding/json/][json package documentation]].
-For an example usage of json see the source files of the [[https://golang.org/pkg/net/rpc/jsonrpc/][jsonrpc package]].
+For more information see the [json package documentation](https://golang.org/pkg/encoding/json/).
+For an example usage of json see the source files of the [jsonrpc package](https://golang.org/pkg/net/rpc/jsonrpc/).