diff options
author | Francisco Souza <franciscossouza@gmail.com> | 2012-03-22 18:25:40 +1100 |
---|---|---|
committer | Andrew Gerrand <adg@golang.org> | 2012-03-22 18:25:40 +1100 |
commit | 289a357104854d5f58102b05b40154c727657408 (patch) | |
tree | a46d45d8758d4dc92dd85fe675ec18b83733afb7 | |
parent | cec67568e98d884b9cc2a9be88c8306fd2556800 (diff) | |
download | go-289a357104854d5f58102b05b40154c727657408.tar.gz go-289a357104854d5f58102b05b40154c727657408.zip |
doc: add JSON and Go article
Originally published on The Go Programming Language Blog, January 25, 2011.
http://blog.golang.org/2011/01/json-and-go.html
R=adg
CC=golang-dev
https://golang.org/cl/5846044
-rw-r--r-- | doc/Makefile | 1 | ||||
-rw-r--r-- | doc/articles/json_and_go.html | 356 | ||||
-rw-r--r-- | doc/docs.html | 2 | ||||
-rw-r--r-- | doc/progs/json1.go | 88 | ||||
-rw-r--r-- | doc/progs/json2.go | 42 | ||||
-rw-r--r-- | doc/progs/json3.go | 73 | ||||
-rw-r--r-- | doc/progs/json4.go | 45 | ||||
-rw-r--r-- | doc/progs/json5.go | 31 | ||||
-rwxr-xr-x | doc/progs/run | 14 | ||||
-rw-r--r-- | src/pkg/encoding/json/encode.go | 2 |
10 files changed, 651 insertions, 3 deletions
diff --git a/doc/Makefile b/doc/Makefile index 547a18bb47..da29e600b3 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -11,6 +11,7 @@ RAWHTML=\ articles/go_concurrency_patterns_timing_out_moving_on.rawhtml\ articles/godoc_documenting_go_code.rawhtml\ articles/gobs_of_data.rawhtml\ + articles/json_and_go.rawhtml\ articles/image_draw.rawhtml\ effective_go.rawhtml\ go1.rawhtml\ diff --git a/doc/articles/json_and_go.html b/doc/articles/json_and_go.html new file mode 100644 index 0000000000..af7776c0a4 --- /dev/null +++ b/doc/articles/json_and_go.html @@ -0,0 +1,356 @@ +<!--{ +"Title": "JSON and Go", +"Template": true +}--> + +<p> +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, +<a href="http://json.org">json.org</a>, provides a wonderfully clear and concise +definition of the standard. +</p> + +<p> +With the <a href="/pkg/encoding/json/">json package</a> it's a snap to read and +write JSON data from your Go programs. +</p> + +<p> +<b>Encoding</b> +</p> + +<p> +To encode JSON data we use the +<a href="/pkg/encoding/json/#Marshal"><code>Marshal</code></a> function. +</p> + +<pre> +func Marshal(v interface{}) ([]byte, error) +</pre> + +<p> +Given the Go data structure, <code>Message</code>, +</p> + +{{code "/doc/progs/json1.go" `/type Message/` `/STOP/`}} + +<p> +and an instance of <code>Message</code> +</p> + +{{code "/doc/progs/json1.go" `/m :=/`}} + +<p> +we can marshal a JSON-encoded version of m using <code>json.Marshal</code>: +</p> + +{{code "/doc/progs/json1.go" `/b, err :=/`}} + +<p> +If all is well, <code>err</code> will be <code>nil</code> and <code>b</code> +will be a <code>[]byte</code> containing this JSON data: +</p> + +<pre> +b == []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`) +</pre> + +<p> +Only data structures that can be represented as valid JSON will be encoded: +</p> + +<ul> +<li> +JSON objects only support strings as keys; to encode a Go map type it must be +of the form <code>map[string]T</code> (where <code>T</code> is any Go type +supported by the json package). +</li> +<li> +Channel, complex, and function types cannot be encoded. +</li> +<li> +Cyclic data structures are not supported; they will cause <code>Marshal</code> +to go into an infinite loop. +</li> +<li> +Pointers will be encoded as the values they point to (or 'null' if the pointer +is <code>nil</code>). +</li> +</ul> + +<p> +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. +</p> + +<p> +<b>Decoding</b> +</p> + +<p> +To decode JSON data we use the +<a href="/pkg/encoding/json/#Unmarshal"><code>Unmarshal</code></a> function. +</p> + +<pre> +func Unmarshal(data []byte, v interface{}) error +</pre> + +<p> +We must first create a place where the decoded data will be stored +</p> + +{{code "/doc/progs/json1.go" `/var m Message/`}} + +<p> +and call <code>json.Unmarshal</code>, passing it a <code>[]byte</code> of JSON +data and a pointer to <code>m</code> +</p> + +{{code "/doc/progs/json1.go" `/err := json.Unmarshal/`}} + +<p> +If <code>b</code> contains valid JSON that fits in <code>m</code>, after the +call <code>err</code> will be <code>nil</code> and the data from <code>b</code> +will have been stored in the struct <code>m</code>, as if by an assignment +like: +</p> + +{{code "/doc/progs/json1.go" `/m = Message/` `/STOP/`}} + +<p> +How does <code>Unmarshal</code> identify the fields in which to store the +decoded data? For a given JSON key <code>"Foo"</code>, <code>Unmarshal</code> +will look through the destination struct's fields to find (in order of +preference): +</p> + +<ul> +<li> +An exported field with a tag of <code>"Foo"</code> (see the +<a href="/ref/spec#Struct_types">Go spec</a> for more on struct tags), +</li> +<li> +An exported field named <code>"Foo"</code>, or +</li> +<li> +An exported field named <code>"FOO"</code> or <code>"FoO"</code> or some other +case-insensitive match of <code>"Foo"</code>. +</li> +</ul> + +<p> +What happens when the structure of the JSON data doesn't exactly match the Go +type? +</p> + +{{code "/doc/progs/json1.go" `/"Food":"Pickle"/` `/STOP/`}} + +<p> +<code>Unmarshal</code> 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 <code>Unmarshal</code>. +</p> + +<p> +But what if you don't know the structure of your JSON data beforehand? +</p> + +<p> +<b>Generic JSON with interface{}</b> +</p> + +<p> +The <code>interface{}</code> (empty interface) type describes an interface with +zero methods. Every Go type implements at least zero methods and therefore +satisfies the empty interface. +</p> + +<p> +The empty interface serves as a general container type: +</p> + +{{code "/doc/progs/json2.go" `/var i interface{}/` `/STOP/`}} + +<p> +A type assertion accesses the underlying concrete type: +</p> + +{{code "/doc/progs/json2.go" `/r := i/` `/STOP/`}} + +<p> +Or, if the underlying type is unknown, a type switch determines the type: +</p> + +{{code "/doc/progs/json2.go" `/switch v/` `/STOP/`}} + + +The json package uses <code>map[string]interface{}</code> and +<code>[]interface{}</code> values to store arbitrary JSON objects and arrays; +it will happily unmarshal any valid JSON blob into a plain +<code>interface{}</code> value. The default concrete Go types are: + +<ul> +<li> +<code>bool</code> for JSON booleans, +</li> +<li> +<code>float64</code> for JSON numbers, +</li> +<li> +<code>string</code> for JSON strings, and +</li> +<li> +<code>nil</code> for JSON null. +</li> +</ul> + +<p> +<b>Decoding arbitrary data</b> +</p> + +<p> +Consider this JSON data, stored in the variable <code>b</code>: +</p> + +{{code "/doc/progs/json3.go" `/b :=/`}} + +<p> +Without knowing this data's structure, we can decode it into an +<code>interface{}</code> value with <code>Unmarshal</code>: +</p> + +{{code "/doc/progs/json3.go" `/var f interface/` `/STOP/`}} + +<p> +At this point the Go value in <code>f</code> would be a map whose keys are +strings and whose values are themselves stored as empty interface values: +</p> + +{{code "/doc/progs/json3.go" `/f = map/` `/STOP/`}} + +<p> +To access this data we can use a type assertion to access <code>f</code>'s +underlying <code>map[string]interface{}</code>: +</p> + +{{code "/doc/progs/json3.go" `/m := f/`}} + +<p> +We can then iterate through the map with a range statement and use a type switch +to access its values as their concrete types: +</p> + +{{code "/doc/progs/json3.go" `/for k, v/` `/STOP/`}} + +<p> +In this way you can work with unknown JSON data while still enjoying the +benefits of type safety. +</p> + +<p> +<b>Reference Types</b> +</p> + +<p> +Let's define a Go type to contain the data from the previous example: +</p> + +{{code "/doc/progs/json4.go" `/type FamilyMember/` `/STOP/`}} + +{{code "/doc/progs/json4.go" `/var m FamilyMember/` `/STOP/`}} + +<p> +Unmarshaling that data into a <code>FamilyMember</code> value works as +expected, but if we look closely we can see a remarkable thing has happened. +With the var statement we allocated a <code>FamilyMember</code> struct, and +then provided a pointer to that value to <code>Unmarshal</code>, but at that +time the <code>Parents</code> field was a <code>nil</code> slice value. To +populate the <code>Parents</code> field, <code>Unmarshal</code> allocated a new +slice behind the scenes. This is typical of how <code>Unmarshal</code> works +with the supported reference types (pointers, slices, and maps). +</p> + +<p> +Consider unmarshaling into this data structure: +</p> + +<pre> +type Foo struct { + Bar *Bar +} +</pre> + +<p> +If there were a <code>Bar</code> field in the JSON object, +<code>Unmarshal</code> would allocate a new <code>Bar</code> and populate it. +If not, <code>Bar</code> would be left as a <code>nil</code> pointer. +</p> + +<p> +From this a useful pattern arises: if you have an application that receives a +few distinct message types, you might define "receiver" structure like +</p> + +<pre> +type IncomingMessage struct { + Cmd *Command + Msg *Message +} +</pre> + +<p> +and the sending party can populate the <code>Cmd</code> field and/or the +<code>Msg</code> field of the top-level JSON object, depending on the type of +message they want to communicate. <code>Unmarshal</code>, when decoding the +JSON into an <code>IncomingMessage</code> struct, will only allocate the data +structures present in the JSON data. To know which messages to process, the +programmer need simply test that either <code>Cmd</code> or <code>Msg</code> is +not <code>nil</code>. +</p> + +<p> +<b>Streaming Encoders and Decoders</b> +</p> + +<p> +The json package provides <code>Decoder</code> and <code>Encoder</code> types +to support the common operation of reading and writing streams of JSON data. +The <code>NewDecoder</code> and <code>NewEncoder</code> functions wrap the +<a href="/pkg/io/#Reader"><code>io.Reader</code></a> and +<a href="/pkg/io/#Writer"><code>io.Writer</code></a> interface types. +</p> + +<pre> +func NewDecoder(r io.Reader) *Decoder +func NewEncoder(w io.Writer) *Encoder +</pre> + +<p> +Here's an example program that reads a series of JSON objects from standard +input, removes all but the <code>Name</code> field from each object, and then +writes the objects to standard output: +</p> + +{{code "/doc/progs/json5.go" `/package main/` `$`}} + +<p> +Due to the ubiquity of Readers and Writers, these <code>Encoder</code> and +<code>Decoder</code> types can be used in a broad range of scenarios, such as +reading and writing to HTTP connections, WebSockets, or files. +</p> + +<p> +<b>References</b> +</p> + +<p> +For more information see the <a href="/pkg/encoding/json/">json package documentation</a>. For an example usage of +json see the source files of the <a href="/pkg/net/rpc/jsonrpc/">jsonrpc package</a>. +</p> diff --git a/doc/docs.html b/doc/docs.html index 12afb7f656..a75ae56cf2 100644 --- a/doc/docs.html +++ b/doc/docs.html @@ -113,7 +113,7 @@ Guided tours of Go programs. <h4>Packages</h4> <ul> -<li><a href="http://blog.golang.org/2011/01/json-and-go.html">JSON and Go</a> - using the <a href="/pkg/encoding/json/">json</a> package.</li> +<li><a href="/doc/articles/json_and_go.html">JSON and Go</a> - using the <a href="/pkg/encoding/json/">json</a> package.</li> <li><a href="/doc/articles/gobs_of_data.html">Gobs of data</a> - the design and use of the <a href="/pkg/encoding/gob/">gob</a> package.</li> <li><a href="/doc/articles/laws_of_reflection.html">The Laws of Reflection</a> - the fundamentals of the <a href="/pkg/reflect/">reflect</a> package.</li> <li><a href="http://blog.golang.org/2011/09/go-image-package.html">The Go image package</a> - the fundamentals of the <a href="/pkg/image/">image</a> package.</li> diff --git a/doc/progs/json1.go b/doc/progs/json1.go new file mode 100644 index 0000000000..9e10f4743d --- /dev/null +++ b/doc/progs/json1.go @@ -0,0 +1,88 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "encoding/json" + "log" + "reflect" +) + +type Message struct { + Name string + Body string + Time int64 +} + +// STOP OMIT + +func Encode() { + m := Message{"Alice", "Hello", 1294706395881547000} + b, err := json.Marshal(m) + + if err != nil { + panic(err) + } + + expected := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`) + if !reflect.DeepEqual(b, expected) { + log.Panicf("Error marshalling %q, expected %q, got %q.", m, expected, b) + } + +} + +func Decode() { + b := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`) + var m Message + err := json.Unmarshal(b, &m) + + if err != nil { + panic(err) + } + + expected := Message{ + Name: "Alice", + Body: "Hello", + Time: 1294706395881547000, + } + + if !reflect.DeepEqual(m, expected) { + log.Panicf("Error unmarshalling %q, expected %q, got %q.", b, expected, m) + } + + m = Message{ + Name: "Alice", + Body: "Hello", + Time: 1294706395881547000, + } + + // STOP OMIT +} + +func PartialDecode() { + b := []byte(`{"Name":"Bob","Food":"Pickle"}`) + var m Message + err := json.Unmarshal(b, &m) + + // STOP OMIT + + if err != nil { + panic(err) + } + + expected := Message{ + Name: "Bob", + } + + if !reflect.DeepEqual(expected, m) { + log.Panicf("Error unmarshalling %q, expected %q, got %q.", b, expected, m) + } +} + +func main() { + Encode() + Decode() + PartialDecode() +} diff --git a/doc/progs/json2.go b/doc/progs/json2.go new file mode 100644 index 0000000000..6089ae6710 --- /dev/null +++ b/doc/progs/json2.go @@ -0,0 +1,42 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "math" +) + +func InterfaceExample() { + var i interface{} + i = "a string" + i = 2011 + i = 2.777 + + // STOP OMIT + + r := i.(float64) + fmt.Println("the circle's area", math.Pi*r*r) + + // STOP OMIT + + 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 + } + + // STOP OMIT +} + +func main() { + InterfaceExample() +} diff --git a/doc/progs/json3.go b/doc/progs/json3.go new file mode 100644 index 0000000000..a04fdfa506 --- /dev/null +++ b/doc/progs/json3.go @@ -0,0 +1,73 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "encoding/json" + "fmt" + "log" + "reflect" +) + +func Decode() { + b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`) + + var f interface{} + err := json.Unmarshal(b, &f) + + // STOP OMIT + + if err != nil { + panic(err) + } + + expected := map[string]interface{}{ + "Name": "Wednesday", + "Age": float64(6), + "Parents": []interface{}{ + "Gomez", + "Morticia", + }, + } + + if !reflect.DeepEqual(f, expected) { + log.Panicf("Error unmarshalling %q, expected %q, got %q", b, expected, f) + } + + f = map[string]interface{}{ + "Name": "Wednesday", + "Age": 6, + "Parents": []interface{}{ + "Gomez", + "Morticia", + }, + } + + // STOP OMIT + + m := f.(map[string]interface{}) + + 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") + } + } + + // STOP OMIT +} + +func main() { + Decode() +} diff --git a/doc/progs/json4.go b/doc/progs/json4.go new file mode 100644 index 0000000000..4926302206 --- /dev/null +++ b/doc/progs/json4.go @@ -0,0 +1,45 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "encoding/json" + "log" + "reflect" +) + +type FamilyMember struct { + Name string + Age int + Parents []string +} + +// STOP OMIT + +func Decode() { + b := []byte(`{"Name":"Bob","Age":20,"Parents":["Morticia", "Gomez"]}`) + var m FamilyMember + err := json.Unmarshal(b, &m) + + // STOP OMIT + + if err != nil { + panic(err) + } + + expected := FamilyMember{ + Name: "Bob", + Age: 20, + Parents: []string{"Morticia", "Gomez"}, + } + + if !reflect.DeepEqual(expected, m) { + log.Panicf("Error unmarshalling %q, expected %q, got %q", b, expected, m) + } +} + +func main() { + Decode() +} diff --git a/doc/progs/json5.go b/doc/progs/json5.go new file mode 100644 index 0000000000..6d7a4ca8c4 --- /dev/null +++ b/doc/progs/json5.go @@ -0,0 +1,31 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +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) + } + } +} diff --git a/doc/progs/run b/doc/progs/run index 1c1ac4322a..8348a33e56 100755 --- a/doc/progs/run +++ b/doc/progs/run @@ -51,7 +51,15 @@ gobs=" gobs2 " -all=$(echo $defer_panic_recover $effective_go $error_handling $law_of_reflection $c_go_cgo $timeout $gobs slices go1) +json=" + json1 + json2 + json3 + json4 + json5 +" + +all=$(echo $defer_panic_recover $effective_go $error_handling $law_of_reflection $c_go_cgo $timeout $gobs $json slices go1) for i in $all; do go build $i.go @@ -79,5 +87,9 @@ testit eff_sequence '^\[-1 2 6 16 44\]$' testit go1 '^Christmas is a holiday: true Sleeping for 0.123s.*go1.go already exists$' testit interface2 "^type: float64$" +testit json1 "^$" +testit json2 "the reciprocal of i is" +testit json3 "Age is int 6" +testit json4 "^$" rm -f $all "$TMPFILE" diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go index edbafcf65f..14957b8487 100644 --- a/src/pkg/encoding/json/encode.go +++ b/src/pkg/encoding/json/encode.go @@ -6,7 +6,7 @@ // RFC 4627. // // See "JSON and Go" for an introduction to this package: -// http://blog.golang.org/2011/01/json-and-go.html +// http://golang.org/doc/articles/json_and_go.html package json import ( |