Try
var timeType = reflect.TypeOf(time.Now())
or
var timeType = reflect.TypeOf(time.Time{})
But then, since you have a fixed set of types for the fields, you can
avoid reflection inside and just a type switch on the field:
switch v := val1.Interface().(type) {
case int:
// v has type int here.
// etc.
case time.Time:
// etc.
}
-rob
case *int:
val2 := field2.Interface().(*int)
if val1 != val2 {
result := &CompareResult{structType.Field(i).Name, strconv.Itoa(*val1), strconv.Itoa(*val2) }
results = append(results, result)
}
break;
Is that the best way to do it?
-rob
import (
"reflect"
"errors"
"fmt"
"strconv"
"time"
"util/config"
)
type CompareResult struct {
FieldName string
Value1 string
Value2 string
}
func Compare (struct1 interface{}, struct2 interface{}) ([]*CompareResult, error) {
if struct1 == nil || struct2 == nil {
return nil, errors.New("One of the inputs cannot be nil.")
}
structVal1 := reflect.ValueOf(struct1)
structVal2 := reflect.ValueOf(struct2)
structType := reflect.TypeOf(struct1)
if !(structVal1.Kind() == reflect.Struct && structVal2.Kind() == reflect.Struct) {
return nil, errors.New("Types must both be structs.")
}
if structVal1.Type() != structVal2.Type() {
return nil, errors.New("Structs must be the same type.")
}
numFields := structVal1.NumField()
results := make([]*CompareResult, 0, numFields)
for i := 0; i < numFields; i++ {
//Get values of the structure's fields
field1 := structVal1.Field(i)
field2 := structVal2.Field(i)
//If the field name is unexported, skip
if structType.Field(i). PkgPath != "" {
continue
}
//Handle nil pointers
isPtr := field1.Kind() == reflect.Ptr
isField1Nil := false
isField2Nil := false
if isPtr {
isField1Nil = field1.IsNil()
isField2Nil = field2.IsNil()
}
//If both fields are nil, continue the loop
if isPtr && isField1Nil && isField2Nil {
continue;
}
switch val1 := field1.Interface().(type) {
case int:
val2 := field2.Interface().(int)
if val1 != val2 {
result := &CompareResult{structType.Field(i).Name, strconv.Itoa(val1), strconv.Itoa(val2) }
results = append(results, result)
}
break;
case *int:
val2 := field2.Interface().(*int)
var int1 int
var int2 int
if isField1Nil {
int1 = 0
} else {
int1 = *val1
}
if isField2Nil {
int2 = 0
} else {
int2 = *val2
}
if int1 != int2 {
result := &CompareResult{structType.Field(i).Name, strconv.Itoa(int1), strconv.Itoa(int2) }
results = append(results, result)
}
break;
case bool:
val2 := field2.Interface().(bool)
if val1 != val2 {
result := &CompareResult{structType.Field(i).Name, strconv.FormatBool(val1), strconv.FormatBool(val2) }
results = append(results, result)
}
break;
case *bool:
val2 := field2.Interface().(*bool)
var bool1 bool
var bool2 bool
if isField1Nil {
bool1 = false
} else {
bool1 = *val1
}
if isField2Nil {
bool2 = false
} else {
bool2 = *val2
}
if bool1 != bool2 {
result := &CompareResult{structType.Field(i).Name, strconv.FormatBool(bool1), strconv.FormatBool(bool2) }
results = append(results, result)
}
break;
case float64:
val2 := field2.Interface().(float64)
if val1 != val2 {
result := &CompareResult{structType.Field(i).Name, strconv.FormatFloat(val1, 'f', 2, 64), strconv.FormatFloat(val2, 'f', 2, 64) }
results = append(results, result)
}
break;
case *float64:
val2 := field2.Interface().(*float64)
var float1 float64
var float2 float64
if isField1Nil {
float1 = 0
} else {
float1 = *val1
}
if isField2Nil {
float2 = 0
} else {
float2 = *val2
}
if float1 != float2 {
result := &CompareResult{structType.Field(i).Name, strconv.FormatFloat(float1, 'f', 2, 64), strconv.FormatFloat(float2, 'f', 2, 64) }
results = append(results, result)
}
break;
case string:
val2 := field2.Interface().(string)
if val1 != val2 {
result := &CompareResult{structType.Field(i).Name, val1, val2 }
results = append(results, result)
}
break;
case *string:
val2 := field2.Interface().(*string)
var string1 string
var string2 string
if isField1Nil {
string1 = ""
} else {
string1 = *val1
}
if isField2Nil {
string2 = ""
} else {
string2 = *val2
}
if string1 != string2 {
result := &CompareResult{structType.Field(i).Name, string1, string2 }
results = append(results, result)
}
break;
case time.Time:
val2 := field2.Interface().(time.Time)
if val1 != val2 {
result := &CompareResult{structType.Field(i).Name, val1.Format(config.DateTimeFormat), val2.Format(config.DateTimeFormat) }
results = append(results, result)
}
break;
case *time.Time:
val2 := field2.Interface().(*time.Time)
var time1 string
var time2 string
if isField1Nil {
time1 = ""
} else {
time1 = val1.Format(config.DateTimeFormat)
}
if isField2Nil {
time2 = ""
} else {
time2 = val2.Format(config.DateTimeFormat)
}
if time1 != time2 {
result := &CompareResult{structType.Field(i).Name, time1, time2 }
results = append(results, result)
}
break;
default:
return nil, errors.New(fmt.Sprintf("Unsupported type: %v", val1))
}
}
return results, nil
Rob,Thanks for all of your feedback. I have posted my final solution before for anyone else who may have the same problem or is interested. Please let me know if there is anything else I can do to improve it.import ("reflect""errors""fmt""strconv""time""util/config")type CompareResult struct {FieldName stringValue1 stringValue2 string
}func Compare (struct1 interface{}, struct2 interface{}) ([]*CompareResult, error) {
if struct1 == nil || struct2 == nil {return nil, errors.New("One of the inputs cannot be nil.")}structVal1 := reflect.ValueOf(struct1)structVal2 := reflect.ValueOf(struct2)structType := reflect.TypeOf(struct1)
if !(structVal1.Kind() == reflect.Struct && structVal2.Kind() == reflect.Struct) {
return nil, errors.New("Types must both be structs.")}if structVal1.Type() != structVal2.Type() {return nil, errors.New("Structs must be the same type.")
}numFields := structVal1.NumField()results := make([]*CompareResult, 0, numFields)
for i := 0; i < numFields; i++ {//Get values of the structure's fieldsfield1 := structVal1.Field(i)field2 := structVal2.Field(i)
//If the field name is unexported, skipif structType.Field(i). PkgPath != "" {
continue}
Rob,Thanks for all of your feedback. I have posted my final solution before for anyone else who may have the same problem or is interested. Please let me know if there is anything else I can do to improve it.
if struct1 == nil || struct2 == nil {
return nil, errors.New("One of the inputs cannot be nil.")}
//Handle nil pointersisPtr := field1.Kind() == reflect.PtrisField1Nil := falseisField2Nil := falseif isPtr {isField1Nil = field1.IsNil()isField2Nil = field2.IsNil()}//If both fields are nil, continue the loopif isPtr && isField1Nil && isField2Nil {continue;}
switch val1 := field1.Interface().(type) {
break;
package compare
import (
"errors"
"fmt"
"reflect"
"strconv"
"time"
"util/config"
)
type CompareResult struct {
FieldName string
Value1 string
Value2 string
}
func Compare(struct1 interface{}, struct2 interface{}) (areEqual bool, differences []*CompareResult, err error) {
if struct1 == nil || struct2 == nil {
return false, nil, errors.New("One of the inputs cannot be nil.")
}
structVal1 := reflect.ValueOf(struct1)
structVal2 := reflect.ValueOf(struct2)
//Handle pointers
structVal1 = reflect.Indirect(structVal1)
structVal2 = reflect.Indirect(structVal2)
if !structVal1.IsValid() || !structVal2.IsValid() {
return false, nil, errors.New(fmt.Sprintf("Types cannot be nil. structVal1 %v - structVal2 %v", structVal1.IsValid(), structVal2.IsValid()))
}
//Cache struct type
structType := structVal1.Type()
if !(structVal1.Kind() == reflect.Struct && structVal2.Kind() == reflect.Struct) {
return false, nil, errors.New(fmt.Sprintf("Types must both be structs. Kind1: %v, Kind2 :v", structVal1.Kind(), structVal2.Kind()))
}
if structVal1.Type() != structVal2.Type() {
return false, nil, errors.New(fmt.Sprintf("Structs must be the same type. Struct1 %v - Stuct2 -%v", structVal1.Type(), structVal2.Type()))
}
numFields := structVal1.NumField()
differences = make([]*CompareResult, 0)
for i := 0; i < numFields; i++ {
//Get values of the structure's fields
field1 := structVal1.Field(i)
field2 := structVal2.Field(i)
//If the field name is unexported, skip
if structType.Field(i).PkgPath != "" {
continue
}
//Handle nil pointers
field1 = reflect.Indirect(field1)
field2 = reflect.Indirect(field2)
field1IsValid := field1.IsValid()
field2IsValid := field2.IsValid()
//If both fields are invalid, continue the loop
if !field1IsValid && !field2IsValid {
continue
}
//If only one field is not valid, replace it with the valid type of the other field
if !field1IsValid {
field1 = reflect.Zero(field2.Type())
}
if !field2IsValid {
field2 = reflect.Zero(field1.Type())
}
switch val1 := field1.Interface().(type) {
case int, bool, string:
val2 := field2.Interface()
if val1 != val2 {
result := &CompareResult{structType.Field(i).Name, fmt.Sprint(val1), fmt.Sprint(val2)}
differences = append(differences, result)
}
case float64:
val2 := field2.Interface().(float64)
if val1 != val2 {
result := &CompareResult{structType.Field(i).Name, strconv.FormatFloat(val1, 'f', 2, 64), strconv.FormatFloat(val2, 'f', 2, 64)}
differences = append(differences, result)
}
case time.Time:
val2 := field2.Interface().(time.Time)
if val1 != val2 {
time1 := val1.Format(config.DisplayDateTimeFormat)
time2 := val2.Format(config.DisplayDateTimeFormat)
result := &CompareResult{structType.Field(i).Name, time1, time2}
differences = append(differences, result)
}
default:
return false, nil, errors.New(fmt.Sprintf("Unsupported type: %v", val1))
}
}
areEqual = len(differences) == 0
return areEqual, differences, nil
Kyle & Steven,Thank you both very much for your feedback. I was able to improve my implementation and I have it posted below in case anyone would like to use it for reference.
package compareimport ("errors""fmt""reflect""strconv""time""util/config")type CompareResult struct {FieldName stringValue1 stringValue2 string
}func Compare(struct1 interface{}, struct2 interface{}) (areEqual bool, differences []*CompareResult, err error) {if struct1 == nil || struct2 == nil {return false, nil, errors.New("One of the inputs cannot be nil.")}structVal1 := reflect.ValueOf(struct1)structVal2 := reflect.ValueOf(struct2)
//Handle pointers
structVal1 = reflect.Indirect(structVal1)structVal2 = reflect.Indirect(structVal2)if !structVal1.IsValid() || !structVal2.IsValid() {return false, nil, errors.New(fmt.Sprintf("Types cannot be nil. structVal1 %v - structVal2 %v", structVal1.IsValid(), structVal2.IsValid()))}
//Cache struct typestructType := structVal1.Type()if !(structVal1.Kind() == reflect.Struct && structVal2.Kind() == reflect.Struct) {
return false, nil, errors.New(fmt.Sprintf("Types must both be structs. Kind1: %v, Kind2 :v", structVal1.Kind(), structVal2.Kind()))}if structVal1.Type() != structVal2.Type() {return false, nil, errors.New(fmt.Sprintf("Structs must be the same type. Struct1 %v - Stuct2 -%v", structVal1.Type(), structVal2.Type()))}numFields := structVal1.NumField()differences = make([]*CompareResult, 0)
for i := 0; i < numFields; i++ {//Get values of the structure's fieldsfield1 := structVal1.Field(i)field2 := structVal2.Field(i)//If the field name is unexported, skipif structType.Field(i).PkgPath != "" {continue}//Handle nil pointers
field1 = reflect.Indirect(field1)field2 = reflect.Indirect(field2)
field1IsValid := field1.IsValid()field2IsValid := field2.IsValid()
//If both fields are invalid, continue the loopif !field1IsValid && !field2IsValid {continue}//If only one field is not valid, replace it with the valid type of the other fieldif !field1IsValid {field1 = reflect.Zero(field2.Type())}if !field2IsValid {field2 = reflect.Zero(field1.Type())}switch val1 := field1.Interface().(type) {
case int, bool, string:val2 := field2.Interface()if val1 != val2 {result := &CompareResult{structType.Field(i).Name, fmt.Sprint(val1), fmt.Sprint(val2)}differences = append(differences, result)}case float64:val2 := field2.Interface().(float64)if val1 != val2 {
result := &CompareResult{structType.Field(i).Name, strconv.FormatFloat(val1, 'f', 2, 64), strconv.FormatFloat(val2, 'f', 2, 64)}differences = append(differences, result)}case time.Time:
val2 := field2.Interface().(time.Time)if val1 != val2 {time1 := val1.Format(config.DisplayDateTimeFormat)time2 := val2.Format(config.DisplayDateTimeFormat)
result := &CompareResult{structType.Field(i).Name, time1, time2}differences = append(differences, result)}default:return false, nil, errors.New(fmt.Sprintf("Unsupported type: %v", val1))
}}areEqual = len(differences) == 0
//Handle nil pointersWhat about non-pointers?
Steven,I think my answer will be that I do not want to handle that case because I have unit tests setup for all of the scenarios that I expect and they are all passing. However, I do not fully understanding the scenario you are describing. Can you please post a small code example that would demonstrate the case you are describing?Luke
I have taken everyone's suggestions into account everyone's suggestions and updated my code and the unit tests. I am posting them both for review and in case anyone else has a similar problem. I am relatively new to the Go programming language and I have learned a tremendous amount from everyone's suggestions. Please let me know if you still see I am missing anything. Thank you so much for your input.Luke
//Initialize differences to ensure length of 0 on return
differences = make([]*CompareResult, 0)
The length and capacity of a nil slice, map, or channel are 0.
switch valid1, valid2 := field1.IsValid(), field2.IsValid(); {
//If both are valid, do nothing
case valid1 && valid2:
//If only field1 is valid, set field2 to reflect.Zero
case valid1:
field2 = reflect.Zero(field1.Type())
//If only field1 is valid, set field2 to reflect.Zero
case valid2:
field1 = reflect.Zero(field2.Type())
//Both are invalid so skip loop body
default:
continue
}
Thanks
Please do not reopen to a four year old thread. Instead please start a new thread describing the problem you have, what you tried, and what happened when you tried.
Thanks
--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/neGz_Rxtxxw/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
If you want to write a general function you will need to use the reflect package, specifically reflect.Value.IsValid to check each field to see if it contains the zero value or not.
i am new to Golang.can you just share some piece of code just to check each field in struct for empty or nil or zero value.
On Tue, Nov 22, 2016 at 4:19 PM, Dave Cheney <da...@cheney.net> wrote:
If you want to write a general function you will need to use the reflect package, specifically reflect.Value.IsValid to check each field to see if it contains the zero value or not.
--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/neGz_Rxtxxw/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.