How to use switch in GO with json keys? - json

Here is an example of POST request body:
{"action":"do_something","id":"001"}
I took an example of simple json parser
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type some_json struct {
Action string `json:"action"`
Id string `json:"id"`
}
func jsonparse(rw http.ResponseWriter, request *http.Request) {
decoder := json.NewDecoder(request.Body)
var post_data some_json
err := decoder.Decode(&post_data)
if err != nil {
panic(err)
}
switch ***WHAT_SHOULD_BE_HERE???*** {
default :
fmt.Fprintf(w,"WRONG PARAM")
case "some_thing":
fmt.Fprintf(w,post_data.Id + "\n\n")
}
}
func main() {
http.HandleFunc("/", jsonparse)
http.ListenAndServe(":8080", nil)
}
I already know how to switch cases from form values, but
how to switch cases of json key values?

I am not sure what you want to switch, but i think that you just need to erase the () so Action is not a function call anymore
Be careful maybe your error is that u mixed up the strings
in JSON: "do_something"
in case: "some_thing"
You can copy following code to playground
package main
import (
"encoding/json"
"fmt"
"strings"
)
type some_json struct {
Action string `json:"action"`
Id string `json:"id"`
}
func jsonparse() {
r := strings.NewReader("{\"action\":\"do_something\",\"id\":\"001\"}")
decoder := json.NewDecoder(r)
var post_data some_json
err := decoder.Decode(&post_data)
if err != nil {
panic(err)
}
switch post_data.Action {
default:
fmt.Println( "WRONG PARAM")
case "do_something":
fmt.Println( post_data.Id+"\n\n")
}
}
func main() {
jsonparse()
}

Related

How to extract specific JSON data

I do not know how to select specific JSON data.
How can I change this code so that I have just the id, and none of the other response data?
I read online, and apparently I need to use a struct? I am unsure of how to approach this problem.
package main
import(
"fmt"
"io"
"log"
"net/http"
)
func get_single_product() {
res, err := http.Get("https://api.pro.coinbase.com/products/UNI-USD")
if err != nil {
log.Fatal(err)
}
data, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s", data)
}
func main() {
// CALL GET PRODUCTS FUNCTION
get_single_product()
}
This Returns...
{
"id":"UNIUSD",
"base_currency":"UNI",
"quote_currency":"USD",
"base_min_size":"0.1",
"base_max_size":"200000",
"quote_increment":"0.0001",
"base_increment":"0.000001",
"display_name":"UNI/USD",
"min_market_funds":"1.0",
"max_market_funds":"100000",
"margin_enabled":false,
"post_only":false,
"limit_only":false,
"cancel_only":false,
"trading_disabled":false,
"status":"online",
"status_message":""
}
You can use the encoding/json package to decode your json data to an object. Just define the fields you are interested in the object and it will only read out those. Then create a decoder with json.NewDecoder().
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
type Response struct {
ID string `json:"id"`
}
func main() {
res, err := http.Get("https://api.pro.coinbase.com/products/UNI-USD")
if err != nil {
log.Fatal(err)
}
var response Response
json.NewDecoder(res.Body).Decode(&response)
fmt.Println(response.ID)
}
// Output
UNI-USD
See also this question: How to get JSON response from http.Get

Trying to marshal a csv string to a struct

Here is my program :
package main
import (
"fmt"
"log"
"github.com/gocarina/gocsv"
)
// GeoAdresseCsv is a test structcd
type GeoAdresseCsv struct {
ID int `csv:"id"`
IDBan string `csv:"id_ban"`
}
func main() {
var adresseDB []GeoAdresseCsv
stringCsv := `id;id_ban
2908743;28009_0019_00008`
err := gocsv.UnmarshalString(stringCsv, &adresseDB)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", adresseDB)
}
But the ouput that i get is :
[{ID:0 IDBan:}]
How should i do correctly to get my csv string correctly mapped to my struct ?
While the separator in your data is ;, the package by default seems to use ,.
So we need to configure the package to use a different separator. According to the documentation, this is possible with a customizable CSV reader:
gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
r := csv.NewReader(in)
r.Comma = ';' // This is our separator now
return r
})
This configures all functions in the package.
Your code should look like this to include the change:
package main
import (
"encoding/csv"
"fmt"
"io"
"log"
"github.com/gocarina/gocsv"
)
// GeoAdresseCsv is a test structcd
type GeoAdresseCsv struct {
ID int `csv:"id"`
IDBan string `csv:"id_ban"`
}
func main() {
gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
r := csv.NewReader(in)
r.Comma = ';' // This is our separator now
return r
})
var adresseDB []GeoAdresseCsv
stringCsv := `id;id_ban
2908743;28009_0019_00008`
err := gocsv.UnmarshalString(stringCsv, &adresseDB)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", adresseDB)
}
Now the output is the expected value:
[{ID:2908743 IDBan:28009_0019_00008}]
You can run it online here.

How can I display the JSON response from the API in Go?

I'm invoking a small API that gives back a JSON response with a Todo object. How can I display that in my Go web server? Right now, all that's displaying is 'Todo', which I hard-coded. How can that be replaced by the API JSON response?
Here is the code:
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
// Todo Structure
type Todo struct {
userID int
id int
title string
completed bool
}
func main() {
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Todo</h1>")
})
response, err := http.Get("http://jsonplaceholder.typicode.com/todos/1")
if err != nil {
fmt.Print(err.Error())
os.Exit(1)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(responseData))
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
You have to write a function delegated to retrieve the data from the external website. Than use the http.HandleFunc in order to expose the method at your endpoint, here an example:
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
// Todo Structure
type Todo struct {
userID int
id int
title string
completed bool
}
func main() {
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, callHTTP())
})
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
func callHTTP() string {
var (
response *http.Response
err error
responseData []byte
)
if response, err = http.Get("http://jsonplaceholder.typicode.com/todos/1"); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
if responseData, err = ioutil.ReadAll(response.Body); err != nil {
log.Fatal(err)
}
ret := string(responseData)
fmt.Println(ret)
return ret
}
Note:
Avoid to use the default http client in production. You can try some HTTP framework like GIN. I prefer fasthttp, here you can view some code as example: https://github.com/alessiosavi/StreamingServer

