Go has a gigantic standard library and of course JSON encoding as well as decoding is included.
Converting a go struct to a JSON object
We first start of by defining a Go structure as a type and afterwards creating a new variable with an instance of this type…
1// main.go
2package main
3
4type Car struct {
5 Name string `json:"name"`
6 Age int `json:"age"`
7 Model string `json:"model"`
8}
9
10type Person struct {
11 Name string `json:"name"`
12 Age int `json:"age"`
13 Hometown string `json:"home_town"`
14 Cars []Car `json:"cars"`
15}
I use a fairly complex data structure here to showcase converting nested structs to json.
Tip
Notice the raw go strings behind each property?
The text in between the quotation marks tells the json parser / generator which Go structure key has what json object key.
1type TestStruct struct {
2 TestName string `json:"name"`
3}
4test := TestStruct{ TestName: "text" }
Converting the above to json, yields the result below:
1{
2 "name": "text"
3}
To convert structures to json objects we will need the encoding/json
package imported from the standard library.:
1// main.go
2package main
3
4import (
5 "encoding/json"
6)
7
8type Car struct {
9 Name string `json:"name"`
10 Age int `json:"age"`
11 Model string `json:"model"`
12}
13
14type Person struct {
15 Name string `json:"name"`
16 Age int `json:"age"`
17 Hometown string `json:"home_town"`
18 Cars []Car `json:"cars"`
19}
Now we create a new variable of type Person and assign some data to it:
1// main.go
2package main
3
4import (
5 "encoding/json"
6)
7
8type Car struct {
9 Name string `json:"name"`
10 Age int `json:"age"`
11 Model string `json:"model"`
12}
13
14type Person struct {
15 Name string `json:"name"`
16 Age int `json:"age"`
17 Hometown string `json:"home_town"`
18 Cars []Car `json:"cars"`
19}
20
21func main(){
22 data := Person{
23 Name: "John",
24 Age: 30,
25 Hometown: "New York",
26 Cars: []Car{
27 {Name: "Ford", Age: 2014, Model: "F150"},
28 {Name: "Mercedes", Age: 2003, Model: "R class"},
29 },
30 }
31}
To convert this Person
structure to json we call the json.Marshall
function which returns the converted json as a bytes array and print it as a string:
1// main.go
2package main
3
4import (
5 "encoding/json"
6)
7
8type Car struct {
9 Name string `json:"name"`
10 Age int `json:"age"`
11 Model string `json:"model"`
12}
13
14type Person struct {
15 Name string `json:"name"`
16 Age int `json:"age"`
17 Hometown string `json:"home_town"`
18 Cars []Car `json:"cars"`
19}
20
21func main(){
22 data := Person{
23 Name: "John",
24 Age: 30,
25 Hometown: "New York",
26 Cars: []Car{
27 {Name: "Ford", Age: 2014, Model: "F150"},
28 {Name: "Mercedes", Age: 2003, Model: "R class"},
29 },
30 }
31
32 res, err := json.Marshal(data)
33
34 if err != nil {
35 log.Fatalln("failed to convert struct to json", err)
36 }
37
38 log.Println(string(res))
39}
Of course we need to handle the error json.Marshal
could return if converting the structure to a json object failed.
Running the above results in the following output:
1$ go run .
22023/01/27 13:02:45 {"name":"John","age":30,"home_town":"New York","cars":[{"name":"Ford","age":2014,"model":"F150"},{"name":"Mercedes","age":2003,"model":"R class"}]}
The pretty printed json looks like this:
1{
2 "name": "John",
3 "age": 30,
4 "home_town": "New York",
5 "cars": [
6 { "name": "Ford", "age": 2014, "model": "F150" },
7 { "name": "Mercedes", "age": 2003, "model": "R class" }
8 ]
9}
Converting a JSON object to a go struct
To reverse the previously generated json back to a struct we use the json.Unmarshal
method…
1package main
2
3import (
4 "encoding/json"
5 "log"
6)
7
8type Car struct {
9 Name string `json:"name"`
10 Age int `json:"age"`
11 Model string `json:"model"`
12}
13
14type Person struct {
15 Name string `json:"name"`
16 Age int `json:"age"`
17 Hometown string `json:"home_town"`
18 Cars []Car `json:"cars"`
19}
20
21func main() {
22 data := Person{
23 Name: "John",
24 Age: 30,
25 Hometown: "New York",
26 Cars: []Car{
27 {Name: "Ford", Age: 2014, Model: "F150"},
28 {Name: "Mercedes", Age: 2003, Model: "R class"},
29 },
30 }
31
32 res, err := json.Marshal(data)
33
34 if err != nil {
35 log.Fatalln("failed to convert struct to json", err)
36 }
37
38 log.Println(string(res))
39
40 newPerson := Person{}
41 err = json.Unmarshal(res, &newPerson)
42
43 if err != nil {
44 log.Fatalln("failed to convert json back to struct", err)
45 }
46
47 log.Println(newPerson)
48}
To use the unmarshal function we have to pass it a byte array (res []byte
) as the first parameter and provide a reference to a variable
we want the json to be converted into as the second parameter (&newPerson &Person
). json.Unmarshal
can return an error if converting the json to a struct fails. We catch this error just like before.
At the end we print the result. The whole program now prints the following:
1$ go run .
22023/01/27 13:14:36 {"name":"John","age":30,"home_town":"New York","cars":[{"name":"Ford","age":2014,"model":"F150"},{"name":"Mercedes","age":2003,"model":"R class"}]}
32023/01/27 13:14:36 {John 30 New York [{Ford 2014 F150} {Mercedes 2003 R class}]}
HTTP server example
The following is a simple http server setup in ~50 lines, which returns the requester a go structure as a JSON string.
1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "io"
7 "log"
8 "net/http"
9 "time"
10)
11
12const PORT = "8080"
13
14// define the data we want to convert to json using the raw strings after the struct fields
15type RequestData struct {
16 Method string `json:"method"`
17 URL string `json:"url"`
18 RemoteAddr string `json:"remote_addr"`
19 StatusCode int `json:"status_code"`
20 Status string `json:"status"`
21 RequestTime string `json:"request_time"`
22}
23
24func getRoot(w http.ResponseWriter, r *http.Request) {
25 // timestamp used for calculating the duration the response took the server to complete
26 begin := time.Now()
27
28 log.Printf("[%s] %s %s", r.Method, r.URL.Path, r.RemoteAddr)
29
30 // define variable of type RequestData and fill its fields with data
31 data := RequestData{
32 Method: r.Method,
33 URL: r.URL.Path,
34 RemoteAddr: r.RemoteAddr,
35 StatusCode: http.StatusOK,
36 Status: fmt.Sprint(http.StatusOK) + " " + http.StatusText(http.StatusOK),
37 RequestTime: fmt.Sprint(time.Since(begin).Milliseconds()) + "ms",
38 }
39
40 // convert the above variable to its json representation
41 res, err := json.Marshal(data)
42
43 // handle possible errors by returning 500 as a statuscode
44 if err != nil {
45 w.WriteHeader(http.StatusInternalServerError)
46 io.WriteString(w, http.StatusText(http.StatusInternalServerError))
47 return
48 }
49
50 w.Header().Set("Content-Type", "application/json")
51 io.WriteString(w, string(res))
52}
53
54func main() {
55 http.HandleFunc("/", getRoot)
56 log.Println("Listening on port", PORT)
57 log.Fatalln(http.ListenAndServe(":"+PORT, nil))
58}
Calling the above yields the following output:
1$ go run .
22023/01/27 13:36:56 Listening on port 8080
32023/01/27 13:37:01 [GET] / 127.0.0.1:51832
Curling the endpoints returns the following json:
1$ curl -v localhost:8080
2* Trying 127.0.0.1:8080...
3* Connected to localhost (127.0.0.1) port 8080 (#0)
4> GET / HTTP/1.1
5> Host: localhost:8080
6> User-Agent: curl/7.87.0
7> Accept: */*
8>
9* Mark bundle as not supporting multiuse
10< HTTP/1.1 200 OK
11< Content-Type: application/json
12< Date: Fri, 27 Jan 2023 12:35:33 GMT
13< Content-Length: 115
14<
15* Connection #0 to host localhost left intact
16{"method":"GET","url":"/","remote_addr":"127.0.0.1:34920","status_code":200,"status":"200 OK","request_time":"0ms"}
The pretty printed json:
1{
2 "method": "GET",
3 "url": "/",
4 "remote_addr": "127.0.0.1:34920",
5 "status_code": 200,
6 "status": "200 OK",
7 "request_time": "0ms"
8}