How to update struct data for array - go

I am trying to update "age" of data struct using SetAge() function after the array creation in the user struct. Here is the code snippet:
//data struct to set the user details
type data struct {
Name string `json:"name"`
College string `json:"college"`
Age int64 `json:"age"`
}
// user struct to store the user details in JSON Array
type user struct {
DataValue []*data `json:"data"`
}
func (u *user) Details(name, college string) *user {
d:=&data{Name:name, College:college}
u.DataValue=append(u.DataValue, d)
return u
}
func (u *user) SetAge(age int64) *user { //age is optional
// what code should be here such that age is added to resp detail
}
Output:
"data":[{
"name":"test",
"college":"test",
"age":10
},{
"name":"test",
"college":"test"
// in this object "Age" hasn't been set
}]

If you want to update the Age field of all the data objects, You're almost done.
You just need to iterate over the u.DataValue slice, and update the age field as follows:
func (u *user) SetAge(age int64) *user {
for index := range u.DataValue {
u.DataValue[index].Age = age
}
return u
}

As per the requirement in my application, it would be like this:
func (u *user) SetAge(age int64) *user {
u.DataValue[len(u.DataValue) - 1].Age = age
return u
}

Related

How to detemine field of struct is zero value by initialize or not?

I have some condition where to make a dynamic query, value is not set will be ignore. But I have confused how to determine the value is zero value is by input(user) or not (golang set for us)
example :
type User struct {
Age int
}
user := User{ Age : 0 } // query := `Where age = ... `
user := User{} // query := ``
I have use pointer and json before, it's work but in this case i cannot change the struct structure.
type User struct {
Age *int `json:"age"`
}
user :=User{}
if user.Age == nil { //not set }
is anyone can give me idea or keywords? Thanks
An alternative to map[string]interface may be to have a wrapping type (if possible in your case):
type UserWrapper {
user User
ageDirty bool
}
func (u UserWrapper) SetAge(age int) {
u.ageDirty = true
u.user.Age = age
}
func (u UserWrapper) GetAge() int {
return u.user.Age
}
func (u UserWrapper) AgeSet() bool {
return u.ageDirty
}
It makes the intentions clearer, even if it should be better to change the structure directly

Marshal JSON field in database to struct using Pop

I'm using Go Buffalo's ORM Pop and want to store JSON in a field and be able to marshal it into a struct.
e.g.
schema.sql
CREATE TABLE tree (
id uuid PRIMARY KEY,
name text NOT NULL,
fruit json,
);
main.go
type Tree struct {
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Fruit []Fruit `json:"fruit" db:"fruit"`
}
type Fruit struct {
ID int `json:"id"`
Name string `json:"name"`
}
I get the following error:
sql: converting argument $25 type: unsupported type []Fruit, a slice of struct
UPDATE
Based on feedback added the following methods:
type Fruits []Fruit
// Value implements the driver.Valuer interface
func (f Fruits) Value() (driver.Value, error) {
return json.Marshal(of)
}
// Scan implements the sql.Scanner interface
func (f * Fruits) Scan(value interface{}) error {
var data []byte
if b, ok := value.([]byte); !ok {
data = b
return errors.New("type assertion to []byte for Fruit failed")
}
return json.Unmarshal(data, &of)
}
Now receive the error:
Cannot fetch from database: unable to fetch records: sql: Scan error on column index 26, name "fruit": unexpected end of JSON input
Update 2
Updated Scan method to the following and fixed error:
// Scan implements the sql.Scanner interface
func (f * Fruits) Scan(value interface{}) error {
var data = []byte(value.([]uint8))
return json.Unmarshal(data, &of)
}
Based on the help provided by #mkopriva
You need to provide Value and Scan methods for your struct
// Tree tall tree with many fruit
type Tree struct {
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Fruit []Fruit `json:"fruit" db:"fruit"`
}
// Fruit fruit found on a tree
type Fruit struct {
ID int `json:"id"`
Name string `json:"name"`
}
// Creates a splice of Fruit
type Fruits []Fruit
// Value implements the driver.Valuer interface
func (f Fruits) Value() (driver.Value, error) {
return json.Marshal(of)
}
// Scan implements the sql.Scanner interface
func (f * Fruits) Scan(value interface{}) error {
var data = []byte(value.([]uint8))
return json.Unmarshal(data, &f)
}

Can I set tag dynamically on runtime? [duplicate]

I have the following:
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
for i := 0; i < value.NumField(); i++ {
tag := value.Type().Field(i).Tag.Get("json")
field := value.Field(i)
fmt.Println(tag, field)
}
return json.Marshal(u)
}
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
I am trying to json encode the struct but before I do I need to change the json key...eg the final json should look like:
{"id": 1, "name": "Ken Jennings", "address": "123 Jennings Street"}
I noticed the method for value.Type().Field(i).Tag.Get("json"), however there is no setter method. Why? and how do I get the desired json output.
Also, how do I iterate through all the fields, including the embedded struct Another?
https://play.golang.org/p/Qi8Jq_4W0t
Since Go 1.8 you can use this solution:
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
type alias struct {
ID int64 `json:"id"`
Name string `json:"name"`
tag string `json:"-"`
Another
}
var a alias = alias(*u)
return json.Marshal(&a)
}
Will give us:
{"id":1,"name":"Ken Jennings","address":"123 Jennings Street"}
This solution made possible by the fact that in Go 1.8 you can assign structs with same structure but different tags to each other. As you see type alias has the same fields as type User but with different tags.
It's kludgy, but if you can wrap the struct in another, and use the new one for encoding, then you could:
Encode the original struct,
Decode it to an interface{} to get a map
Replace the map key
Then encode the map and return it
Thus:
type MyUser struct {
U User
}
func (u MyUser) MarshalJSON() ([]byte, error) {
// encode the original
m, _ := json.Marshal(u.U)
// decode it back to get a map
var a interface{}
json.Unmarshal(m, &a)
b := a.(map[string]interface{})
// Replace the map key
b["name"] = b["first"]
delete(b, "first")
// Return encoding of the map
return json.Marshal(b)
}
In the playground: https://play.golang.org/p/TabSga4i17
You can create a struct copy w/ new tag name by using reflect.StructOf and reflect.Value.Convert function
https://play.golang.org/p/zJ2GLreYpl0
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
t := value.Type()
sf := make([]reflect.StructField, 0)
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Tag)
sf = append(sf, t.Field(i))
if t.Field(i).Name == "Name" {
sf[i].Tag = `json:"name"`
}
}
newType := reflect.StructOf(sf)
newValue := value.Convert(newType)
return json.Marshal(newValue.Interface())
}
It seems the tag property of type User in the question is meant to be used for renaming the JSON fieldname for the Name property.
An implementation of MarshalJSON with a bit of reflection can do the job without that additional tag property and also without an additional wrapper struct (as suggested in the accepted answer), like so:
package main
import (
"encoding/json"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
Another
}
type Another struct {
Address string `json:"address"`
}
// define the naming strategy
func (User) SetJSONname(jsonTag string) string {
if jsonTag == "first"{
return "name"
}
return jsonTag
}
// implement MarshalJSON for type User
func (u User) MarshalJSON() ([]byte, error) {
// specify the naming strategy here
return marshalJSON("SetJSONname", u)
}
// implement a general marshaler that takes a naming strategy
func marshalJSON(namingStrategy string, that interface{}) ([]byte, error) {
out := map[string]interface{}{}
t := reflect.TypeOf(that)
v := reflect.ValueOf(that)
fnctn := v.MethodByName(namingStrategy)
fname := func(params ...interface{}) string {
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
return fnctn.Call(in)[0].String()
}
outName := ""
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
switch n := f.Tag.Get("json"); n {
case "":
outName = f.Name
case "-":
outName = ""
default:
outName = fname(n)
}
if outName != "" {
out[outName] = v.Field(i).Interface()
}
}
return json.Marshal(out)
}
func main() {
anoth := Another{"123 Jennings Street"}
u := User{1, "Ken Jennings", anoth,}
e := json.NewEncoder(os.Stdout)
e.Encode(u)
}
This will print:
{"Another":{"address":"123 Jennings Street"},"id":1,"name":"Ken Jennings"}
However, be aware that MarshalJSON always sorts the JSON tags, while the standard encoder preserves the order of struct fields.

