JSON

JSON stands for JavaScript Object Notation. It was designed to be a lightweight means of passing data between JavaScript systems. It uses a text-based format and is sufficiently general that it has become used as a general purpose serialisation method for many programming languages.

JSON serialises objects, arrays and basic values. The basic values include string, number, boolean values and the null value. Arrays are a comma-separated list of values that can represent arrays, vectors, lists or sequences of various programming languages. They are delimited by square brackets [ ... ]. Objects are represented by a list of “field: value” pairs enclosed in curly braces { ... }.

For example, the table of employees given earlier could be written as an array of employee objects:

  1. [
  2. {Name: fred, Occupation: programmer},
  3. {Name: liping, Occupation: analyst},
  4. {Name: sureerat, Occupation: manager}
  5. ]

There is no special support for complex data types such as dates, no distinction between number types, no recursive types, etc. JSON is a very simple language, but nevertheless can be quite useful. Its text-based format makes it easy for people to use, even though it has the overheads of string handling.

From the Go JSON package specification, marshalling uses the following type-dependent default encodings:

  • Boolean values encode as JSON booleans.
  • Floating point and integer values encode as JSON numbers.
  • String values encode as JSON strings, with each invalid UTF-8 sequence replaced by the encoding of the Unicode replacement character U+FFFD.
  • Array and slice values encode as JSON arrays, except that []byte encodes as a base64-encoded string.
  • Struct values encode as JSON objects. Each struct field becomes a member of the object. By default the object’s key name is the struct field name converted to lower case. If the struct field has a tag, that tag will be used as the name instead.
  • Map values encode as JSON objects. The map’s key type must be string; the object keys are used directly as map keys.
  • Pointer values encode as the value pointed to. (Note: this allows trees, but not graphs!). A nil pointer encodes as the null JSON object.
  • Interface values encode as the value contained in the interface. A nil interface value encodes as the null JSON object.
  • Channel, complex, and function values cannot be encoded in JSON. Attempting to encode such a value cause Marshal to return an InvalidTypeError.
  • JSON cannot represent cyclic data structures and Marshal does not handle them. Passing cyclic structures to Marshal will result in an infinite recursion.

A program to store JSON serialised data into a file is

  1. /* SaveJSON
  2. */
  3. package main
  4. import (
  5. "encoding/json"
  6. "fmt"
  7. "os"
  8. )
  9. type Person struct {
  10. Name Name
  11. Email []Email
  12. }
  13. type Name struct {
  14. Family string
  15. Personal string
  16. }
  17. type Email struct {
  18. Kind string
  19. Address string
  20. }
  21. func main() {
  22. person := Person{
  23. Name: Name{Family: "Newmarch", Personal: "Jan"},
  24. Email: []Email{Email{Kind: "home", Address: "jan@newmarch.name"},
  25. Email{Kind: "work", Address: "j.newmarch@boxhill.edu.au"}}}
  26. saveJSON("person.json", person)
  27. }
  28. func saveJSON(fileName string, key interface{}) {
  29. outFile, err := os.Create(fileName)
  30. checkError(err)
  31. encoder := json.NewEncoder(outFile)
  32. err = encoder.Encode(key)
  33. checkError(err)
  34. outFile.Close()
  35. }
  36. func checkError(err error) {
  37. if err != nil {
  38. fmt.Println("Fatal error ", err.Error())
  39. os.Exit(1)
  40. }
  41. }

and to load it back into memory is

  1. /* LoadJSON
  2. */
  3. package main
  4. import (
  5. "encoding/json"
  6. "fmt"
  7. "os"
  8. )
  9. type Person struct {
  10. Name Name
  11. Email []Email
  12. }
  13. type Name struct {
  14. Family string
  15. Personal string
  16. }
  17. type Email struct {
  18. Kind string
  19. Address string
  20. }
  21. func (p Person) String() string {
  22. s := p.Name.Personal + " " + p.Name.Family
  23. for _, v := range p.Email {
  24. s += "\n" + v.Kind + ": " + v.Address
  25. }
  26. return s
  27. }
  28. func main() {
  29. var person Person
  30. loadJSON("person.json", &person)
  31. fmt.Println("Person", person.String())
  32. }
  33. func loadJSON(fileName string, key interface{}) {
  34. inFile, err := os.Open(fileName)
  35. checkError(err)
  36. decoder := json.NewDecoder(inFile)
  37. err = decoder.Decode(key)
  38. checkError(err)
  39. inFile.Close()
  40. }
  41. func checkError(err error) {
  42. if err != nil {
  43. fmt.Println("Fatal error ", err.Error())
  44. os.Exit(1)
  45. }
  46. }

