Converting string to json in Go - json

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.

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"`
}

Parse stringified JSON

I'm trying to parse stringified JSON with Go, but I cannot find anything that explains how do to efficient parsing.
Example JSON:
{
"messages":"<ul class=\"messages\"><li class=\"success-msg\"><ul><li><span>Item succcessfully added<\/span><\/li><\/ul><\/li><\/ul>",
"app_cart":"[]",
"addcartrows":"[{\"productId\":\"1675688\",\"quantity\":1,\"unitPrice\":\"290.00\",\"currency\":\"EUR\",\"sku\":\"P00525485-3\"}]",
"minicart_content":"<ul class=\"checkout-types minicart\">\n
<li>",
"cart_qty":"1",
"added_product_json":"{\"id\":\"1675695\",\"size\":\"40\"}"
}
I usually parse json by casting it to a struct. like this:
type AutoGenerated struct {
Messages string `json:"messages"`
AppCart string `json:"app_cart"`
Addcartrows string `json:"addcartrows"`
MinicartContent string `json:"minicart_content"`
CartQty string `json:"cart_qty"`
AddedProductJSON string `json:"added_product_json"`
}
var j AutoGenerated
if err = json.Unmarshal(body, &AutoGenerated); err != nil {
fmt.Println(err) // json: cannot unmarshal string into Go struct field AutoGenerated.added_product_json
}
However I do not know how to correctly parse this type of response.
Any help?
do it in two steps.
package main
import (
"encoding/json"
"fmt"
)
func main() {
type AutoGenerated struct {
Messages string `json:"messages"`
AppCart string `json:"app_cart"`
Addcartrows string `json:"addcartrows"`
MinicartContent string `json:"minicart_content"`
CartQty string `json:"cart_qty"`
AddedProductJSON string `json:"added_product_json"`
}
type addedProduct struct {
ID string `json:"id"`
Size string `json:"size"`
}
type productRow struct {
ProductID string `json:"productId"`
Quantity int `json:"quantity"`
UnitPrice string `json:"unitPrice"`
Currency string `json:"currency"`
SKU string `json:"sku"`
}
var j AutoGenerated
body := []byte(`{
"messages":"<ul class=\"messages\"><li class=\"success-msg\"><ul><li><span>Item succcessfully added<\/span><\/li><\/ul><\/li><\/ul>",
"app_cart":"[]",
"addcartrows":"[{\"productId\":\"1675688\",\"quantity\":1,\"unitPrice\":\"290.00\",\"currency\":\"EUR\",\"sku\":\"P00525485-3\"}]",
"minicart_content":"<ul class=\"checkout-types minicart\">\n<li>",
"cart_qty":"1",
"added_product_json":"{\"id\":\"1675695\",\"size\":\"40\"}"
}`)
if err := json.Unmarshal(body, &j); err != nil {
panic(err)
}
var k []productRow
if err := json.Unmarshal([]byte(j.Addcartrows), &k); err != nil {
panic(err)
}
var u addedProduct
if err := json.Unmarshal([]byte(j.AddedProductJSON), &u); err != nil {
panic(err)
}
fmt.Printf("%#v\n\n", j)
fmt.Printf("%#v\n\n", k)
fmt.Printf("%#v\n\n", u)
}
Assuming the json you have added has a formatting issue for value of "minicart_content" , below should help -
package main
import (
"fmt"
"encoding/json"
)
var myjson string = `{
"messages": "<ul class=\"messages\"><li class=\"success-msg\"><ul><li><span>Item succcessfully added<\/span><\/li><\/ul><\/li><\/ul>",
"app_cart": "[]",
"addcartrows": "[{\"productId\":\"1675688\",\"quantity\":1,\"unitPrice\":\"290.00\",\"currency\":\"EUR\",\"sku\":\"P00525485-3\"}]",
"minicart_content": "<ul class=\"checkout-types minicart\">\n <li > ",
"cart_qty": "1",
"added_product_json": "{\"id\":\"1675695\",\"size\":\"40\"}"
}`
type AutoGenerated struct {
Messages string `json:"messages"`
AppCart string `json:"app_cart"`
Addcartrows string `json:"addcartrows"`
MinicartContent string `json:"minicart_content"`
CartQty string `json:"cart_qty"`
AddedProductJSON string `json:"added_product_json"`
}
func main() {
var j AutoGenerated
if err := json.Unmarshal([]byte(myjson), &j); err != nil {
fmt.Println(err)
}
fmt.Println(j.AddedProductJSON)
}

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.

Unmarshaling JSON not printing values in stuct

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"`
}

Is there a way to format this json in golang?

I'm just starting to learn GoLang today, I'm trying to build a simple Rest API Web server.
Here's the response Struct I want to send for each request to the web server :
package main
type HttpResp struct{
Status int `json:"status"`
Description string `json:"description"`
Body string `json:"body"`
}
And here's my articles.go file who have the function who gets all the articles in the database :
package main
import (
"encoding/json"
"net/http"
"log"
)
type Article struct{
Id string `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
Description string `json:"description"`
}
func AllArticles(w http.ResponseWriter, r *http.Request){
log.Print("/articles - GET")
db := connect()
defer db.Close()
var articles []Article
results, err := db.Query("SELECT * FROM Articles")
if err != nil{
log.Print(err)
return
}
for results.Next(){
var article Article
err = results.Scan(&article.Title, &article.Description, &article.Body, &article.Id)
if err != nil{
serr, _ := json.Marshal(err)
json.NewEncoder(w).Encode(HttpResp{Status: 500, Description: "Failed to retrieve all articles", Body: string(serr)})
}
articles = append(articles, article)
}
sarr, _ := json.Marshal(articles)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(HttpResp{Status: 200, Body: string(sarr)})
}
The issue I'm facing here is that the response is like this :
{"status":200,"description":"","body":"[{\"id\":\"1\",\"title\":\"First\",\"body\":\"This
is a test body\",\"description\":\"This is a test\"}]"}
I'd like the body to be just JSON and not a string. How can I acheive that ?
No point in marshalling the body separately from the HttpResp. Instead change the Body field's type to interface{} and then set the field to any value of a concrete type as opposed to a json string, e.g. []Article and then marshal the resp once.
type HttpResp struct{
Status int `json:"status"`
Description string `json:"description"`
Body interface{} `json:"body"`
}
And the rest...
package main
import (
"encoding/json"
"net/http"
"log"
)
type Article struct{
Id string `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
Description string `json:"description"`
}
func AllArticles(w http.ResponseWriter, r *http.Request){
log.Print("/articles - GET")
db := connect()
defer db.Close()
var articles []Article
results, err := db.Query("SELECT * FROM Articles")
if err != nil{
log.Print(err)
return
}
for results.Next(){
var article Article
err = results.Scan(&article.Title, &article.Description, &article.Body, &article.Id)
if err != nil{
serr, _ := json.Marshal(err)
json.NewEncoder(w).Encode(HttpResp{Status: 500, Description: "Failed to retrieve all articles", Body: string(serr)})
}
articles = append(articles, article)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(HttpResp{Status: 200, Body: articles})
}