Unmarshalling to same struct but different json name

I am trying to unmarshal a particular json data, perform some data transformations and then marshal the data and send it. However I want to marshal it with different json variable name.
Can I marshal the data to another json name like have xyz instead of abc
{"abc":1}
to
{"xyz":1}
package main
import (
"fmt"
"encoding/json"
)
type SomeStruct struct {
SomeField int `json:"abc"`
}
func main(){
jsonData := []byte(`{"abc":1}`)
strct := SomeStruct{}
json.Unmarshal([]byte(jsonData), &strct)
fmt.Println(strct)
x, err := json.Marshal(strct)
if err != nil {
fmt.Println("errs", err)
}
finalValue := string(x)
fmt.Println(finalValue)
}
Define two structures, one for your input, one for your output, and convert like this:
package main
import (
"fmt"
"encoding/json"
)
type inStruct struct {
SomeField int `json:"abc"`
}
type outStruct struct {
SomeField int `json:"xyz"`
}
func main(){
jsonData := []byte(`{"abc":1}`)
strct := inStruct{}
if err := json.Unmarshal(jsonData, &strct); err != nil {
log.Fatal(err)
}
// Edit to reflect mkopriva's comment
// outStruct := &outStruct{SomeField: strct.SomeField}
outStruct := outStruct(strct)
x, err := json.Marshal(outStruct)
if err != nil {
log.Fatal(err)
}
finalValue := string(x)
fmt.Println(finalValue)
}
You can potentially implement a custom ToJSON method for the struct, as follows:
package main
import (
"encoding/json"
"fmt"
)
type SomeStruct struct {
SomeField int `json:"abc"`
}
func (s *SomeStruct) ToJSON() string {
return fmt.Sprintf("{\"xyz\":%d}", s.SomeField)
}
func main() {
jsonData := []byte(`{"abc":1}`)
strct := SomeStruct{}
json.Unmarshal([]byte(jsonData), &strct)
fmt.Println(strct)
x, err := json.Marshal(strct)
if err != nil {
fmt.Println("errs", err)
}
finalValue := string(x)
fmt.Println(finalValue)
fmt.Println("custom ToJSON", strct.ToJSON())
}
Playground link: https://play.golang.org/p/mjW0dBPN59Q
It might not be flexible in the long run though. Personally, for requirement like this, I would prefer the solution posted by #Clément

Can get value of JSON in Go

I'm new in Go. I'm trying to read a JSON file and get a part of it for then operate with the values obtained.
My JSON is in the file example.json:
{"results":[{"statement_id":0,"series":[{"name":"cpu/node_utilization","columns":["time","distinct"],"values":[[10,1],[11,3],[13,5]]}]}]}
So what I would like to get is the "values" for get the sum of all the elements. In this case: 1+3+5
Here is the code that I have. I'm available to get the results, but then I don't manage to get series.
Here is the code that I have:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
func main() {
// Open our jsonFile
jsonFile, err := os.Open("example.json")
// if we os.Open returns an error then handle it
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened example.json")
// defer the closing of our jsonFile so that we can parse it later on
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
var all_data map[string]interface{}
json.Unmarshal([]byte(byteValue), &all_data)
fmt.Println(all_data["results"])
}
I've tried diferent solutions like
all_data["results"].(map[string]interface{})["series"])
But the problem is that the map is in an array, and I don't know how to solve it.
Using interfaces and map
package main
import (
"encoding/json"
"fmt"
)
func main() {
byteValue := []byte(`{"results":[{"statement_id":0,"series":[{"name":"cpu/node_utilization","columns":["time","distinct"],"values":[[10,1],[11,3],[13,5]]}]}]}`)
var all_data map[string][]interface{}
json.Unmarshal([]byte(byteValue), &all_data)
fmt.Println("result:", all_data["results"])
for _, r := range all_data["results"] {
s := r.(map[string]interface{})
fmt.Println("series:", s["series"])
w := s["series"].([]interface{})
for _, x := range w {
y := x.(map[string]interface{})
fmt.Println(y)
z := y["values"].([]interface{})
fmt.Println("values:", z)
for _, v := range z {
u := v.([]interface{})
fmt.Println(u)
for _, i := range u {
val := i.(float64)
fmt.Println(val)
}
}
}
}
}
I have solved defining an Struct.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
type AutoGenerated struct {
Results []struct {
StatementID int `json:"statement_id"`
Series []struct {
Name string `json:"name"`
Columns []string `json:"columns"`
Values [][]int `json:"values"`
} `json:"series"`
} `json:"results"`
}
func main() {
// Open our jsonFile
jsonFile, err := os.Open("example.json")
// if we os.Open returns an error then handle it
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened example.json")
// defer the closing of our jsonFile so that we can parse it later on
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
all_data := AutoGenerated{}
json.Unmarshal([]byte(byteValue), &all_data)
fmt.Println(all_data.Results[0].Series[0].Values)
}
I have used this web to generate automatically the Struct providing the JSON structure