The serialised form is (formatted nicely)

  1. {"Name":{"Family":"Newmarch",
  2. "Personal":"Jan"},
  3. "Email":[{"Kind":"home","Address":"jan@newmarch.name"},
  4. {"Kind":"work","Address":"j.newmarch@boxhill.edu.au"}
  5. ]
  6. }

A client and server

A client to send a person’s data and read it back ten times is

  1. /* JSON EchoClient
  2. */
  3. package main
  4. import (
  5. "fmt"
  6. "net"
  7. "os"
  8. "encoding/json"
  9. "bytes"
  10. "io"
  11. )
  12. type Person struct {
  13. Name Name
  14. Email []Email
  15. }
  16. type Name struct {
  17. Family string
  18. Personal string
  19. }
  20. type Email struct {
  21. Kind string
  22. Address string
  23. }
  24. func (p Person) String() string {
  25. s := p.Name.Personal + " " + p.Name.Family
  26. for _, v := range p.Email {
  27. s += "\n" + v.Kind + ": " + v.Address
  28. }
  29. return s
  30. }
  31. func main() {
  32. person := Person{
  33. Name: Name{Family: "Newmarch", Personal: "Jan"},
  34. Email: []Email{Email{Kind: "home", Address: "jan@newmarch.name"},
  35. Email{Kind: "work", Address: "j.newmarch@boxhill.edu.au"}}}
  36. if len(os.Args) != 2 {
  37. fmt.Println("Usage: ", os.Args[0], "host:port")
  38. os.Exit(1)
  39. }
  40. service := os.Args[1]
  41. conn, err := net.Dial("tcp", service)
  42. checkError(err)
  43. encoder := json.NewEncoder(conn)
  44. decoder := json.NewDecoder(conn)
  45. for n := 0; n < 10; n++ {
  46. encoder.Encode(person)
  47. var newPerson Person
  48. decoder.Decode(&newPerson)
  49. fmt.Println(newPerson.String())
  50. }
  51. os.Exit(0)
  52. }
  53. func checkError(err error) {
  54. if err != nil {
  55. fmt.Println("Fatal error ", err.Error())
  56. os.Exit(1)
  57. }
  58. }

and the corresponding server is

  1. /* JSON EchoServer
  2. */
  3. package main
  4. import (
  5. "fmt"
  6. "net"
  7. "os"
  8. "encoding/json"
  9. )
  10. type Person struct {
  11. Name Name
  12. Email []Email
  13. }
  14. type Name struct {
  15. Family string
  16. Personal string
  17. }
  18. type Email struct {
  19. Kind string
  20. Address string
  21. }
  22. func (p Person) String() string {
  23. s := p.Name.Personal + " " + p.Name.Family
  24. for _, v := range p.Email {
  25. s += "\n" + v.Kind + ": " + v.Address
  26. }
  27. return s
  28. }
  29. func main() {
  30. service := "0.0.0.0:1200"
  31. tcpAddr, err := net.ResolveTCPAddr("tcp", service)
  32. checkError(err)
  33. listener, err := net.ListenTCP("tcp", tcpAddr)
  34. checkError(err)
  35. for {
  36. conn, err := listener.Accept()
  37. if err != nil {
  38. continue
  39. }
  40. encoder := json.NewEncoder(conn)
  41. decoder := json.NewDecoder(conn)
  42. for n := 0; n < 10; n++ {
  43. var person Person
  44. decoder.Decode(&person)
  45. fmt.Println(person.String())
  46. encoder.Encode(person)
  47. }
  48. conn.Close() // we're finished
  49. }
  50. }
  51. func checkError(err error) {
  52. if err != nil {
  53. fmt.Println("Fatal error ", err.Error())
  54. os.Exit(1)
  55. }
  56. }