Unmarshaling JSON not printing values in stuct - json

I am trying to get list of reminders from slack by calling its api. I need to extract the time stamp and user id from the response. But I am not able to get values copied to struct while unmarshaling the JSON.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)
type ReadingHttpResponse struct {
ID string `json:"id"`
Creator string `json:"creator"`
User string `json:"user"`
Text string `json:"text"`
Recurring bool `json:"recurring"`
Time time.Time `json:"time"`
CompleteTS int `json:"complete_ts"`
}
func main() {
url := "https://slack.com/api/reminders.list"
var bearer = "Bearer " + "My Bearer token"
req, err := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", bearer)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Println("Error on response.\n[ERRO] -", err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
var m ReadingHttpResponse
err = json.Unmarshal(body, &m)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%#v\r\n", m)
}
json in response from api is
"ok":true,"reminders":[{"id":"bacsasdad","creator":"asdasdww","user":"asdasdad","text":"Hello this is for testing purpose only","recurring":false,"time":1578470823,"complete_ts":0}]}
But the Unmarshaled data has no values
main.ReadingHttpResponse{ID:"", Creator:"", User:"", Text:"", Recurring:false, Time:time.Time{wall:0x0, ext:0, loc:(*time.Location)(nil)}, CompleteTS:0}

Your struct doesn’t correspond to the JSON response, it should be like:
type ReadingHttpResponse struct {
Ok bool `json:"ok"`
Reminders []Reminder `json:"reminders"`
}
type Reminder struct {
ID string `json:"id"`
Creator string `json:"creator"`
User string `json:"user"`
Text string `json:"text"`
Recurring bool `json:"recurring"`
Time int `json:"time"`
CompleteTs int `json:"complete_ts"`
}

Related

How to store data from a HTTP Get request in a slice of structs

Problem
I'm new to Go and I'm trying to store json data in a struct from the Gov.uk public holidays API, so I can use this later on in my frontend.
If I run
var sb = string(body)
fmt.Println(sb)
I can see the data that's being returned in my terminal. I know that the response body is made up of bytes and the above converts it to a string.
I would like to iterate through the response body and store the data in a slice of structs called holidays, each struct will contain the data for a single public holiday. For some reason, the holidays variable returns an empty slice: [].
I guess my two questions are:
What's the best way to transform json data into a slice of structs to be used later on?
Why does the holidays variable return an empty slice?
Thanks!
Here's my code below:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
type Response struct {
Data []Data
}
type Data struct {
Division string
Bankholiday Bankholiday
}
type Bankholiday struct {
Title string
Date string
}
func main() {
resp, err := http.Get("https://www.gov.uk/bank-holidays.json")
if err != nil {
log.Fatal(err)
}
if resp.Body != nil {
defer resp.Body.Close()
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
}
var response Response
json.Unmarshal(body, &response)
var holidays = []Bankholiday{}
for _, date := range response.Data {
holidays = append(holidays, Bankholiday{
Title: date.Bankholiday.Title,
Date: date.Bankholiday.Date,
})
}
fmt.Println("holidays: ", holidays)
}
I had to adjust the Response struct to handle correct unmarshaling of data. Find below the working code:
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
)
type Response map[string]Data
type Data struct {
Division string `json:"division"`
Events []struct {
Title string `json:"title"`
Date string `json:"date"`
Notes string `json:"notes"`
Bunting bool `json:"bunting"`
} `json:"events"`
}
func main() {
resp, err := http.Get("https://www.gov.uk/bank-holidays.json")
if err != nil {
log.Fatal(err)
}
if resp.Body != nil {
defer resp.Body.Close()
}
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
}
var response Response
if err = json.Unmarshal(body, &response); err != nil {
log.Fatalln(err)
}
for div, _ := range response {
for _, event := range response[div].Events {
fmt.Printf("Division=%s, Holiday=%s, Date=%s\n", div, event.Title, event.Date)
}
}
}
Because your json fields must match your structs.
type Response map[string]Data
type Data struct {
Division string `json:"division"`
Events []Event `json:"events"`
}
type Event struct {
Title string `json:"title"`
Date string `json:"date"`
Notes string `json:"notes"`
Bunting bool `json:"bunting"`
}

How to modify a return json

