How to clear struct values except certain fields with struct's method - go

I have a struct. I want to clear all fields except some public fields, e.g. Name, Gender, how to implement the function through method?
In my real code, I have many fields in the struct, so reset those sensitive fields manually is not my option.
type Agent struct {
Name string
Gender string
Secret1 string
Secret2 string
}
func (a *Agent) HideSecret() {
fmt.Println("Hidding secret...")
new := &Agent{
Name: a.Name,
Gender: a.Gender,
}
a = new
}
I tried a few combination of * and &, but it seems not working...
Please help.
James := Agent{
Name: "James Bond",
Gender: "M",
Secret1: "1234",
Secret2: "abcd",
}
fmt.Printf("[Before] Secret: %s, %s\n", James.Secret1, James.Secret2)
James.HideSecret()
fmt.Printf("[After] Secret: %s, %s\n", James.Secret1, James.Secret2) // not working
The golang playground is here: https://go.dev/play/p/ukJf2Fa0fPI

The receiver is a pointer. You have to update the object that the pointer points to:
func (a *Agent) HideSecret() {
fmt.Println("Hidding secret...")
cleaned := Agent{
Name: a.Name,
Gender: a.Gender,
}
*a=cleaned
}

If you want to just clear the fields, this is an easy solution. it saves some memory
func (a *Agent) HideSecret() {
fmt.Println("Hidding secret...")
a.Secret1 = ""
a.Secret2 = ""
}

Related

how to unmarshal a map from DynamoDB to struct?

