Zac Gross

Code & More

Golang Type Comparisons and Struct Initialization Using Reflection

| Comments

While writing some basic CRUD code for a recent project, I decided to create a base struct containing the redundant fields for all of my data entities (id,date_created, etc…). I then embedded this struct in all data entities and tagged it as inline so json/bson marshalers would treat it accordingly. A problem arose when I wanted to pass newly created data entities from clients into a json webservice. Normally when instantiating a data entity struct I would use the associated creation method (NewSomeEntity()) which would set the appropriate id/created date etc… however the json marshaler is not smart enough to do this as it builds the object graph. If the object graph is only one level deep, you can just run an init function on the new object returned from the marshaler, when the object contains n-levels (n-many relationships) it becomes a problem.

I had two options: I could implement custom marshal interfaces for every data entity struct, or I could write a function that reflects over the object graph after it has been built by the json marshaler and run my initialization function against any new/unintitalized base entity structs. I decided to go with the later option.

There are a few key functions needed to achieve the method described above, mainly: reflecting over an object to get a list of it’s fields, checking the type of the reflected field against your base struct, checking if the reflected field value is uninitialized or in the case of a pointer nil, and finally setting the value of an empty field to an initialized struct.

Here are some code examples:

1
2
3
4
5
6
7
8
9
10
  //base struct for all data entities
  type Entity struct {
      Id        Uuid `bson:"_id,omitempty" json:id`
      CreatedOn time.Time
  }

 //initialization function
 func NewEntity() Entity {
  return Entity{CreatedOn: time.Now(), Id: Uuid.NewUuid()}
 }

Reflect over object and get list of fields

1
2
3
4
5
6
7
8
obj := Entity{};
//retrieve list of fields
r := reflect.ValueOf(obj).Elem()

//iterate over fields
for i := 0; i < r.NumField(); i++ {
      f := r.Field(i)
}

Compare reflected type

1
2
3
if (f.Type() == reflect.TypeOf(Entity{})) {
  //reflected type is of type "Entity"
}

Checking for uninitialized/empty struct

1
2
3
if (f.Interface().(Entity) == Entity{}) {
  //reflected field is an uninitialized entity struct
}

Checking for nil pointer - if you are using pointers you may need to check for a nil pointer rather than an uninitialized struct.

1
2
3
if (f.Kind() == reflect.Pointer && f.IsNil()) {
  //reflected field is a null pointer
}

Finally once an empty field is found set it to an initialized Entity struct

1
2
//sets field f to an initialized Entity struct
f.Set(reflect.ValueOf(NewEntity()))

With the above snippets you can easily build a custom function for iterating over your object graph and initialize empty structs.

Comments