Is there a way to format this json in golang? - json

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})
}

Related

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

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)
}
}

json: cannot unmarshal array into Go value of type main.Posts

I'm trying to read a json file with golang but i'm getting this error.
I've checked almost every question about it but still couldnt get it.
Here's the example json file:
https://jsonplaceholder.typicode.com/posts
And my code:
package main
import (
"net/http"
"log"
"fmt"
"io/ioutil"
"encoding/json"
)
type Posts struct {
Post []struct{
UserId int `json:"userId"`
ID int `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
}
}
func main (){
resp, err := http.Get("https://jsonplaceholder.typicode.com/posts")
if err != nil {
log.Fatal(err)
}
content, _ := ioutil.ReadAll(resp.Body)
var posts Posts
parsed := json.Unmarshal([]byte(content), &posts)
//fmt.Println(string(content))
fmt.Println(parsed)
}
Posts is an array of Post struct but you defined Post as array it is your first mistake, also Unmarshal doesn't returns result it returns only error and fills given parameter.
package main
import (
"net/http"
"log"
"fmt"
"io/ioutil"
"encoding/json"
)
type Post struct {
UserId int `json:"userId"`
ID int `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
}
type Posts []Post
func main (){
resp, err := http.Get("https://jsonplaceholder.typicode.com/posts")
if err != nil {
log.Fatal(err)
}
content, _ := ioutil.ReadAll(resp.Body)
var posts Posts
err = json.Unmarshal(content, &posts)
if err != nil {
log.Fatal(err)
}
fmt.Println(posts[0].Body)
}
That JSON is, at its root, an array. You're trying to unmarshal it into an object, which contains, as a field, an array - hence the error that you passed an object when the JSON is an array. You want to pass an array (or slice, really), as in:
type Post struct {
UserId int `json:"userId"`
ID int `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
}
//...
var posts []Post
err := json.Unmarshal([]byte(content), &posts)
// Check err, do stuff with posts