Am am attempting to create a file parser that can parse multiple types of data (users, addresses, etc) into a struct. To do this I have created an interface called Datatype:
package main
type Datatype interface {
name() string
}
And several structs that implement the interface:
ex.
package main type User struct { Username string `validate:"nonzero"` FirstName string `validate:"nonzero"` LastName string `validate:"nonzero"` Email string `validate:"regexp=^[0-9a-zA-Z]+@[0-9a-zA-Z]+(\\.[0-9a-zA-Z]+)+$"` Phone string `validate:"min=10"` DateOfBirth string } type Users []User func (u User) name() string { return "user" }
I then read the filename, get the type of data it contains from the name of the file and create an instance of that struct to pass to the parser:
func Parsefile(file string, dtype Datatype) ([]Datatype, error) { // Do stuff in here to parse the file
I did this hoping I could create a parse method that took any one of the structs, detect the type and unmarshall from the csv record. However, what I am finding is that I can't do it this was as I can't seem to get the the underlying type from the interface. Or at least not with my Unmarshall function:
func Unmarshal(reader *csv.Reader, v *Datatype) error {
record, err := reader.Read()
fmt.Println("Record: ", record)
if err != nil {
return err
}
s := reflect.ValueOf(v).Elem()
if s.NumField() != len(record) {
return &FieldMismatch{s.NumField(), len(record)}
}
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
switch f.Type().String() {
case "string":
f.SetString(record[i])
case "int":
ival, err := strconv.ParseInt(record[i], 10, 0)
if err != nil {
return err
}
f.SetInt(ival)
default:
return &UnsupportedType{f.Type().String()}
}
}
return nil
}
In the above function I get the following error when it hits the line trying to determine the number of fields in the Datatype:
panic: reflect: call of reflect.Value.NumField on interface Value
I know I am going about this poorly and I feel there must be a way to achieve this pattern without having to write logic specific to each data type. However, for the life of my I cannot figure out how to achieve this in go.
interface{}
instead of*Datatype
? – captncraig