6

I'm trying to write a helper function that can take in different custom Types in Golang, but I can't figure out how to do it exactly the way I want. Here's the situation (incidentally, I'm building an API that returns JSON objects implementing HAL protocol. This just means that resources and relationships are returned as links and not simply ID').

I have a number of models in my app, such as Student, Principal, School, etc... Each of these models has many fields, some same, some different. Ideally, I'd like a function that can iterate through the fields of a struct, and change another field in the struct. The big challenge is that these structs can be of type Student, Principal, School, etc...

Models:

type Person struct {
    halgo.Links
    Id        bson.ObjectId 
    Firstname string        
    Lastname  string        
    Email     string        
}

type Student struct {
    Person `bson:",inline"`

    School mgo.DBRef
}

type School struct {
    Id      bson.ObjectId
    Address []string     
    Name    string       
    Description string
}

Then, I'd like a function that can basically take any of these structs, iterate through the fields (using reflect), and do something with each field.

I've tried a function that takes interface{} but the problem is you have to type-assert the argument to have access to any of the fields. And even once you have that, you'll still have to write individual functions for each type/model anyways.:

func GenerateLinksHelper(m interface{}) {
...
}

Ultimately, I'm trying to find a way to write a function that takes in a somewhat arbitrary struct and perform operations on fields that may or may not be there.

platwp
  • 3,025
  • 4
  • 15
  • 10
  • You can have your function accept `reflect.Value` and not `interface{}`, pass to it `reflect.ValueOf(whatever)` and then you could traverse the struct fields. – Not_a_Golfer May 12 '14 at 21:40
  • 6
    What kind of "do something" do you have in mind? Go isn't object oriented, and if you try to force it to be, you'll likely have a lot of headaches. Focus on some specific problem you're trying to solve. The answer in general to this kind of problem is "create an interface," but it depends on what you're really trying to do during your iteration. reflect should be your last choice for really general problems (like marshaling arbitrary data), not your first choice for handling known types. – Rob Napier May 13 '14 at 02:43
  • I'm trying to generate a JSON object for each model that converts DBRefs into links. For example, before returning the Student as JSON, I have to iterate through its fields, find any DBrefs, and convert them to relative links, i.e. "school": "/schools/123". I'd like a general way to generate these links, since different models can have the same relationships (i.e. a Principal or Teacher can also have a School). – platwp May 14 '14 at 00:34

1 Answers1

4

I am not sure to understand what you are trying to do, but with reflection, you can see if a field exists or not and then do something with it.

Example derived from the 'laws of reflection' article (http://blog.golang.org/laws-of-reflection). Play: http://play.golang.org/p/neU3j2MYvz

package main

import (
    "fmt"
    "reflect"
)

type T1 struct {
    A int
    B string
}

type T2 struct {
    A int
}

func fct(i interface{}) {
    s := reflect.ValueOf(i).Elem()
    typeOfT := s.Type()
    for i := 0; i < s.NumField(); i++ {
        f := s.Field(i)
        if typeOfT.Field(i).Name == "B" {
            fmt.Printf("I am %s and I have a field B: %s\n", typeOfT.Name(), f.Interface())
        }
    }

}

func main() {
    t1 := T1{23, "skidoo"}
    t2 := T2{23}
    fct(&t1)
    fct(&t2)
}
creack
  • 116,210
  • 12
  • 97
  • 73
  • This answer is as close to solving my problem as possible, but ultimately I was trying to do something that apparently can't be done: http://stackoverflow.com/questions/21495810/golang-how-to-do-type-assertion-for-unknown-interface – platwp May 15 '14 at 14:39