The gob package

Gob is a serialisation technique specific to Go. It is designed to encode Go data types specifically and does not at present have support for or by any other languages. It supports all Go data types except for channels and functions. It supports integers of all types and sizes, strings and booleans, structs, arrays and slices. At present it has some problems with circular structures such as rings, but that will improve over time.

Gob encodes type information into its serialised forms. This is far more extensive than the type information in say an X.509 serialisation, but far more efficient than the type information contained in an XML document. Type information is only included once for each piece of data, but includes, for example, the names of struct fields.

This inclusion of type information makes Gob marshalling and unmarshalling fairly robust to changes or differences between the marshaller and unmarshaller. For example, a struct

  1. struct T {
  2. a int
  3. b int
  4. }

can be marshalled and then unmarshalled into a different struct

  1. struct T {
  2. b int
  3. a int
  4. }

where the order of fields has changed. It can also cope with missing fields (the values are ignored) or extra fields (the fields are left unchanged). It can cope with pointer types, so that the above struct could be unmarshalled into

  1. struct T {
  2. *a int
  3. **b int
  4. }

To some extent it can cope with type coercions so that an int field can be broadened into an int64, but not with incompatible types such as int and uint.

To use Gob to marshall a data value, you first need to create an Encoder. This takes a Writer as parameter and marshalling will be done to this write stream. The encoder has a method Encode which marshalls the value to the stream. This method can be called multiple times on multiple pieces of data. Type information for each data type is only written once, though.

You use a Decoder to unmarshall the serialised data stream. This takes a Reader and each read returns an unmarshalled data value.

A program to store gob serialised data into a file is

  1. /* SaveGob
  2. */
  3. package main
  4. import (
  5. "fmt"
  6. "os"
  7. "encoding/gob"
  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. saveGob("person.gob", person)
  27. }
  28. func saveGob(fileName string, key interface{}) {
  29. outFile, err := os.Create(fileName)
  30. checkError(err)
  31. encoder := gob.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. /* LoadGob
  2. */
  3. package main
  4. import (
  5. "fmt"
  6. "os"
  7. "encoding/gob"
  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. loadGob("person.gob", &person)
  31. fmt.Println("Person", person.String())
  32. }
  33. func loadGob(fileName string, key interface{}) {
  34. inFile, err := os.Open(fileName)
  35. checkError(err)
  36. decoder := gob.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. }

A client and server

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

  1. /* Gob EchoClient
  2. */
  3. package main
  4. import (
  5. "fmt"
  6. "net"
  7. "os"
  8. "encoding/gob"
  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 := gob.NewEncoder(conn)
  44. decoder := gob.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 corrsponding server is

  1. /* Gob EchoServer
  2. */
  3. package main
  4. import (
  5. "fmt"
  6. "net"
  7. "os"
  8. "encoding/gob"
  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 := gob.NewEncoder(conn)
  41. decoder := gob.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. }