Home Articles

从接口获取struct值

Asked
Viewed 839 times
1

我正在尝试创建一个文件解析器,它可以将多种类型的数据(用户,地址等)解析为结构 . 为此,我创建了一个名为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 Answer

  • 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)
    

Related