how to modify struct fields in golang

I have example play.golang.org/p/Y1KX-t5Sj9 where I define method Modify() on struct User
type User struct {
Name string
Age int
}
func (u *User) Modify() {
*u = User{Name: "Paul"}
}
in the main() I am defining struct literal &User{Name: "Leto", Age: 11} then call u.Modify(). That results in printing 'Paul 0' I like that struct field Name is changed , but what is the correct way to keep Age field ?
Just modify the field you want to change:
func (u *User) Modify() {
u.Name = "Paul"
}
This is covered well in the Go tour which you should definitely read through, it doesn't take long.

How do I dynamically change the struct's json tag?

I have the following:
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
for i := 0; i < value.NumField(); i++ {
tag := value.Type().Field(i).Tag.Get("json")
field := value.Field(i)
fmt.Println(tag, field)
}
return json.Marshal(u)
}
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
I am trying to json encode the struct but before I do I need to change the json key...eg the final json should look like:
{"id": 1, "name": "Ken Jennings", "address": "123 Jennings Street"}
I noticed the method for value.Type().Field(i).Tag.Get("json"), however there is no setter method. Why? and how do I get the desired json output.
Also, how do I iterate through all the fields, including the embedded struct Another?
https://play.golang.org/p/Qi8Jq_4W0t
Since Go 1.8 you can use this solution:
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
type alias struct {
ID int64 `json:"id"`
Name string `json:"name"`
tag string `json:"-"`
Another
}
var a alias = alias(*u)
return json.Marshal(&a)
}
Will give us:
{"id":1,"name":"Ken Jennings","address":"123 Jennings Street"}
This solution made possible by the fact that in Go 1.8 you can assign structs with same structure but different tags to each other. As you see type alias has the same fields as type User but with different tags.
It's kludgy, but if you can wrap the struct in another, and use the new one for encoding, then you could:
Encode the original struct,
Decode it to an interface{} to get a map
Replace the map key
Then encode the map and return it
Thus:
type MyUser struct {
U User
}
func (u MyUser) MarshalJSON() ([]byte, error) {
// encode the original
m, _ := json.Marshal(u.U)
// decode it back to get a map
var a interface{}
json.Unmarshal(m, &a)
b := a.(map[string]interface{})
// Replace the map key
b["name"] = b["first"]
delete(b, "first")
// Return encoding of the map
return json.Marshal(b)
}
In the playground: https://play.golang.org/p/TabSga4i17
You can create a struct copy w/ new tag name by using reflect.StructOf and reflect.Value.Convert function
https://play.golang.org/p/zJ2GLreYpl0
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
t := value.Type()
sf := make([]reflect.StructField, 0)
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Tag)
sf = append(sf, t.Field(i))
if t.Field(i).Name == "Name" {
sf[i].Tag = `json:"name"`
}
}
newType := reflect.StructOf(sf)
newValue := value.Convert(newType)
return json.Marshal(newValue.Interface())
}
It seems the tag property of type User in the question is meant to be used for renaming the JSON fieldname for the Name property.
An implementation of MarshalJSON with a bit of reflection can do the job without that additional tag property and also without an additional wrapper struct (as suggested in the accepted answer), like so:
package main
import (
"encoding/json"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
Another
}
type Another struct {
Address string `json:"address"`
}
// define the naming strategy
func (User) SetJSONname(jsonTag string) string {
if jsonTag == "first"{
return "name"
}
return jsonTag
}
// implement MarshalJSON for type User
func (u User) MarshalJSON() ([]byte, error) {
// specify the naming strategy here
return marshalJSON("SetJSONname", u)
}
// implement a general marshaler that takes a naming strategy
func marshalJSON(namingStrategy string, that interface{}) ([]byte, error) {
out := map[string]interface{}{}
t := reflect.TypeOf(that)
v := reflect.ValueOf(that)
fnctn := v.MethodByName(namingStrategy)
fname := func(params ...interface{}) string {
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
return fnctn.Call(in)[0].String()
}
outName := ""
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
switch n := f.Tag.Get("json"); n {
case "":
outName = f.Name
case "-":
outName = ""
default:
outName = fname(n)
}
if outName != "" {
out[outName] = v.Field(i).Interface()
}
}
return json.Marshal(out)
}
func main() {
anoth := Another{"123 Jennings Street"}
u := User{1, "Ken Jennings", anoth,}
e := json.NewEncoder(os.Stdout)
e.Encode(u)
}
This will print:
{"Another":{"address":"123 Jennings Street"},"id":1,"name":"Ken Jennings"}
However, be aware that MarshalJSON always sorts the JSON tags, while the standard encoder preserves the order of struct fields.

Resources