Go – How to loop through the fields in a Golang struct to get and set values in an extensible way

goreflection

I have a struct Person.

type Person struct {
    Firstname string       
    Lastname  string       
    Years     uint8       
}

Then I have two instances of this struct, PersonA and PersonB.

PersonA := {"", "Obama", 6}
PersonB := {"President", "Carter", 8}

I want to write a function that copies the values from PersonA to PersonB given some condition for each field (i.e. non-empty). I know how to do this by hard-coding the field names, but I want a function that works even if I change the Person struct.

I know Go reflections is helpful, but the issue is getting and setting the values requires knowing the types, if you want to use something like SetInt. But is there is a "simple" way to do this?

** Javascript analogy **
In Javascript, you could just do a (for property in someObject) to loop through.

(for propt in personA) {
  if personA[propt] != "" {
    // do something
    personB[propt] = personA[propt]
  }
}

Options I've ruled out:

  1. Keeping track of the fields in each struct in a map, then using a combination of FieldByName and the collection of Set* functions in the reflect pkg.

  2. Creating a loop through the fields of Person manually (below). Because I want to do this type of "update" for many other structs (School, Animals, etc…)

    if PersonA.Firstname != "" {
      PersonB.Firstname = PersonA.Firstname 
    }
    

    if PersonA.Years != "" {
      PersonB.Years = PersonA.Years 
    }
    

The question below gets me half-way there, but still isn't extensible to all structs for which I want to utilize this "update" function.

in golang, using reflect, how do you set the value of a struct field?

** Other Helpful Links **
GoLang: Access struct property by name

Best Answer

Use reflect.ValueOf() to convert to concrete type. After that you could use reflect.Value.SetString to set the value you want.

structValue := FooBar{Foo: "foo", Bar: 10}
fields := reflect.TypeOf(structValue)
values := reflect.ValueOf(structValue)

num := fields.NumField()

for i := 0; i < num; i++ {
    field := fields.Field(i)
    value := values.Field(i)
    fmt.Print("Type:", field.Type, ",", field.Name, "=", value, "\n")

    switch value.Kind() {
    case reflect.String:
        v := value.String()
        fmt.Print(v, "\n")
    case reflect.Int:
        v := strconv.FormatInt(value.Int(), 10)
        fmt.Print(v, "\n")
    case reflect.Int32:
        v := strconv.FormatInt(value.Int(), 10)
        fmt.Print(v, "\n")
    case reflect.Int64:
        v := strconv.FormatInt(value.Int(), 10)
        fmt.Print(v, "\n")
    default:
        assert.Fail(t, "Not support type of struct")
    }
}
Related Topic