Wrap API Json response in Go - json

I am sorry if this is a silly question because i am very new in Go.
I am calling couple of apis on the base of business logic, different types of response coming like json array, nest json and single json object.
i need to wrap a api response that called according to business logic in a common format like:
{
"data":"api response here",
"statusCode":200
}
i tried some but its not expect output
type Model[T any] struct {
Data T
StatusCode int
}
model := Model[string]{Data: apiResponse, StatusCode: 200}
out, err := json.Marshal(model)
out put of this code is
{
"Data": "[{\"name\":\"Harry Potter\",\"city\":\"London\"},{\"name\":\"Don Quixote\",\"city\":\"Madrid\"},{\"name\":\"Joan of Arc\",\"city\":\"Paris\"},{\"name\":\"Rosa Park\",\"city\":\"Alabama\"}]",
"StatusCode": 200
}
this i made these changes
var result []map[string]interface{}
json.Unmarshal([]byte(body), &result)
out, err := json.Marshal(result)
output was as expected, above api response was in proper json when use []map[string]interface
problem is, its only for those api that return array of json. those apis returning single json object then to make it work i need to do this
map[string]interface`
means remove the array map.
i need to make it generic so that any kind of json response map into it.

Use type of field Data as an interface{}
type APIResponse struct {
Data interface{} `json:"data"`
StatusCode int `json:"statusCode"`
}
And then you can assign any API Response type to the Data field and marshal it.
func main() {
r := []Person{
{
Name: "Harry Porter",
City: "London",
},
{
Name: "Don Quixote",
City: "Madrid",
},
}
res := APIResponse{
Data: r,
StatusCode: 200,
}
resByt, err := json.Marshal(res)
if err != nil {
panic(err)
}
fmt.Println(string(resByt))
}
Output
{"data":[{"name":"Harry Porter","city":"London"},{"name":"Don Quixote","city":"Madrid"}],"statusCode":200}
Run the full code here in Playground.

You can simply do:
result:=map[string]interface{} {
"data": apiResponse,
"statusCode": 200,
}
out, err:=json.Marshal(result)

Related

Golang - Can't get array of objects after fetching JSON

After I make a request to a server - I get JSON like this:
{
"actions": [
{
"class": "...",
"parameters": [
{ ... }
{ ... }
]
}
]
...
}
The type of the variable I've put this data in, is map[string]interface{}.
I want to access the parameters array in the first object of the actions array. I can successfully get the class property - data.(map[string]interface{})["class"].
However, if I try the same for the parameters property - I get nil ...
I tried data.(map[string][]interface{})["parameters"] - but I get error panic: interface conversion: interface {} is map[string]interface {}, not map[string][]interface {}.
Any idea what I'm missing here?
EDIT:
The Golang code for this is this:
func main() {
var jsonResult map[string]interface{}
errorFromJsonFetching := utils.GetJSON(theUrl, &jsonResult)
if errorFromJsonFetching != nil {
fmt.Printf("Error from checking deploy build: %#v\n", errorFromJsonFetching)
}
// get the top level "actions" prop ..
actionZero := jsonResult["actions"].([]interface{})[0]
fmt.Printf("Class prop: /%v %T\n", actionZero.(map[string]interface{})["class"], actionZero)
fmt.Printf("Parameters prop: /%v %T\n", actionZero.(map[string]interface{})["parameters"], actionZero)
}
The GetJSON function is from another file in the project:
func GetJSON (url string, result interface{}) error {
fmt.Printf("Getting JSON from %v\n", url)
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("Cannot fetch URL %q: %v\n", url, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("Bad resposne status code: %s\n", resp.Status)
}
// attempt to put the JSON in the `result` ..
err = json.NewDecoder(resp.Body).Decode(&result)
if err != nil {
return fmt.Errorf("Could not decode the JSON from response, err: %v\n", err)
}
return nil;
}
EDIT2:
Thanks to the ideas of #larsks in the comments - I created a minimal code sample with JSON in a string, directly in main.go file - and all worked fine.
Then I went ahead to the browser again and tried to fetch the data again - from directly hitting the URL or with $.getJSON from a page - and both returned one and the same JSON data.
However, in my Go code, when I dump the JSON data - I see this for the first member of actions:
map[_class:hudson.model.ParametersDefinitionProperty parameterDefinitions:[map[...
So when I try to get the parameters array by the key parameterDefinitions - then I get the array of object ... :O
Sooooo ... I don't know what's happening ... either Go itself modifies the JSON data when it gets it from the backend, or the backend itself returns different things, depending on how the data is being fetched.
(The backend is the Jenkins API by the way ... so I don't know why I get parameterDefinitions instead of parameters in Go ... :( ...)
Thanks for updating your question; having the correct data makes it easier to answer.
It's best if the code you include in your question is something we can just grab and run -- that means it compiles and runs, and when given the sample data in your question, it produces the behavior you're asking about.
I've modified your code so that I can run it locally, primarily by replacing the GetJSON method with something that reads from a file named data.json instead of hitting an API:
package main
import (
"encoding/json"
"fmt"
"os"
)
func GetJSON(result interface{}) error {
datafile, err := os.Open("data.json")
if err != nil {
panic(err)
}
return json.NewDecoder(datafile).Decode(&result)
}
func main() {
var jsonResult map[string]interface{}
errorFromJsonFetching := GetJSON(&jsonResult)
if errorFromJsonFetching != nil {
fmt.Printf("Error from checking deploy build: %#v\n", errorFromJsonFetching)
}
// get the top level "actions" prop ..
actionZero := jsonResult["actions"].([]interface{})[0]
fmt.Printf("Class prop: /%v %T\n", actionZero.(map[string]interface{})["class"], actionZero)
fmt.Printf("Parameters prop: /%v %T\n", actionZero.(map[string]interface{})["parameters"], actionZero)
}
If I feed it this sample data, which I believe matches the structure of what you show in your question:
{
"actions": [
{
"class": "Name of class",
"parameters": [
{
"name": "alice",
"color": "blue"
},
{
"name": "bob",
"count": 10,
},
{
"name": "mallory",
"valid": false,
}
]
}
]
}
It produces the following output:
Class prop: /Name of class map[string]interface {}
Parameters prop: /[map[color:blue name:alice] map[count:10 name:bob] map[name:mallory valid:false]] map[string]interface {}
This doesn't produce a nil value for the parameters key. In order to more fully answer your question, can you update it so that it has sample data and code that reproduces the behavior you're asking baout?

Parsing JSON with GoLang in AWS Lamda

As part of an application we are building, one of the process steps is an AWS Lamda that captures a post request does some work with it, and then moves one. It has an API Gateway Request as a trigger and the body of this request would be a JSON String. I am having trouble parsing the JSON String to GoLang Object. Here is what I have:
The function that catches request:
func HandleRequest(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
log.Print(fmt.Sprintf("body:[%s] ", event.Body))
parseResponseStringToTypedObject(event.Body)
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Body: "OK",
}, nil
}
Then the parseResponseStringToTypedObject function :
func parseResponseStringToTypedObject(responseString string) {
b := []byte(responseString)
var resp SimpleType
err := json.Unmarshal(b, &resp)
if err == nil {
log.Print(fmt.Sprintf("Account Name: [%s]", resp.accountName))
} else {
log.Print(fmt.Sprintf("Could not unmarshall JSON string: [%s]", err.Error()))
}
}
Here is the SimpleType struct:
type SimpleType struct {
accountName string `json:accountName`
amount int `json:amount`
}
I then, as a test, posted this JSON Body via Postman:
I opened up the CloudWatch Logs (where my lamda logs to) and see that the body is present in the event.Body property, and then logging out a field in the unmarshalled object (resp.accountName) I note that the field is blank. Why is this? Here is log output for the request:
Your SimpleType struct needs 2 things here...
1) The properties need to be "public" or "exported". Meaning they need to start with an upper cased character.
AND
2) The properties need proper tags for the serialization and de serialization of JSON. e.g. each JSON tag surrounded by "
type SimpleType struct {
AccountName string `json:"accountName"`
Amount int `json:"amount"`
}
Hope this helps!

How to create multiple validation methods for one endpoint?

I want to make a validation api in order to validate a set of json requests regarding specific set of rules. To do that I want to use just one endpoint and call functions that correspond to the specific json struct. I know that there is no method overloading in go so I am kind of stumped.
...
type requestBodyA struct {
SomeField string `json:"someField"`
SomeOtherField string `json:"someOtherField"`
}
type requestBodyB struct {
SomeDifferentField string `json:"someDifferentField"`
SomeOtherDifferentField string `json:"someOtherDifferentField"`
}
type ValidationService interface {
ValidateRequest(ctx context.Context, s string) (err error)
}
type basicValidationService struct{}
...
So in order to validate lots of different json requests, is it better to create structs for each and every json request? Or should I create these dynamically? How can I know what kind of request is sent if I only have one endpoint?
If you have a single endpoint/rpc that has to accept different JSON types, you'll need to tell it how to distinguish between them, somehow. One option is to have something like:
type request struct {
bodyA *requestBodyA
bodyB *requestBodyB
}
Then, populate these fields in a container JSON object appropriately. The json module will only populate bodyA if a bodyA key is present, otherwise leaving it a nil, and so on.
Here's a more complete example:
type RequestBodyFoo struct {
Name string
Balance float64
}
type RequestBodyBar struct {
Id int
Ref int
}
type Request struct {
Foo *RequestBodyFoo
Bar *RequestBodyBar
}
func (r *Request) Show() {
if r.Foo != nil {
fmt.Println("Request has Foo:", *r.Foo)
}
if r.Bar != nil {
fmt.Println("Request has Bar:", *r.Bar)
}
}
func main() {
bb := []byte(`
{
"Foo": {"Name": "joe", "balance": 4591.25}
}
`)
var req Request
if err := json.Unmarshal(bb, &req); err != nil {
panic(err)
}
req.Show()
var req2 Request
bb = []byte(`
{
"Bar": {"Id": 128992, "Ref": 801472}
}
`)
if err := json.Unmarshal(bb, &req2); err != nil {
panic(err)
}
req2.Show()
}
Another option is to do it more dynamically with maps, but it's likely that the method above will be sufficient.

Is it possible to partially deserialise JSON into a struct in go?

I need to integrate with an API that returns loads of elements in its response.
Is it possible to cherry-pick just the fields I want with go's json library or do I need to deserialise the entire response?
Yes.
Here's an example of having 2 fields in json and only decoding one:
jsonString := `{"a": 1, "b": 2}`
var rec struct {
A int `json:"a"`
}
err := json.Unmarshal([]byte(jsonString), &rec)
if err != nil {
log.Fatalf("json.Unmarshal() failed with '%s'\n", err)
}
fmt.Printf("rec: %+v\n", rec)
When run it prints:
rec: {A:1}
i.e. field "a" in json was decoded and field "b" was discarded.
See https://play.golang.org/p/89tu-ZC4pR for full example.

Custom JSON mapping function in Go

So I'm making a Go service that makes a call to a restful API, I have no control over the API I'm calling.
I know that Go has a nice built in deserializer in NewDecoder->Decode, but it only works for struct fields that start with capital letters (aka public fields). Which poses a problem because the JSON I'm trying to consume looks like this:
{
"_next": "someValue",
"data": [{/*a collection of objects*/}],
"message": "success"
}
How the heck would I map "_next"?
Use tags to specify the field name in JSON. The JSON object you posted above can be modeled like this:
type Something struct {
Next string `json:"_next"`
Data []interface{} `json:"data"`
Message string `json:"message"`
}
Testing it:
func main() {
var sg Something
if err := json.Unmarshal([]byte(s), &sg); err != nil {
panic(err)
}
fmt.Printf("%+v", sg)
}
const s = `{
"_next": "someValue",
"data": ["one", 2],
"message": "success"
}`
Output (try it on the Go Playground):
{Next:someValue Data:[one 2] Message:success}
Also note that you may also unmarshal into maps or interface{} values, so you don't even have to create structs, but it won't be as convenient using it as the structs:
func main() {
var m map[string]interface{}
if err := json.Unmarshal([]byte(s), &m); err != nil {
panic(err)
}
fmt.Printf("%+v", m)
}
const s = `{
"_next": "someValue",
"data": ["one", 2],
"message": "success"
}`
Output (try it on the Go Playground):
map[_next:someValue data:[one 2] message:success]
Tags will solve your problem.
Hoping it may help others who come here, you can make use of https://mholt.github.io/json-to-go/ to generate Go structs. Paste a JSON structure on the left and the equivalent Go type will be generated to the right, which you can paste into your program.