Reputation: 2376
I came across an interesting scenario. I had a struct and I want to add a field message to it. I was able to do this by going through Can I add a field to an existing struct with Go?.
type User struct {
// user fields here
}
type UpdationResponse struct {
User
Message string `json:"message,omitempty"`
}
func SendSuccessResponse(w http.ResponseWriter, r *http.Request, resp interface{}) interface{} {
w.Header().Set("Content-Type", "application/json")
return json.NewEncoder(w).Encode(resp)
}
This returns a JSON like
{
"id": "50",
"firstName": "vibhor",
"lastName": "agrawal",
"email": "[email protected]",
"isVerified": false,
"joinedAt": "2020-06-28T09:45:59Z",
"fullName": "vibhor agrawal"
"message": "Profile Updated."
}
So, this helps me in sending the User data along with a message let's say "Profile Updated.". If I want to generalize this for all my APIs. Is there something I can do.
I tried:
type SuccessResponse struct {
Data interface{}
Message string `json:"message,omitempty"`
}
func SendSuccessResponse(w http.ResponseWriter, r *http.Request, resp SuccessResponse) interface{} {
w.Header().Set("Content-Type", "application/json")
return json.NewEncoder(w).Encode(resp)
}
But when I send it as JSON it makes a structure like
{
"Data": {
"id": "50",
"firstName": "vibhor",
"lastName": "agrawal",
"email": "[email protected]",
"isVerified": false,
"joinedAt": "2020-06-28T09:45:59Z",
"fullName": "vibhor agrawal"
},
"message": "Profile Updated."
}
Is there a way I can add message in the data itself and generalize this for all my success responses irrespective of Data
?
Upvotes: 1
Views: 354
Reputation: 38223
You could have the response implement the json.Marshaler
interface, have it marshal the two fields separately and then merge the results at the end.
func (r Response) MarshalJSON() ([]byte, error) {
out1, err := json.Marshal(r.Data)
if err != nil {
return nil, err
}
type _Response Response // to avoid infinite recursion
out2, err := json.Marshal((_Response)(r))
if err != nil {
return nil, err
}
// NOTE: this may need more work to handle other cases, for example
// the Data field's dynamic type being a slice of some type, rather than
// a single struct....
if size := len(out1); size > 0 && out1[size-1] == '}' {
if size > 2 {
out1[size-1] = ',' // replace "}" with ","
} else {
out1 = out1[:size-1] // drop "}"
}
out2 = out2[1:] // drop "{"
out := append(out1, out2...) // merge
return out, nil
}
return out2, nil
}
https://play.golang.org/p/9u0jcEgt2zL
Upvotes: 3
Reputation: 5760
In short: This is not possible.
Long answer:
In your first example the struct UpdationResponse
extends the struct User
. This is also called a mixin. Simply speaking UpdationResponse
takes all attributes of User
and uses them itself.
In your second example the struct SuccessResponse
defines the attribute Data
which can be anything. Data
is a child element of SuccessResponse
and gets marshalled as such. There is no way around it, as go source code is statically typed and compiled ahead of time.
Upvotes: 4