0

I am trying to do updates on structs for use in a PUT API. I need to find out if a field in the new struct has a different value as the same field in the old struct. I have never used reflect before, so I am a bit confused. Here is the code I have, I expected it to only print fields that are different, but it prints every field.

package main

import (
    "fmt"
    "reflect"
)

type Permission struct {
    User       int    `json:"user" db:"user"`
    ObjectId   int    `json:"object_id" db:"object_id"`
    ObjectType string `json:"object_type" db:"object_type"`
    Permission string `json:"codename" db:"codename"`
}

func main() {
    old := Permission{1, 1, "site", "view_site"}
    new := Permission{1, 2, "site", "edit_site"}
    v1 := reflect.ValueOf(old)
    v2 := reflect.ValueOf(new)
    t := reflect.TypeOf(old)
    for i := 0; i < v1.NumField(); i++ {
        if v2.Field(i) != v1.Field(i) {
            fmt.Printf("%v ", t.Field(i).Name)
            fmt.Printf("old: %v ", v1.Field(i))
            fmt.Printf("new: %v ", v2.Field(i))
            fmt.Println("")
        }
    }
}

I guess the reason for this is that each Value is a different struct and so they are not equal, but I cannot seem to figure out how to actually do what I need to do.

1 Answer 1

1

Keep in mind the difference between your reflection values and the values of the underlying struct's fields. This line:

v2.Field(i) != v1.Field(i)

Compares the reflected field of one struct to the reflected field of another. Not the field values, but the reflection of the fields themselves. To get the values, you'd need to use Field(i).Interface(), which would return the field's value as an interface{}.

You can see a working example here: https://play.golang.org/p/0tAkjGTpCeu

Sign up to request clarification or add additional context in comments.

8 Comments

Ha! I knew I was missing something simple. How can I handle a situation where my struct has other structs as members? (in particular Null types from database/sql)
It's reflection, so it's complicated. If you're looking for specific handling for certain types, you could use type assertion or type switching on the value returned from Interface(). For more generic handling, you can check the field's Kind() and if it's a struct, handle it recursively.
As with anything involving reflection, I'd strongly suggest finding an alternative design. Reflection should be a solution of last resort.
There seems to be an awful lot of reflect usage in the base library, both encoding and database packages seem to use reflect a lot for purposes similar to the one I have above. I am not sure I can come up with a better design provided my problem domain involves both serialization and reading from database.
Given that use case I'd say it's a toss-up between using reflection, and writing a helper to use with go generate to generate static-typed code for each type. If performance is of any concern, the latter is probably preferable, but YMMV.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.