我正在尝试创建一个文件解析器,它可以将多种类型的数据(用户,地址等)解析为结构。为此,我创建了一个名为Datatype的接口:
package main
type Datatype interface {
name() string
}
实现接口的几个结构:
离。
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" }
然后我读取文件名,从文件名中获取它包含的数据类型,并创建该结构的实例以传递给解析器:
func Parsefile(file string, dtype Datatype) ([]Datatype, error) { // Do stuff in here to parse the file
我这样做希望我可以创建一个解析方法,它接受任何一个结构,检测类型并从csv记录解组。但是,我发现我无法做到这一点,因为我似乎无法从界面中获得底层类型。或者至少不是我的Unmarshall功能:
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
}
在上面的函数中,当它遇到试图确定数据类型中字段数的行时会出现以下错误:
panic: reflect: call of reflect.Value.NumField on interface Value
我知道我这样做很糟糕,我觉得必须有一种方法来实现这种模式,而不必编写特定于每种数据类型的逻辑。但是,对于我的生活,我无法弄清楚如何实现这一目标。
答案 0 :(得分:2)
您似乎正在尝试使用this question中的csv unmarshalling代码。当你有一个特定类型的预分配结构传入它时,它就可以工作了。您遇到了问题,因为v是静态的接口类型,即使传入的特定值是结构。
我建议在你的界面和每个子类型上放置一个Unmarshal方法:
func (u *User) populateFrom(reader *csv.Reader) string {
Unmarshal(reader, u)
}
另一个很酷的事情是type switch:
var i interface{}
switch t := v.(type) {
case *User:
i := t // i is now a *User.
case *Address:
i := t // i is now a *Address.
default:
panic("unknown type")
}
Unmarshal(reader,i)