There is the following field on dynamo
{
"config": {
"BASE_AUTH_URL_KEY": "https://auth.blab.bob.com",
"BASE_URL": "https://api.dummy.data.com",
"CONN_TIME_OUT_SECONDS": "300000",
"READ_TIME_OUT_SECONDS": "300000"
},
"id": "myConfig"
}
and getting the element with dynamodbattribute
import(
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute")
result, err := svc.GetItem(&dynamodb.GetItemInput{
TableName: aws.String(tableName),
Key: map[string]*dynamodb.AttributeValue{
"id": {
S: aws.String(configId),
},
},
})
this code its working but when i try to retrieve the object its rendered like this
map[config:{
M: {
BASE_AUTH_URL_KEY: {
S: "https://auth.blab.bob.com"
},
CONN_TIME_OUT_SECONDS: {
S: "300000"
},
READ_TIME_OUT_SECONDS: {
S: "300000"
},
BASE_URL: {
S: "https://api.dummy.data.com"
}
}
} id:{
S: "myConfig"
}]
for that reason when i try to unmarshal my object the object unmarshalled returns as {}
type Config struct {
id string
baseAuthUrlKey string
baseUrl string
connectTimeOutSecs string
readTimeOutSecs string
}
item := Config{}
err = dynamodbattribute.UnmarshalMap(result.Item, &item)
how can i assign the value return from the GetItem that seems to be a map to my struct ?
The root of the issue is that your Config struct is incorrectly structured.
I recommend using json-to-go when converting JSON to Go structs; this tool will help you catch issues like this in the future.
Once you get your struct constructed correctly, you'll also notice that your struct fields are not capitalized, meaning they will not be exported (i.e. able to be used by other packages), which is another reason that your UnmarshalMap code will not return the result you are expecting.
Here is a good answer on struct field visibility and its importance, briefly summarized above.
Below is a corrected version of your struct that, combined with your UnmarshalMap code, will correctly allow you to print your item and not receive a {} which is no fun.
type Item struct {
Config struct {
BaseAuthUrlKey string `json:"BASE_AUTH_URL_KEY"`
BaseUrl string `json:"BASE_URL"`
ConnTimeoutSeconds string `json:"CONN_TIME_OUT_SECONDS"`
ReadTimeoutSeconds string `json:"READ_TIME_OUT_SECONDS"`
} `json:"config"`
ID string `json:"id"`
}

Update the fields of one struct to another struct

I have a struct:
type Person struct {
FirstName string
LastName int
Age int
HairColor string
EyeColor string
Height string
}
And I have a function that takes in 2 parameters, and updates the first Person's fields to the second person's fields:
func updateFields(personA *Person, personB Person) {
personA.FirstName = personB.FirstName
personA.LastName = personB.LastName
// Don't want to change Age.
personA.HairColor = personB.HairColor
personA.EyeColor = personB.EyeColor
personA.Height = personB.Height
}
Instead of hard-coding the values to change, how can I loop through the fields and update the first Person to have the same values as the second Person except for the "Age" field?
You can just copy the entire personB into personA and make a backup of the Age field before:
func updateFields(personA *Person, personB Person) {
tempAge := personA.Age
*personA = personB
personA.Age = tempAge
}
For trivially copying every field, you can simply do something like *personA = personB. If you need to "not copy" just one specific field (same field every time), you could probably just save that field's value in a separate variable, copy everything with *personA = personB, and then copy the value back. But this is only useful for very specific situations. It wouldn't allow for example to have a dynamic set of fields not to copy.
If you want to do it with more flexibility, you can do it using reflection, sample code below.
Notice that there may be a few limitations. Notably, you can only set exported fields. Also, if you don't test for these limitations and accidentally try to set a field that's not settable, or with a value of type that's not assignable to that field, etc, the reflect package will happily panic. So it's wise to add a lot of checks before you actually .Set(...) the field.
import (
"fmt"
"reflect"
)
type Person struct {
FirstName string
LastName int
Age int
HairColor string
EyeColor string
Height string
}
func updateFields(personA *Person, personB Person) {
// .Elem() called to dereference the pointer
aVal := reflect.ValueOf(personA).Elem()
aTyp := aVal.Type()
// no .Elem() called here because it's not a pointer
bVal := reflect.ValueOf(personB)
for i := 0; i < aVal.NumField(); i++ {
// skip the "Age" field:
if aTyp.Field(i).Name == "Age" {
continue
}
// you might want to add some checks here,
// eg stuff like .CanSet(), to avoid panics
aVal.Field(i).Set(bVal.Field(i))
}
}
func main() {
b := Person{
FirstName: "Bruno",
LastName: 1,
Age: 2,
HairColor: "hello",
EyeColor: "world",
Height: "tall",
}
a := Person{}
fmt.Println(a)
updateFields(&a, b)
fmt.Println(a)
}

Unmarshal DynamoDBAttributeValues into a struct with different attributes

I read some data from dynamodb. This is what I get
{
Item: {
rating: {
N: "6"
},
pk: {
S: "artist-1"
},
gender: {
S: "woman"
},
sk: {
S: "Alexandra A"
}
}
}
Now I have a struct which looks like this:
type Artist struct {
ArtistID string `json:"id"`
Gender string `json:"gender"`
Name string `json:"name"`
Rating float64 `json:"rating"`
}
Now I do
artist := model.Artist{}
err = dynamodbattribute.UnmarshalMap(result.Item, &artist)
Now I can access e.g. gender with artist.gender. So this is fine, but I can't do this for ArtistId because it's called pk in my dynamodb and I use 'id' for my struct. What is a clean way to solve this? I don't want to replace my 'id' with 'pk' in the struct.
UnmarshalMap() didn't support any given tag Unmarshal. If you don't want to change 'id' with 'pk' in the struct then you have to set value of pk in id key before UnmarshalMap in map manually.
func UnmarshalMap(m map[string]*dynamodb.AttributeValue, out interface{}) error {
m["id"] = m["pk"]
dynamodbattribute.UnmarshalMap(m, out)
}
Tt's better to create a own generic Unmarshal function for this special case tweak and then call UnmarshalMap() inside.
artist := model.Artist{}
err = UnmarshalMap(result.Item, &artist)

Is it possible to use mass assignment in golang?

I have my struct like this:
type User struct {
Id uint64
Email string
}
And I know, that I can declare it like this:
user := User{
Id: 1,
Email: "test#example.com",
}
And I can update it like this:
user.Id = 2
user.Email = "test1#example.com"
Is it possible to use similar construction like for creating but for updating struct?
No, there really isn't an equivalent multi-property setter.
EDIT:
Possibly with reflection you could do something like:
updates := user := User{
Email: "newemail#example.com",
}
//via reflection (pseudo code)
for each field in target object {
if updates.field is not zero value{
set target.field = updates.field
}
}
The reflection part could be factored into a function updateFields(dst, src interface{}), but I would generally say the complexity is not worth the savings. Just have a few lines of setting fields one by one.
It's not the same, but you can use the multivalue return functionality to set them in one line.
https://play.golang.org/p/SGuOhdJieW
package main
import (
"fmt"
)
type User struct {
Id uint64
Email string
Name string
}
func main() {
user := User{
Id: 1,
Email: "test#example.com",
Name: "Peter",
}
fmt.Println(user)
user.Id, user.Email = 2, "also-test#example.com"
fmt.Println(user) // user.Name = "Peter"
}
Do you mean like this?
package main
import (
"fmt"
)
type User struct {
Id uint64
Email string
}
func main() {
user := User{
Id: 1,
Email: "test#example.com",
}
fmt.Println(user)
user = User{
Id: 2,
Email: "also-test#example.com",
}
fmt.Println(user)
}
https://play.golang.org/p/5-ME3p_JaV

Copy one struct to another where structs have same members and different types

I have two struct having the same members, I want to copy one struct to another, see the pseudo code below:
type Common struct {
Gender int
From string
To string
}
type Foo struct {
Id string
Name string
Extra Common
}
type Bar struct {
Id string
Name string
Extra Common
}
Then I have foo of struct Foo, and bar of struct Bar, Is there any way to copy bar from foo?
Use a conversion to change the type. The following code uses a conversion to copy a value of type Foo to a value of type Bar:
foo := Foo{Id: "123", Name: "Joe"}
bar := Bar(foo)
playground example
The conversion only works when the underlying types are identical except for struct tags.
https://github.com/jinzhu/copier (same author of gorm) is also quite a good one, I have nested structs and all I do is:
copier.Copy(&employees, &user)
works great
If you would like to copy or clone to a different struct, I would suggest using deepcopier
It provides nice features like skipping, custom mapping, and forcing. below is an example from github:
Install:
go get -u github.com/ulule/deepcopier
Example:
package main
import (
"fmt"
"github.com/ulule/deepcopier"
)
// Model
type User struct {
// Basic string field
Name string
// Deepcopier supports https://golang.org/pkg/database/sql/driver/#Valuer
Email sql.NullString
}
func (u *User) MethodThatTakesContext(ctx map[string]interface{}) string {
// do whatever you want
return "hello from this method"
}
// Resource
type UserResource struct {
//copy from field "Name"
DisplayName string `deepcopier:"field:Name"`
//this will be skipped in copy
SkipMe string `deepcopier:"skip"`
//this should call method named MethodThatTakesContext
MethodThatTakesContext string `deepcopier:"context"`
Email string `deepcopier:"force"`
}
func main() {
user := &User{
Name: "gilles",
Email: sql.NullString{
Valid: true,
String: "gilles#example.com",
},
}
resource := &UserResource{}
deepcopier.Copy(user).To(resource)
//copied from User's Name field
fmt.Println(resource.DisplayName)//output: gilles
fmt.Println(resource.Email) //output: gilles#example.com
fmt.Println(resource.MethodThatTakesContext) //output: hello from this method
}
Also, some other way you could achieve this is by encoding the source object to JSON and then decode it back to the destination object.
This is another possible answer
type Common struct {
Gender int
From string
To string
}
type Foo struct {
Id string
Name string
Extra Common
}
type Bar struct {
Id string
Name string
Extra Common
}
foo:=Foo{
Id:"123",
Name:"damitha",
Extra: struct {
Gender int
From string
To string
}{Gender:1 , From:"xx", To:"yy" },
}
bar:=*(*Bar)(unsafe.Pointer(&foo))
fmt.Printf("%+v\n",bar)
If you would like to copy or clone to a different struct, I would suggest using deepcopier
It provides nice features like skipping, custom mapping, and forcing.
You can achieve nested struct copy following way.
Install:
go get -u github.com/ulule/deepcopier
Example:
package main
import (
"fmt"
"strconv"
"github.com/ulule/deepcopier"
)
//FieldStruct - Field Struct
type FieldStruct struct {
Name string `deepcopier:"field:TargetName"`
Type string `deepcopier:"field:TargetType"`
}
//SourceStruct - Source Struct
type SourceStruct struct {
Name string `deepcopier:"field:TargetName"`
Age int `deepcopier:"field:TargetAge"`
StringArray []string `deepcopier:"field:TargetStringArray"`
StringToInt string `deepcopier:"context"`
Field FieldStruct
Fields []FieldStruct
}
//TargetFieldStruct - Field Struct
type TargetFieldStruct struct {
TargetName string
TargetType string
}
//TargetStruct - Target Struct
type TargetStruct struct {
TargetName string
TargetAge int
TargetStringArray []string
TargetInt int
TargetField TargetFieldStruct
TargetFields []TargetFieldStruct
}
//write methods
//TargetInt - StringToInt
func (s *SourceStruct) TargetInt() int {
i, _ := strconv.Atoi(s.StringToInt)
return i
}
func main() {
s := &SourceStruct{
Name: "Name",
Age: 12,
StringArray: []string{"1", "2"},
StringToInt: "123",
Field: FieldStruct{
Name: "Field",
Type: "String",
},
Fields: []FieldStruct{
FieldStruct{
Name: "Field1",
Type: "String1",
},
FieldStruct{
Name: "Field2",
Type: "String2",
},
},
}
t := &TargetStruct{}
//coping data into inner struct
deepcopier.Copy(&t.TargetField).From(&s.Field)
// copied array of Struct
for i := range s.Fields {
// init a struct
t.TargetFields = append(t.TargetFields, TargetFieldStruct{})
// coping the data
deepcopier.Copy(&t.TargetFields[i]).From(&s.Fields[i])
}
//Top level copy
deepcopier.Copy(t).From(s)
fmt.Println(t)
}
Output:
&{Name 12 [1 2] 123 {Field String} [{Field1 String1} {Field2 String2}]}

Resources