从接口获取struct值

时间:2015-04-18 03:21:55

标签: go

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

答案 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)