]> Cypherpunks repositories - gostls13.git/commitdiff
doc: add JSON and Go article
authorFrancisco Souza <franciscossouza@gmail.com>
Thu, 22 Mar 2012 07:25:40 +0000 (18:25 +1100)
committerAndrew Gerrand <adg@golang.org>
Thu, 22 Mar 2012 07:25:40 +0000 (18:25 +1100)
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

doc/Makefile
doc/articles/json_and_go.html [new file with mode: 0644]
doc/docs.html
doc/progs/json1.go [new file with mode: 0644]
doc/progs/json2.go [new file with mode: 0644]
doc/progs/json3.go [new file with mode: 0644]
doc/progs/json4.go [new file with mode: 0644]
doc/progs/json5.go [new file with mode: 0644]
doc/progs/run
src/pkg/encoding/json/encode.go

index 547a18bb47b4bd58506f2c3f63cc59f281be2c4d..da29e600b331bd3864748a150d13e7408e607e8b 100644 (file)
@@ -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 (file)
index 0000000..af7776c
--- /dev/null
@@ -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>
index 12afb7f65695c47a71367315274569ecb13e34ba..a75ae56cf29a7b48186c018244c0f83b9d848152 100644 (file)
@@ -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 (file)
index 0000000..9e10f47
--- /dev/null
@@ -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 (file)
index 0000000..6089ae6
--- /dev/null
@@ -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 (file)
index 0000000..a04fdfa
--- /dev/null
@@ -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 (file)
index 0000000..4926302
--- /dev/null
@@ -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 (file)
index 0000000..6d7a4ca
--- /dev/null
@@ -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)
+               }
+       }
+}
index 1c1ac4322a05a461138b5b6bd7af3bed7f6298c2..8348a33e56e73a025fddd5d681ebd78855ccefd5 100755 (executable)
@@ -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"
index edbafcf65f1fa6d78e6e54b49040fe049c43d7c7..14957b8487ba1cc20e432f03202fddaeb08cf74f 100644 (file)
@@ -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 (