我正在尝试创建一个文件解析器,它可以将多种类型的数据(用户,地址等)解析为结构 . 为此,我创建了一个名为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
我知道我这样做很糟糕,我觉得必须有一种方法来实现这种模式,而不必编写特定于每种数据类型的逻辑 . 但是,对于我的生活我无法弄清楚如何实现这一目标 .
1 回答
您似乎正在尝试使用this question中的csv unmarshalling代码 . 当你有一个特定类型的预分配结构传入它时,它就可以工作了 . 您遇到了问题,因为v是静态的接口类型,即使传入的特定值是结构 .
我建议在您的界面和每个子类型上放置一个Unmarshal方法:
另一个很酷的事情是type switch: