Building Web Server with Go - Part 8

· ☕ 5 min read
This is part 8 of 15 part series on building web server with go.
Checkout https://www.gophersumit.com/series/web-server/ for more.

working with JSON data

Sending and accepting JSON data is very common while building REST Services. Let's see how we can build web server in Go to work with JSON data.

encoding/json

To work wit JSON data we will be using encoding/json standard library package. We have two main constructs while working with JSON data.

Encoding

Encoding is the process of serializing data. In Go, for working with JSON, struct data is serialized in byte slice.
For serialization, we use json.Marshal() method. Let's see Marshalling in action.

First we define our struct type that will be Marshalled:

1
2
3
4
5
type Address struct {
	Street   string
	Landmark string
	Pincode  int
}

We then initialize this using struct literal:

1
2
3
4
5
a := Address{
    Street:   "Viman Nagar",
    Landmark: "Nexa",
    Pincode:  411014,
}

And for serialization we use Marshal which returns byte slice and error.

1
bytes, err := json.Marshal(a)

Decoding

Decoding/Unmarshaling is opposite of what we did above. This is used to extract value from byte slice back to struct,

To Unmarshal byte slice , we use json.Unmarshal
Complete code for Marshalling and Unmarshaling :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main

import (
	"encoding/json"
	"fmt"
	"log"
)

type Address struct {
	Street   string
	Landmark string
	Pincode  int
}

func main() {
	a := Address{
		Street:   "Viman Nagar",
		Landmark: "Nexa",
		Pincode:  411014,
	}

	b := Address{}

	bytes, err := json.Marshal(a)
	if err != nil {
		log.Fatalf("unable to encode")
	}
	fmt.Println(bytes)

	err = json.Unmarshal(bytes, &b)
	if err != nil {
		log.Fatalf("unable to decode")
	}
	fmt.Println(b)
}
// [123 34 83 116 114 101 101 116 34 58 34 
// 86 105 109 97 110 32 78 97 103 97 114 34 
// 44 34 76 97 110 100 109 97 114 107 34 58 
// 34 78 101 120 97 34 44 34 80 105 110 99 
// 111 100 101 34 58 52 49 49 48 49 52 125]
// {Viman Nagar Nexa 411014}

encoding/json package only accesses the exported fields of struct types. That is the reason we have capitalized all struct fields. If we want to serialize only few fields of struct, we should leave rest of fields as un-exported. If we Marshal and Unmarshal same code using below defined struct, pincode will not be serialized.

1
2
3
4
5
type Address struct {
	Street   string
	Landmark string
	pincode  int
}

Customizing encoding of struct using tags

The encoding of each struct field can be customized by the format string stored under the “json” key in the struct field's tag.

We can use add tags to struct field extension to easily add and customize how struct will be Marshalled. omitempty signals to leave out struct fields which are empty.

1
2
3
4
5
6
type Address struct {
	Street   string `json:"street,omitempty"`
	Landmark string `json:"landmark,omitempty"`
	Pincode  int    `json:"pin,omitempty"`
}

If we now Marshal this struct value, it will generate below JSON.
Notice how casing is small and for Pincode, we have used pin in JSON.

1
2
3
4
5
{
	"street":"xyz",
	"landmark":"abc",
	"pin":"xyz"
}

Return json from web server

Let's build a simple web server to return json data. We will use the same struct that we have defined above.

To send JSON data, first step is to set content type to application/json so that browser will treat it as JSON data. We do this by using Set method on Header of response writer.

1
w.Header().Set("Content-Type", "application/json")

To send struct as JSON data, we need to create a NewEncoder first. NewEncoder is used to write to io.Writer interface.

This encoder has Encode method which writes to writer interface. For web apps, we write to ResponseWriter interface.

Complete web server is as simple as :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
	"encoding/json"
	"net/http"
)

type Address struct {
	Street   string `json:"street,omitempty"`
	Landmark string `json:"landmark,omitempty"`
	Pincode  int    `json:"pincode,omitempty"`
}

func sendJSON(w http.ResponseWriter, r *http.Request) {
	a := Address{
		Street:   "Viman Nagar",
		Landmark: "Nexa",
		Pincode:  411014,
	}
	w.Header().Set("Content-Type", "application/json")
	encoder := json.NewEncoder(w)
	encoder.Encode(a)
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", sendJSON)
	http.ListenAndServe(":3000", mux)
}

Accepting JSON data

Most of the web-apis will require to accept json data.
Let's see how we can accept JSON data.
To accept JSON data, we first create an instance of Decoder.
decoder := json.NewDecoder(r.Body)

We then extract JSON data in struct by using Decode method.

1
2
a := Address{}
err := decoder.Decode(&a)

Complete web app code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

type Address struct {
	Street   string `json:"street,omitempty"`
	Landmark string `json:"landmark,omitempty"`
	Pincode  int    `json:"pincode,omitempty"`
}

func acceptJSON(w http.ResponseWriter, r *http.Request) {
	a := Address{}
	decoder := json.NewDecoder(r.Body)
	err := decoder.Decode(&a)
	if err != nil {
		fmt.Fprintf(w, "Error parsing json")
		return
	}
	fmt.Fprintln(w, "received :", &a)
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", acceptJSON)
	http.ListenAndServe(":3000", mux)
}


Sumit
WRITTEN BY
Sumit
Gopher


What's on this Page