18. JSON and Go
18.1 JSON
JSON stands for JavaScript Object Notation.
JSON is syntax for storing and exchanging text information, much like XML. JSON is smaller than XML, and faster and easier to parse. JSON is language independent. Here’s an example:
1 {
2 "employees": [
3 { "firstName":"John" , "lastName":"Doe" },
4 { "firstName":"Anna" , "lastName":"Smith" },
5 { "firstName":"Peter" , "lastName":"Jones" }
6 ]
7 }
The employee object is an array of 3 employee records (objects).
JSON data is written as name/value pairs. A name/value pair consists of a field name (in double quotes), followed by a colon and followed by a value:
"firstName" : "Satish"
JSON values can be:
- A number (integer or floating point)
- A string (in double quotes)
- A Boolean (true or false)
- An array (in square brackets)
- An object (in curly brackets)
- null
JSON objects are written inside curly brackets, Objects can contain multiple name/values pairs:
{ "firstName":"Satish" , "lastName":"Talim" }
18.2 Package json
Usage: import "encoding/json"
Package json implements encoding and decoding of JSON objects.
18.2.1 Encoding
To encode JSON data we use the Marshal function.
func Marshal(v interface{}) ([]byte, error)
Let’s look at an example.
Program: json1.go
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 )
7
8 type User struct {
9 UserName string
10 EmailID string
11 Password string
12 }
13
14 func main() {
15 u := User{UserName: "IndianGuru", EmailID: "satishtalim@gmail.com", Password: "password"}
16 m, _ := json.Marshal(u)
17 fmt.Println(string(m))
18 }
The output is:
{"UserName":"IndianGuru","EmailID":"satishtalim@gmail.com","Password":"password"}
In the above program we have a simple struct, we create a new instance of this struct and encode it.
18.2.2 Struct tags
1 type Person struct {
2 UserName string `json:"user_name"`
3 EmailID string
4 Password string `json:"-"`
5 }
When we use the Marshal function on a struct instance it produces JSON.
In the above example, a field appears in JSON as key “user_name” and the Password field is ignored by this package.
In the previous example json1.go the password got printed. However, we don’t want to print the password. Here’s the changed code:
Program: json2.go
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 )
7
8 type User struct {
9 UserName string `json:"user_name"`
10 EmailID string
11 Password string `json:"-"`
12 }
13
14 func main() {
15 u := User{UserName: "IndianGuru", EmailID: "satishtalim@gmail.com", Password: "password"}
16 m, _ := json.Marshal(u)
17 fmt.Println(string(m))
18 }
The output is:
{"user_name":"IndianGuru","EmailID":"satishtalim@gmail.com"}
What if I don’t want to show the UserName if it is empty? Here’s the modified code:
Program: json3.go
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 )
7
8 type User struct {
9 UserName string `json:"first_name,omitempty"`
10 EmailID string
11 Password string `json:"-"`
12 }
13
14 func main() {
15 u := User{UserName: "", EmailID: "", Password: "password"}
16 m, _ := json.Marshal(u)
17 fmt.Println(string(m))
18 }
The output is:
{"EmailID":""}
The JSON parser also accepts a flag in the tag to let it know what to do if the field is empty. The omitempty flag tells it to not include the JSON value in the output if it’s the “zero-value” for that type.
The “zero-value” for numbers is 0, for strings it’s the empty string, for maps, slices and pointers it’s nil. This is how you include the omitempty flag.
1 type MyStruct struct {
2 SomeField string `json:"some_field,omitempty"`
3 }
Notice that the flag goes inside the quotes.
If the SomeField was an empty string, and you converted it to JSON, some_field wouldn’t be included in the output at all.
What if I want to retain the field name EmailID and at the same time use the omitempty flag? Here’s the modified code:
Program: json4.go
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 )
7
8 type User struct {
9 UserName string `json:"first_name,omitempty"`
10 EmailID string `json:",omitempty"`
11 Password string `json:"-"`
12 }
13
14 func main() {
15 u := User{UserName: "", EmailID: "", Password: "password"}
16 m, _ := json.Marshal(u)
17 fmt.Println(string(m))
18 }
The output is:
{}
Let’s look at another example:
Program: json5.go
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 )
7
8 type Response1 struct {
9 Page int
10 Conf []string
11 }
12
13 type Response2 struct {
14 Page int `json:"page"` // Field appears in JSON as key "page"
15 Conf []string `json:"conf"` // Field appears in JSON as key "conf"
16 }
17
18 func main() {
19 // encoding basic data types to JSON strings
20 bolB, _ := json.Marshal(false)
21 fmt.Println(string(bolB))
22
23 intB, _ := json.Marshal(3)
24 fmt.Println(string(intB))
25
26 fltB, _ := json.Marshal(87.23)
27 fmt.Println(string(fltB))
28
29 strB, _ := json.Marshal("go class")
30 fmt.Println(string(strB))
31
32 // encoding a slice
33 slcD := []string{"gophercon", "gopherconindia", "go china"}
34 slcB, _ := json.Marshal(slcD)
35 fmt.Println(string(slcB))
36
37 // encoding a map
38 // the map's key type must be a string
39 mapD := map[string]int{"pune": 5, "franklin": 7}
40 mapB, _ := json.Marshal(mapD)
41 fmt.Println(string(mapB))
42
43 // The JSON package can automatically encode your
44 // custom data types. It will only include exported
45 // fields in the encoded output and will by default
46 // use those names as the JSON keys.
47 res1D := &Response1{
48 Page: 1,
49 Conf: []string{"gophercon", "gopherconindia", "go china"}}
50 res1B, _ := json.Marshal(res1D)
51 fmt.Println(string(res1B))
52
53 // You can use tags on struct field declarations to
54 // customize the encoded JSON key names. Check the
55 // definition of Response2 above to see an example of
56 // such tags.
57 res2D := &Response2{
58 Page: 1,
59 Conf: []string{"gophercon", "gopherconindia", "go china"}}
60 res2B, _ := json.Marshal(res2D)
61 fmt.Println(string(res2B))
62
63 // The last 2 lines above can be replaced with
64 json.NewEncoder(os.Stdout).Encode(res2D)
65 }
The output is:
false
3
87.23
"go class"
["gophercon","gopherconindia","go china"]
{"franklin":7,"pune":5}
{"Page":1,"Conf":["gophercon","gopherconindia","go china"]}
{"page":1,"conf":["gophercon","gopherconindia","go china"]}
func NewEncoder(w io.Writer) *Encoder
NewEncoder returns a new encoder that writes to w.
func (enc *Encoder) Encode(v interface{}) error
Encode writes the JSON encoding of v to the stream, followed by a newline character.
|
Tip: The |
18.2.3 Decoding
To decode JSON data we use the Unmarshal function.
func Unmarshal(data []byte, v interface{}) error
We must first create a place where the decoded data will be stored.
var m Message
and call json.Unmarshal, passing it a []byte of JSON data and a pointer to m.
err := json.Unmarshal(b, &m)
If b contains valid JSON that fits in m, after the call err will be nil and the data from b will have been stored in the struct m, as if by an assignment like:
1 m = Message {
2 Name: "Alice",
3 Body: "Hello",
4 Time: 1294706395881547000,
5 }
How does Unmarshal identify the fields in which to store the decoded data? For a given JSON key “Foo”, Unmarshal will look through the destination struct’s fields to find (in order of preference):
- An exported field with a tag of “Foo” (see the Go spec for more on struct tags),
- An exported field named “Foo”, or
- An exported field named “FOO” or “FoO” or some other case-insensitive match of “Foo”.
What happens when the structure of the JSON data doesn’t exactly match the Go type?
1 b := []byte(`{"Name":"Bob","Food":"Pickle"}`)
2 var m Message
3 err := json.Unmarshal(b, &m)
Unmarshal 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 Unmarshal.
Unmarshal stores in the interface value: map[string]interface{}, for JSON objects.
Here’s an example:
Program: json6.go
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 )
7
8 type Response2 struct {
9 Page int `json:"page"`
10 Conf []string `json:"conf"`
11 }
12
13 func main() {
14 // We can also decode JSON into custom data types.
15 // This has the advantages of adding additional
16 // type-safety to our programs and eliminating the
17 // need for type assertions when accessing the decoded data.
18 // `Unmarshal` will decode only the fields that it can find
19 // in the destination type. In this case, only the Page and
20 // Conf field of res will be populated, and the Food field
21 // will be ignored.
22 str := `{"page": 1, "conf": ["gophercon", "gopherconindia"], "Food":"Pickle"}`
23 res := &Response2{}
24 json.Unmarshal([]byte(str), &res)
25 fmt.Println(res)
26 fmt.Println(res.Conf[0])
27 }
The output is:
&{1 [gophercon gopherconindia]}
gophercon
18.2.4 Streaming Encoders and Decoders
The json package provides Decoder and Encoder types to support the common operation of reading and writing streams of JSON data. The NewDecoder and NewEncoder functions wrap the io.Reader and io.Writer interface types.
func NewDecoder(r io.Reader) *Decoder
func NewEncoder(w io.Writer) *Encoder
func (enc *Encoder) Encode(v interface{}) error
Encode1 writes the JSON encoding of v to the stream, followed by a newline character.
func (dec *Decoder) Decode(v interface{}) error
Decode2 reads the next JSON-encoded value from its input and stores it in the value pointed to by v.
Previously, to encode JSON data we had used the Marshal function.
Program: json1.go
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 )
7
8 type User struct {
9 UserName string
10 EmailID string
11 Password string
12 }
13
14 func main() {
15 u := User{UserName: "IndianGuru", EmailID: "satishtalim@gmail.com", Password: "password"}
16 m, _ := json.Marshal(u)
17 fmt.Println(string(m))
18 }
The output was:
{"UserName":"IndianGuru","EmailID":"satishtalim@gmail.com","Password":"password"}
Let’s rewrite the above program using json.NewEncoder as follows:
Program: json7.go
1 package main
2
3 import (
4 "encoding/json"
5 "log"
6 "os"
7 )
8
9 type User struct {
10 UserName string
11 EmailID string
12 Password string
13 }
14
15 func main() {
16 u := User{UserName: "IndianGuru", EmailID: "satishtalim@gmail.com", Password: "password"}
17 enc := json.NewEncoder(os.Stdout)
18 if err := enc.Encode(u); err != nil {
19 log.Println(err)
20 }
21 }
The output is the same as before:
{"UserName":"IndianGuru","EmailID":"satishtalim@gmail.com","Password":"password"}
These Encoder and Decoder types can be used in a broad range of scenarios, such as reading and writing to HTTP connections, WebSockets, or files.
18.3 A Fun, Weather Forecast Go Web App
We shall build a fun Go app that displays the current weather forecast for a given city.
18.3.1 Register for an account at Forecast for Developers
Go to https://developer.forecast.io/register3 and register for an account. We shall use their free plan that allows us to use 1000 calls to their API per day. As such, there is no need to enter your credit card details. Also, this page gives you your very own API key that you will use in your program.
18.3.1.1 Study the API documentation
We need to read thro’ the API documentation4 of the Forecast for Developers.
Very briefly:
The Forecast Call
https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE?units=ca
APIKEY should be your API key as mentioned above. LATITUDE and LONGITUDE should be the geographic coordinates of a location in decimal degrees.
The response will be a JSON-formatted object with the following properties defined:
-
latitude: The requested latitude. -
longitude: The requested longitude. -
timezone: The timezone name for the requested location (e.g. America/New_York). -
offset: The current timezone offset in hours from GMT. -
currently: A data point (see below) containing the current weather conditions at the requested location.
The following JSON Schema of the API is very informative - forecast.json5
Data Points
A data point object represents the various weather phenomena occurring at a specific instant of time, and has many varied properties. All of these properties (except time) are optional, and will only be set if we have that type of information for that location and time.
The following JSON Schema of the API is very informative - datapoint.json6
We shall use the two schema (datapoint.json and forecast.json) in our program.
Let us define our structs DataPoint and Forecast based on these two schema as follows:
1 type DataPoint struct {
2 Time float64
3 Summary string
4 Icon string
5 SunriseTime float64
6 SunsetTime float64
7 PrecipIntensity float64
8 PrecipIntensityMax float64
9 PrecipIntensityMaxTime float64
10 PrecipProbability float64
11 PrecipType string
12 PrecipAccumulation float64
13 Temperature float64
14 TemperatureMin float64
15 TemperatureMinTime float64
16 TemperatureMax float64
17 TemperatureMaxTime float64
18 DewPoint float64
19 WindSpeed float64
20 WindBearing float64
21 CloudCover float64
22 Humidity float64
23 Pressure float64
24 Visibility float64
25 Ozone float64
26 }
27
28 type Forecast struct {
29 Latitude float64
30 Longitude float64
31 Timezone string
32 Offset float64
33 Currently DataPoint
34 Junk string
35 }
Understanding url query string
A query string is a part of an URL that contains data that can be passed to web applications. This data needs to be encoded, and this encoding is done using url.QueryEscape. It performs what is also commonly called URL encoding7.
1 // addr is a string which contains our address
2 addr := "Pune,India"
3
4 // QueryEscape escapes the addr string so
5 // it can be safely placed inside a URL query
6 safeAddr := url.QueryEscape(addr)
7 fmt.Println(safeAddr) // Pune%2CIndia
With url.QueryEscape our address “Pune,India” becomes “Pune%2CIndia” where %2C is the ASCII keycode in hexadecimal for a comma (,).
The Google Geocoding API
We shall use The Google Geocoding API8 to help us convert addresses (like “1600 Amphitheatre Parkway, Mountain View, CA”) into geographic coordinates (like latitude 37.423021 and longitude -122.083739).
To access the Geocoding API over HTTP, use:
http://maps.googleapis.com/maps/api/geocode/output?parameters
where output may be either of the following values:
- json (recommended) indicates output in JavaScript Object Notation (JSON)
- xml indicates output as XML
Some parameters are required while some are optional. As is standard in URLs, parameters are separated using the ampersand (&) character.
Required parameters in a geocoding request:
address — The street address that you want to geocode, in the format used by the national postal service of the country concerned. Additional address elements such as business names and unit, suite or floor numbers should be avoided.
We are not using any optional parameters in our geocoding request.
1 // Geocoding API
2 fullUrl := fmt.Sprintf("http://maps.googleapis.com/maps/api/geocode/json?address=%s", safe\
3 Addr)
4 fmt.Println(fullUrl)
The output is:
http://maps.googleapis.com/maps/api/geocode/json?address=Pune%2C+India
In your browser, open the site http://maps.googleapis.com/maps/api/geocode/json?address=Pune%2C+India the browser output is a huge blob of JSON. This may be difficult to look at in the browser, unless you have the JSONView plugin installed. These extensions are available for Firefox9 and Chrome10. With the extension installed you should be able to see a better view of the JSOn returned.
Build the http request
func NewRequest(method, urlStr string, body io.Reader) (*Request, error)
NewRequest11 returns a new Request given a method, URL, and an optional body.
1 // Build the http request
2 req, err1 := http.NewRequest("GET", fullUrl, nil)
3 check(err1, "NewRequest:")
Also, we write a check function that checks for errors, if any:
1 func check(e error, str string) {
2 if e != nil {
3 log.Fatal(str, " ", e)
4 return
5 }
6 }
Create a Client
For control over HTTP client headers, redirect policy, and other settings, create a Client12. A Client is an HTTP client:
client := &http.Client{}
Send the request via a client
func (c *Client) Do(req *Request) (resp *Response, err error)
Do sends an HTTP request and returns an HTTP response.
1 resp, err2 := client.Do(req)
2 check(err2, "Do:")
3
4 // Callers should close resp.Body
5 // when done reading from it
6 // Defer the closing of the body
7 defer resp.Body.Close()
Read the content into a byte array
func ReadAll(r io.Reader) ([]byte, error)
ReadAll13 reads from r until an error or EOF and returns the data it read. A successful call returns err == nil, not err == EOF. Because ReadAll is defined to read from src until EOF, it does not treat an EOF from Read as an error to be reported.
1 body, dataReadErr := ioutil.ReadAll(resp.Body)
2 check(dataReadErr, "ReadAll:")
Create a map
The json package uses map[string]interface{} and []interface{} values to store arbitrary JSON objects and arrays.
Observe the JSON output when you open the site http://maps.googleapis.com/maps/api/geocode/json?address=Pune%2C+India in your browser. There is a results array, within which there is geometry, then location which finally contains lat and lng.
Therefore let’s define our map as follows:
res := make(map[string][]map[string]map[string]map[string]interface{}, 0)
0 above is the initial capacity of the map.
Using the Unmarshal function
We will be using the Unmarshal function to transform our JSON bytes into the appropriate structure. The Unmarshal function accepts a byte array and a reference to the object which shall be filled with the JSON data (this is simplifying, it actually accepts an interface).
json.Unmarshal(body, &res)
Extract the latitude and logitude
1 // lat, lng as float64
2 lat, _ := res["results"][0]["geometry"]["location"]["lat"]
3 lng, _ := res["results"][0]["geometry"]["location"]["lng"]
** Use the Forecast API**
Declare a global constant APIKey as:
const APIKey string = "yourapikey"
safeLatLng := url.QueryEscape(fmt.Sprintf("%.13f,%.13f", lat, lng))
url := fmt.Sprintf("https://api.forecast.io/forecast/%s/%s?units=ca", APIKey, safeLatLng)
- Remember to replace
APIKeyabove with your actual api key. -
%.13fis used to convertfloat64to astring -
?units=ca- the API request was optionally modified through the use of query parameterunits=cawill return temperatures in degrees Celsius.
Use http.Get and ioutil.ReadAll
func Get(url string) (resp *Response, err error)
Get14 issues a GET to the specified URL.
1 resp, err := http.Get(url)
2 check(err, "Get:")
3 defer resp.Body.Close()
4
5 fbody, err := ioutil.ReadAll(resp.Body)
6 check(err, "ReadAll:")
Use the Forecast struct to get the results
1 var f Forecast
2 json.Unmarshal(fbody, &f)
3
4 fmt.Println("The Weather at ", addr)
5 fmt.Println("Timezone = ", f.Timezone)
6 fmt.Println("Temp in Celsius = ", f.Currently.Temperature)
7 fmt.Println("Summary = ", f.Currently.Summary)
Here’s the full program:
Program: weather.go
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 "io/ioutil"
7 "log"
8 "net/http"
9 "net/url"
10 )
11
12 type DataPoint struct {
13 Time float64
14 Summary string
15 Icon string
16 SunriseTime float64
17 SunsetTime float64
18 PrecipIntensity float64
19 PrecipIntensityMax float64
20 PrecipIntensityMaxTime float64
21 PrecipProbability float64
22 PrecipType string
23 PrecipAccumulation float64
24 Temperature float64
25 TemperatureMin float64
26 TemperatureMinTime float64
27 TemperatureMax float64
28 TemperatureMaxTime float64
29 DewPoint float64
30 WindSpeed float64
31 WindBearing float64
32 CloudCover float64
33 Humidity float64
34 Pressure float64
35 Visibility float64
36 Ozone float64
37 }
38
39 type Forecast struct {
40 Latitude float64
41 Longitude float64
42 Timezone string
43 Offset float64
44 Currently DataPoint
45 Junk string
46 }
47
48 // Replace the text `yourapikey` below with your actual api key
49 const APIKey string = "yourapikey"
50
51 func main() {
52 Get()
53 }
54
55 func check(e error, str string) {
56 if e != nil {
57 log.Fatal(str, " ", e)
58 return
59 }
60 }
61
62 func Get() {
63 var addr string
64
65 fmt.Println("Enter City eg. Pune, India: ")
66 fmt.Scanf("%s", &addr)
67
68 // QueryEscape escapes the addr string so
69 // it can be safely placed inside a URL query
70 safeAddr := url.QueryEscape(addr)
71
72 // Geocoding API
73 fullUrl := fmt.Sprintf("http://maps.googleapis.com/maps/api/geocode/json?address=%s", saf\
74 eAddr)
75
76 // Build the http request
77 req, err1 := http.NewRequest("GET", fullUrl, nil)
78 check(err1, "NewRequest:")
79
80 // For control over HTTP client headers,
81 // redirect policy, and other settings,
82 // create a Client
83 // A Client is an HTTP client
84 client := &http.Client{}
85
86 // Send the request via a client
87 // Do sends an HTTP request and
88 // returns an HTTP response
89 resp, err2 := client.Do(req)
90 check(err2, "Do:")
91
92 // Callers should close resp.Body
93 // when done reading from it
94 // Defer the closing of the body
95 defer resp.Body.Close()
96
97 // Read the content into a byte array
98 body, dataReadErr := ioutil.ReadAll(resp.Body)
99 check(dataReadErr, "ReadAll:")
100
101 res := make(map[string][]map[string]map[string]map[string]interface{}, 0)
102
103 // We will be using the Unmarshal function
104 // to transform our JSON bytes into the
105 // appropriate structure.
106 // The Unmarshal function accepts a byte array
107 // and a reference to the object which shall be
108 // filled with the JSON data (this is simplifying,
109 // it actually accepts an interface)
110 json.Unmarshal(body, &res)
111
112 // lat, lng as float64
113 lat, _ := res["results"][0]["geometry"]["location"]["lat"]
114 lng, _ := res["results"][0]["geometry"]["location"]["lng"]
115
116 safeLatLng := url.QueryEscape(fmt.Sprintf("%.13f,%.13f", lat, lng))
117 url := fmt.Sprintf("https://api.forecast.io/forecast/%s/%s?units=ca", APIKey, safe\
118 LatLng)
119
120 resp, err := http.Get(url)
121 check(err, "Get:")
122 defer resp.Body.Close()
123
124 fbody, err := ioutil.ReadAll(resp.Body)
125 check(err, "ReadAll:")
126
127 var f Forecast
128 json.Unmarshal(fbody, &f)
129
130 fmt.Println("The Weather at ", addr)
131 fmt.Println("Timezone = ", f.Timezone)
132 fmt.Println("Temp in Celsius = ", f.Currently.Temperature)
133 fmt.Println("Summary = ", f.Currently.Summary)
134 }
The output is:
Enter City eg. Pune, India:
Pune, India
The Weather at Pune,
Timezone = Asia/Kolkata
Temp in Celsius = 30.59
Summary = Mostly Cloudy
Previously we had used a map in our program, namely:
res := make(map[string][]map[string]map[string]map[string]interface{}, 0)
Let’s use a struct instead:
1 type Response struct {
2 Results []struct {
3 Geometry struct {
4 Location struct {
5 Lat float64
6 Lng float64
7 }
8 }
9 }
10 }
The modified program is:
Program: weather_new.go
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 "io/ioutil"
7 "log"
8 "net/http"
9 "net/url"
10 )
11
12 type DataPoint struct {
13 Time float64
14 Summary string
15 Icon string
16 SunriseTime float64
17 SunsetTime float64
18 PrecipIntensity float64
19 PrecipIntensityMax float64
20 PrecipIntensityMaxTime float64
21 PrecipProbability float64
22 PrecipType string
23 PrecipAccumulation float64
24 Temperature float64
25 TemperatureMin float64
26 TemperatureMinTime float64
27 TemperatureMax float64
28 TemperatureMaxTime float64
29 DewPoint float64
30 WindSpeed float64
31 WindBearing float64
32 CloudCover float64
33 Humidity float64
34 Pressure float64
35 Visibility float64
36 Ozone float64
37 }
38
39 type Forecast struct {
40 Latitude float64
41 Longitude float64
42 Timezone string
43 Offset float64
44 Currently DataPoint
45 Junk string
46 }
47
48
49 type Response struct {
50 Results []struct {
51 Geometry struct {
52 Location struct {
53 Lat float64
54 Lng float64
55 }
56 }
57 }
58 }
59
60 // Replace the text `yourapikey` below with your actual api key
61 const APIKey string = "yourapikey"
62
63 func main() {
64 Get()
65 }
66
67 func check(e error, str string) {
68 if e != nil {
69 log.Fatal(str, " ", e)
70 return
71 }
72 }
73
74 func Get() {
75 var addr string
76
77 fmt.Println("Enter City eg. Pune, India: ")
78 fmt.Scanf("%s", &addr)
79
80 // QueryEscape escapes the addr string so
81 // it can be safely placed inside a URL query
82 safeAddr := url.QueryEscape(addr)
83
84 // Geocoding API
85 fullUrl := fmt.Sprintf("http://maps.googleapis.com/maps/api/geocode/json?address=%\
86 s", safeAddr)
87
88 // Build the http request
89 req, err1 := http.NewRequest("GET", fullUrl, nil)
90 check(err1, "NewRequest:")
91
92 // For control over HTTP client headers,
93 // redirect policy, and other settings,
94 // create a Client
95 // A Client is an HTTP client
96 client := &http.Client{}
97
98 // Send the request via a client
99 // Do sends an HTTP request and
100 // returns an HTTP response
101 resp, err2 := client.Do(req)
102 check(err2, "Do:")
103
104 // Callers should close resp.Body
105 // when done reading from it
106 // Defer the closing of the body
107 defer resp.Body.Close()
108
109 // Read the content into a byte array
110 body, dataReadErr := ioutil.ReadAll(resp.Body)
111 check(dataReadErr, "ReadAll:")
112
113 var res Response
114
115 // We will be using the Unmarshal function
116 // to transform our JSON bytes into the
117 // appropriate structure.
118 // The Unmarshal function accepts a byte array
119 // and a reference to the object which shall be
120 // filled with the JSON data (this is simplifying,
121 // it actually accepts an interface)
122 json.Unmarshal(body, &res)
123
124 // lat, lng as float64
125 lat := res.Results[0].Geometry.Location.Lat
126 lng := res.Results[0].Geometry.Location.Lng
127
128 safeLatLng := url.QueryEscape(fmt.Sprintf("%.13f,%.13f", lat, lng))
129 url := fmt.Sprintf("https://api.forecast.io/forecast/%s/%s?units=ca", APIKey, safe\
130 LatLng)
131
132 resp, err := http.Get(url)
133 check(err, "Get:")
134 defer resp.Body.Close()
135
136 fbody, err := ioutil.ReadAll(resp.Body)
137 check(err, "ReadAll:")
138
139 var f Forecast
140 json.Unmarshal(fbody, &f)
141
142 fmt.Println("The Weather at ", addr)
143 fmt.Println("Timezone = ", f.Timezone)
144 fmt.Println("Temp in Celsius = ", f.Currently.Temperature)
145 fmt.Println("Summary = ", f.Currently.Summary)
146 }
The output is:
Enter City eg. Pune, India:
Pune, India
The Weather at Pune,
Timezone = Asia/Kolkata
Temp in Celsius = 30.59
Summary = Mostly Cloudy
That’s it!
- http://golang.org/pkg/encoding/json/#Encoder.Encode↩
- http://golang.org/pkg/encoding/json/#Decoder.Decode↩
- https://developer.forecast.io/register↩
- https://developer.forecast.io/docs/v2↩
- https://github.com/kingsfleet/rest-metadata/blob/master/forecast.io/forecast.json↩
- https://github.com/kingsfleet/rest-metadata/blob/master/forecast.io/datapoint.json↩
- http://en.wikipedia.org/wiki/Query_string#URL_encoding↩
- https://developers.google.com/maps/documentation/geocoding/↩
- https://addons.mozilla.org/en-us/firefox/addon/jsonview/↩
- https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc↩
- http://golang.org/pkg/net/http/#NewRequest↩
- http://golang.org/pkg/net/http/#Client↩
- https://golang.org/pkg/io/ioutil/#ReadAll)↩
- http://golang.org/pkg/net/http/#Get↩