Json unmarshal struct to map is ignoring zero values of struct - go

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

Related

Go - Save JSON string into a DB (most efficient?)

I am getting a JSON response from a server and want to save that into a db, where one of the columns is a JSON column. The response looks similar to the following:
msgJson = [{"id": 1, "type": "dog", "attributes": {"weight":20, "sound":"bark"}}]
So I am currently making a struct and trying to update each element in the DB.
type Animal struct {
Id int `json:"id"`
Type string `json:"type"`
Attributes string `json:"attributes"`
}
var animals []Animal
json.Unmarshal([]byte(msgJson), &animals)
sqlStatement := `
UPDATE animals
SET type = $2, attributes = $3
WHERE id = $1;`
_, err := db.Exec(
sqlStatement,
animals[0].Id,
animals[0].Type,
animals[0].Attributes)
Of course, this doesn't work, because the attributes field is supposed to be JSON.
I believe I could Unmarshal the JSON into nested structs, and then Marshal it when updating the DB, but since this will have many fields, is there a way to take the string and immediately represent it as JSON when adding to the DB? I hope that question makes sense.
Thank you
Unmarshal the attributes field to a json.RawMessage. Save the raw message to the database.
type Animal struct {
Id int `json:"id"`
Type string `json:"type"`
Attributes json.RawMessage `json:"attributes"`
}
⋮
_, err := db.Exec(
sqlStatement,
animals[0].Id,
animals[0].Type,
animals[0].Attributes)

Binding validations does not work when request body is Array of objects

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.

How to pass a pointer of struct field in mapstructure.decode

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)

Decoding JSON object and mapping the values to a struct in Go

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

create array of struct from by decoding a JSON file in go

All I looking to do is to create an array of struct Response from a json encoded file.
the file that contains json data looks like this.
cat init.txt
{"events": [{"action":"cpr","id":69,"sha1":"abc","cpr":"cpr_data0"},{"action":"cpr","id":85,"sha1":"def","cpr":"cpr_data1"}]}
The way I have gone about approaching this is
I created a response of type map[string][]Response
.. decoded the JSON from the file
.. created a responseStruct of type []Response
But somehow when I inspect the Value they all look 0 or empty
map[events:[{ 0 } { 0 }]
What is wrong with the approach mentioned above.
type Response struct {
action string `json:"action"`
id int64 `json:"id"`
sha1 string `json:"sha1"`
cpr string `json:"cpr"`
}
func main() {
file, err := os.Open("init.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
var response map[string][]Response
err = json.NewDecoder(file).Decode(&response)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
var responseArray []Response
responseArray = response["events"]
for _, responseStruct := range responseArray {
fmt.Println("id =", responseStruct.id)
fmt.Println("action =", responseStruct.action)
fmt.Println("sha1 = ", responseStruct.sha1)
fmt.Println("cpr =", responseStruct.cpr)
fmt.Println("==========")
}
fmt.Println(response)
}
Well If I modify the struct to look like this it works
type Response struct {
Action string `json:"action"`
ID int64 `json:"id"`
Sha1 string `json:"sha1"`
Cpr string `json:"cpr"`
}
So my question is this how the stuff would work, can't I get the above code to work in the way it is?
Go has the notion of public and private fields in objects, and the only distinction is whether they start with an initial capital letter or not. This applies to not just code modules but also the reflect package and things that use it. So in your initial version
type Response struct {
action string `json:"action"`
...
}
nothing outside your source package, not even the "encoding/json" module, can see the private fields, so it can't fill them in. Changing these to public fields by capitalizing Action and the other field names makes them visible to the JSON decoder.
Lowercase struct elements in golang are private, Hence json decoder (which is an external package) can't access them.
Its able to create the struct object but not able to set values. They appear zero because they 0 is default value.

Resources