I'm taking the data from the github API with golang, however, I want to send to the front only the necessary data (id, name, url, language and description) instead of just returning everything that the github API gives me.
func GetAllReposStars(w http.ResponseWriter, r *http.Request) {
enableCors(&w)
params := mux.Vars(r)
username := params["username"]
res, err := http.Get("https://api.github.com/users/" + username + "/starred")
body, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}
log.Printf("Body: %s\n", body)
if err != nil {
log.Fatal(err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(body)
json.NewEncoder(w)
}
You can define a type that has only the keys you need and decode the response from the GitHub API into a variable of that type so that only the keys you need are kept, then write that variable to the response.
For example:
package main
import (
"encoding/json"
"log"
"net/http"
"github.com/gorilla/mux"
)
type RelevantRepoData struct {
Id int `json:"id"`
Name string `json:"name"`
Url string `json:"url"`
Language string `json:"language"`
Description string `json:"description"`
}
func GetAllReposStars(w http.ResponseWriter, r *http.Request) {
enableCors(&w)
params := mux.Vars(r)
username := params["username"]
res, err := http.Get("https://api.github.com/users/" + username + "/starred")
var repoData RelevantRepoData
err = json.NewDecoder(res.Body).Decode(&repoData)
if err != nil {
log.Fatal(err)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(repoData)
}
If you want to send some specific fields to front-end but in future want to handle them on backend, you can use json:"-" in your struct
e.g :
type RelevantRepoData struct {
Irrelevant string `json:"-"`
Id int `json:"id"`
Name string `json:"name"`
Url string `json:"url"`
Language string `json:"language"`
Description string `json:"description"`
}
When you return this object, the fields with json:"-" will be ignored but you can still use them on backend.

Converting string to json in Go

I am new to Go, and was learning to setup a http server.
What i am trying to do is return a json output of 10 movies in my sql database. But the resulting output isn't in json.
I checked the output with online json formatters, and the output was in json format.
I tried json.Marshall as well as json.Encode, but both are not giving the desired results.
type movie_list struct {
Page int `json:"Page"`
Results []movie `json:"Results"`
}
type movie struct {
Id int `json:"Id"`
Title string `json:"Title"`
Language string `json:"Language"`
Release_date string `json:"Release_date"`
Poster_path string `json:"Poster_path"`
Background_path string `json:"Background_path"`
Overview string `json:"Overview"`
Genre_ids string `json:"Genre_ids"`
}
rows,err:=db.Query("select * from movies limit 10")
if err!=nil{
fmt.Println(err)
}
var list movie_list
var tag movie
for rows.Next(){
err:=rows.Scan(&tag.Id,&tag.Title,&tag.Language,&tag.Release_date,&tag.Poster_path,&tag.Background_path,&tag.Overview,&tag.Genre_ids)
if err != nil {
fmt.Println(err)
}
list.Results = append(list.Results,tag)
}
json.NewEncoder(w).Encode(list)
The ouput in postman -
The formatted output -
my entire code is as follows (for reference)
package main
import (
"fmt"
"log"
"net/http"
"encoding/json"
"database/sql"
_ "github.com/go-sql-driver/mysql"
"github.com/gorilla/mux"
"github.com/davecgh/go-spew/spew"
)
func handleRequests() {
myRouter := mux.NewRouter().StrictSlash(true)
myRouter.HandleFunc("/", homePage)
myRouter.HandleFunc("/movie/top_rated", returnSingleArticle)
log.Fatal(http.ListenAndServe(":10000", myRouter))
}
type movie_list struct {
Page int `json:"Page"`
Results []movie `json:"Results"`
}
type movie struct {
Id int `json:"Id"`
Title string `json:"Title"`
Language string `json:"Language"`
Release_date string `json:"Release_date"`
Poster_path string `json:"Poster_path"`
Background_path string `json:"Background_path"`
Overview string `json:"Overview"`
Genre_ids string `json:"Genre_ids"`
}
func homePage(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome to the HomePage!")
fmt.Println("Endpoint Hit: homePage")
}
func returnSingleArticle(w http.ResponseWriter, r *http.Request) {
//vars := mux.Vars(r)
//key := vars["id"]
db, err := sql.Open("mysql", "root:72574484#tcp(127.0.0.1:3306)/PicturePerfect")
if err != nil {
fmt.Println(err)
}else{
fmt.Println("Connection Established")
}
rows,err:=db.Query("select * from movies limit 10")
if err!=nil{
fmt.Println(err)
}
var list movie_list
var tag movie
for rows.Next(){
err:=rows.Scan(&tag.Id,&tag.Title,&tag.Language,&tag.Release_date,&tag.Poster_path,&tag.Background_path,&tag.Overview,&tag.Genre_ids)
if err != nil {
fmt.Println(err)
}
fmt.Println(tag.Id)
s2, _ := json.Marshal(tag)
list.Results = append(list.Results,tag)
}
err = rows.Err()
if err != nil {
fmt.Println(err)
}
defer db.Close()
//fmt.Fprintf(w, "Hello, %q\n", list.Results[3])
json.NewEncoder(w).Encode(list)
spew.Dump(list)
//fmt.Fprintf(w, "given lamguage, %q\n", tag.Poster_path)
}
func main() {
handleRequests()
}
The problem is that the response content-type header is not application/json. Fix by setting the header before writing the body.
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(list)
If the content type is not specified by the application, then the net/http server calls http.DetectConentType to set the content type in the response header. The function does not detect JSON and defaults to text/plain.

How to unmarshal json data to print in a well defined format

I can't figure out how to unmarshal the json data provided by an api and consume the data to print in a specified format.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type postOffice []struct {
Name string
Taluk string
Region string
Country string
}
func main() {
data, err := http.Get("http://postalpincode.in/api/pincode/221010")
if err != nil {
fmt.Printf("The http request has a error : %s", err)
} else {
read, _ := ioutil.ReadAll(data.Body)
var po postOffice
err = json.Unmarshal(read, &po)
if err != nil {
fmt.Printf("%s", err)
}
fmt.Print(po)
}
}
The code was working well till the "read" was evaluated but is throwing the following error on using json.Unmarshal "json: cannot unmarshal object into Go value of type main.post[]"
You need create a second struct to receive the whole JSON.
type JSONResponse struct {
Message string `json:"Message"`
Status string `json:"Success"`
PostOffice postOffice `json:"PostOffice"`
}
This is because the PostOffice is an array inside of the response.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
//this is the new struct
type JSONResponse struct {
Message string `json:"Message"`
Status string `json:"Success"`
PostOffice postOffice `json:"PostOffice"`
}
type postOffice []struct {
Name string
Taluk string
Region string
Country string
}
func main() {
data, err := http.Get("http://postalpincode.in/api/pincode/221010")
if err != nil {
fmt.Printf("The http request has a error : %s", err)
} else {
read, _ := ioutil.ReadAll(data.Body)
//change the type of the struct
var po JSONResponse
err = json.Unmarshal(read, &po)
if err != nil {
fmt.Printf("%s", err)
}
fmt.Print(po)
}
}

Converting Json to string in golang

What I am trying to do is to convert the JSON response I got from a third party API to string to be able to render it on the webpage. My attempt first was to create a struct called money which holds the 3 values which are being returned and then Unmarshel the bytes but I don't get anything displayed
Here is the struct
type money struct {
Base string `json:"base"`
Currency string `json:"currency"`
Amount float32 `json:"amount"`}
and inside the getCurrency() func
response, err := http.Get("https://api.coinbase.com/v2/prices/spot?currency=USD")
if err != nil {
fmt.Printf("The http requst failed with error %s \n", err)
} else {
answer, _ := ioutil.ReadAll(response.Body)
response := money{}
json.Unmarshal([]byte(answer), &response)
fmt.Fprintln(w, response)
fmt.Fprintln(w, response.Currency)
}
Finally here is what i get from the json response
{"data":{"base":"BTC","currency":"USD","amount":"4225.87"}}
I had to remove the double quotes from the 'amount' value in order to allow the parsing into float32:
{"data":{"base":"BTC","currency":"USD","amount":4225.87}}
See on Playground: https://play.golang.org/p/4QVclgjrtyi
Full code:
package main
import (
"encoding/json"
"fmt"
)
type money struct {
Base string `json:"base"`
Currency string `json:"currency"`
Amount float32 `json:"amount"`
}
type info struct {
Data money
}
func main() {
str := `{"data":{"base":"BTC","currency":"USD","amount":4225.87}}`
var i info
if err := json.Unmarshal([]byte(str), &i); err != nil {
fmt.Println("ugh: ", err)
}
fmt.Println("info: ", i)
fmt.Println("currency: ", i.Data.Currency)
}