I have many different models:
type objectModel struct {
Title string `json:"title"`
Body string `json:"body"`
}
// Many more models...
These models are used to create response objects that are returned to clients. All responses must contain a boolean OK value; other fields depend on the context.
type objectResponse struct {
OK bool `json:"ok"`
Object *objectModel `json:"object"`
}
type objectListResponse struct {
OK bool `json:"ok"`
Objects []*objectModel `json:"objects"`
}
// Many more response types that are similar to these two examples.
Problem
What I want to have is a reusable response object that embeds all these different custom response objects objectResponse, objectListResponse and so on. In this case I wouldn't need to define OK bool on every response object I have. I would use it like this: response.write(responseWriter), but that's out of the scope of this question.
type response struct {
OK bool `json:"ok"`
CustomResponse
}
It should be possible with an interface, but I don't know what common method all of the responses should implement.
What I want to have is a reusable response object that embeds all these different custom response objects objectResponse, objectListResponse and so on. In this case I wouldn't need to define OK bool on every response object I have.
What about doing the opposite: have a reusable Response object that is embedded in your custom response objects? That would allow you to not define OK bool on every response object (your stated goal), you would just embed the Response struct in each one. If there are other repeated fields you could use the same embedded struct for those as well, or another struct if they only belong in some custom responses.
Here's a simplified working example:
package main
import(
"fmt"
"encoding/json"
)
type Response struct {
OK bool `json:"ok"`
}
type lookResponse struct {
Response
Color string `json:"color"`
Shape string `json:"shape"`
}
func main() {
b := []byte(`{"ok":true,"color":"blue","shape":"circle"}`)
var r lookResponse
err := json.Unmarshal(b, &r)
if err != nil {
fmt.Printf("Error: %s\n", err)
}
fmt.Printf("ok: %v, color: %v, shape: %v\n", r.OK, r.Color, r.Shape)
}
When run that prints:
ok: true, color: blue, shape: circle
You can read more about embedded fields in the Go Language Specification, e.g.:
A field declared with a type but no explicit field name is called an
embedded field... A field or method f of an embedded field in a struct
x is called promoted if x.f is a legal selector that denotes that
field or method f. Promoted fields act like ordinary fields of a
struct except that they cannot be used as field names in composite
literals of the struct.
If that doesn't solve your issue, please clarify your goals/question.
I would use an interface{} there. For example:
type response struct {
OK bool `json:"ok"`
Object interface{} `json:"object"`
}
Now when I want to use I can put any other types there:
res := response{
OK: true,
Object: varOfCustomObjectModel,
}
Here is a response object that I used for one of my project:
https://github.com/leninhasda/gitpull-me/blob/master/api/response.go
Use case:
res := &response{
StatusCode: http.StatusOK,
Data: map[string]interface{}{
"product_title": "Awesome Product",
"price": 188.99,
"quantity": 1,
"meta": map[string]interface{}{
"height": 10,
"width": 15,
},
},
// or as simple as just
// Data: "hello world!"
}
res.json(w) // w is http.ResponseWriter
Related
Let's say I have the first struct as
type Person struct {
Name string `json:"person_name"`
Age int `json:"person_age"`
Data map[string]interface{} `json:"data"`
}
and I am trying to marshal an array of the above struct
Things work well till here and a sample response I receive is
[
{
"person_name":"name",
"person_age":12,"data":{}
},
{
"person_name":"name2",
"person_age":12,"data":{}
}
]
Now, I need to append another struct over here and the final response should like
[
{
"person_name":"name",
"person_age":12,"data":{}
},
{
"person_name":"name2",
"person_age":12,"data":{}
},
{
"newData":"value"
}
]
So can someone help on this and how i can achieve this ?
I tried by creating an []interface{} and then iterating over person to append each data, but the issue in this approach is that it makes the Data as null if in case it's an empty string.
I would need it be an empty map only.
Let me prefix this by saying this looks to me very much like you might be dealing with an X-Y problem. I can't really think of many valid use-cases where one would end up with a defined data-type that has to somehow be marshalled alongside a completely different, potentially arbitrary/freeform data structure. It's possible, though, and this is how you could do it:
So you just want to append a completely different struct to the data-set, then marshal it and return the result as JSON? You'll need to create a new slice for that:
personData := []Person{} // person 1 and 2 here
more := map[string]string{ // or some other struct
"newdata": "value",
}
allData := make([]any, 0, len(personData) + 1) // the +1 is for the more, set cap to however many objects you need to marshal
for _, p := range personData {
allData = append(allData, p) // copy over to this slice, because []Person is not compatible with []any
}
allData = append(allData, more)
bJSON, err := json.Marshal(allData)
if err != nil {
// handle
}
fmt.Println(string(bJSON))
Essentially, because you're trying to marshal a slice containing multiple different types, you have to add all objects to a slice of type any (short for interface{}) before marshalling it all in one go
Cleaner approaches
There are much, much cleaner approaches that allow you to unmarshal the data, too, assuming the different data-types involved are known beforehand. Consider using a wrapper type like so:
type Person struct {} // the one you have
type NewData {
NewData string `json:"newdata"`
}
type MixedData struct {
*Person
*NewData
}
In this MixedData type, both Person and NewData are embedded, so MixedData will essentially act as a merged version of all embedded types (fields with the same name should be overridden at this level). With this type, you can marshal and unmarshal the JSON accordingly:
allData := []MixedData{
{
Person: &person1,
},
{
Person: &person2,
},
{
NewData: &newData,
},
}
Similarly, when you have a JSON []byte input, you can unmarshal it same as you would any other type:
data := []MixedData{}
if err := json.Unmarshal(&data, in); err != nil {
// handle
}
fmt.Printf("%#v\n", data) // it'll be there
It pays to add some functions/getters to the MixedData type, though:
func (m MixedData) IsPerson() bool { return m.Person != nil }
func (m MixedData) Person() *Person {
if m.Person == nil {
return nil
}
cpy := *m.Person // create a copy to avoid shared pointers
return &cpy // return pointer to the copy
}
Do the same for all embedded types and this works like a charm.
As mentioned before, should your embedded types contain fields with the same name, then you should override them in the MixedData type. Say you have a Person and Address type, and both have an ID field:
type MixedData struct {
ID string `json:"id"`
*Person
*Address
}
This will set the ID value on the MixedData type, and all other (non-shared) fields on the corresponding embedded struct. You can then use the getters to set the ID where needed, or use a custom unmarshaller, but I'll leave that to you to implement
I am trying to convert a struct to map using following method
func ConvertStructToMap(in interface{}) map[string]interface{} {
fmt.Println(in)
var inInterface map[string]interface{}
inrec, _ := json.Marshal(in)
json.Unmarshal(inrec, &inInterface)
return inInterface
}
The problem is when I am trying to convert a struct
type Data struct{
Thing string `json:"thing,omitempty"`
Age uint8 `json:"age,omitempty"`
}
With this data
c:=Data{
Thing :"i",
Age:0,
}
it just gives me the following output map[things:i] instead it should give the output
map[things:i,age:0]
And when I don't supply age
like below
c:=Data{
Thing :"i",
}
Then it should give this output map[things:i] .I am running an update query and the user may or may not supply the fields ,any way to solve it .I have looked over the internet but couldn't get my head on place
Edit -
I am sending json from frontend which gets converted to struct with go fiber Body parser
if err := c.BodyParser(&payload); err != nil {
}
Now If I send following payload from frontend
{
thing:"ii"
}
the bodyParser converts it into
Data{
Thing :"ii",
Age :0
}
that's why I have used omitempty such that bodyParser can ignore the field which are not present
omitempty ignore when convert from struct to json
type Response struct {
Age int `json:"age,omitempty"`
Name string `json:"name,omitempty"`
}
resp:=Response {
Name: "john",
}
data,_:=json.Marshal(&resp)
fmt.Println(string(data)) => {"name": "john"}
In you case, you convert from json to struct, if attribute not present, it will be fill with default value of data type, in this case is 0 with uint8
Gin's request validation feature is not working when the request body (JSON) represents an array of objects
Ex:
[
{
"field1":"aaa",
"field2":"bbb"
}
]
code:
type item struct {
Field1 string `json:"field1" binding:"required"`
Field2 string `json:"field2" binding:"required"`
}
var items []item
err := c.BindJSON(&items)
To be more clear, the validation logic in gin is expecting the root object to be a struct and hence bails out when an array type gets passed.
what is best way to validate each object in an array passed in the body?
The JSON will be parsed here and then validated here.
The comment on the default ValidateStruct method:
ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
You can work around this by defining a struct, as it is needed, that holds your data:
type itemHolder struct {
Items []item
}
Then defining a custom Unmarshaler, like this:
func (i *itemHolder) UnmarshalJSON(b []byte) error {
return json.Unmarshal(b, &i.Items)
}
Now *itemHolder implements json.Unmarshaler, which means in turn that it will be a struct that is supported by gin.
This code should work now:
var items itemHolder
err := c.BindJSON(&items)
if err != nil {
// handle...
}
// use items.Items from here on
Please note that your marshal behaviour will change; so you should definitely implement the Marshaler interface if you need to.
I am trying to decode a map into a struct type with help of mapstructure library. If I do it with plain variable it decodes ok, but if I pass struct field it does not decode the map:
package main
import (
"github.com/mitchellh/mapstructure"
)
type Person struct {
Name string
}
type Bundle struct {
Name string
Struct interface{}
}
func main() {
p_map := map[string]string{
"Name": "John",
}
p := Person{}
mapstructure.Decode(p_map, &p)
print(p.Name) // shows name John
b := Bundle{
"person"
Person{},
}
mapstructure.Decode(p_map, &b.Struct)
print(b.Struct.(Person).Name) // Does not show any name. Blank
}
Could you please clarify if I am passing wrong storage for map decoding or it is just mapstructure limitation and I am not able to decode maps into struct fields? Thank you!
UPD
I am sorry if I was not clear enough about the actual reason I need to use such flow:
I send HTTP requests to different resources and get various objects with different fields so initially I collect them as interface{}. After I get a particular resource object, I need to convert it into a particular struct (Person in my sample) so I use mapstructure.decode() function for that.
As I have various objects that are decoded in different structures I want to create a loop in order to avoid code duplication. What I wanted to do is to create a slice with different structures like:
bundles := []Bundle{
{"person", Person{}}
{"employee", Employee{}}
...
}
And then decode objects in a loop:
for bundle := range bundles {
// map_storage contains different type maps that are going to be decoded into struct and key for the specific object is bundle name
mapstructure.Decode(maps_storage[bundle.Name], &bundle.Struct)
// bundle.Struct blank, but I expect that it is filled as in the example below
}
I think you must slightly change the implementation
var p1 Person
mapstructure.Decode(p_map, &p1)
b := Bundle{
p1,
}
print(b.Struct.(Person).Name) // John will appear
I'm trying your code above but lead to empty Person. Maybe Decode function cannot change real value of b.Struct(I'm not sure the exact reason, this is just my opinion), but if I decode to struct Person first then assign to Bundle that works.
Updated:
with some research, I found out the problem.You must use pointer instead of struct. here the updated code
package main
import (
"github.com/mitchellh/mapstructure"
)
type Person struct {
Name string
}
type Bundle struct {
Name string
Struct interface{}
}
func main() {
p_map := map[string]string{
"Name": "John",
}
p := &Person{}
mapstructure.Decode(p_map, &p)
print(p.Name) // shows name John
b := Bundle{
"person",
&Person{},
}
mapstructure.Decode(p_map, &b.Struct)
print(b.Struct.(*Person).Name) // Does not show any name. Blank
}
After changing the type of Struct field in the Bundle from interface{} to Person, It worked for me.
type Bundle struct {
Struct Person
}
print(b.Struct.Name)
I have created a webserver using the library net http. My problem is that I want to create a Struct like below from a JSON object, whose keys names don't match with the struct attributes.
type JsonData struct {
Operation string
IsStaff int
}
The JSON object sent from the server is:
{
"function": "search",
"is_staff": 1
"description": "Test"
}
Most of the solutions I have found are creating another struct, where the JSON keys and struct attribute names match each other. Is there a way to map the JSON decoded to my JsonData struct? Below it is my current function right now.
func handler(w http.ResponseWriter, r *http.Request){
switch r.Method {
case http.MethodPost:
var data JsonData
err := json.NewDecoder(r.Body).Decode(&data)
}
}
Search for "tag" in the json.Marshal documentation.
Basically, you can tag struct fields with special annotations that tell the default un/marshaler to use the given name instead of the (case-insensitive) name of the struct field.
For your struct you can likely just do the following, depending on your actual intent:
type JsonData struct {
Operation string `json:"function"`
IsStaff int `json:"is_staff"`
}
func main() {
jsonstr := `{"function":"search","is_staff":1,"description":"Test"}`
var jd JsonData
err := json.Unmarshal([]byte(jsonstr), &jd)
fmt.Printf("OK: jd=%#v, err=%v\n", jd, err)
// OK: jd=main.JsonData{Operation:"search", IsStaff:1}, err=<nil>
}