1.8.2 反射操作 Struct

1.8.2 反射操作 Struct #

结构体是 Go 语言中最重要的复合类型,反射在处理结构体时展现出强大的能力。本节将详细介绍如何使用反射来检查、访问和修改结构体的字段,以及如何处理结构体标签。

结构体反射基础 #

获取结构体信息 #

package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    Name    string `json:"name" validate:"required"`
    Age     int    `json:"age" validate:"min=0,max=120"`
    Email   string `json:"email" validate:"email"`
    Address string `json:"address,omitempty"`
}

func main() {
    p := Person{
        Name:    "Alice",
        Age:     30,
        Email:   "[email protected]",
        Address: "123 Main St",
    }

    t := reflect.TypeOf(p)
    v := reflect.ValueOf(p)

    fmt.Printf("Type: %v\n", t)
    fmt.Printf("Kind: %v\n", t.Kind())
    fmt.Printf("Number of fields: %d\n", t.NumField())

    // 遍历所有字段
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        value := v.Field(i)

        fmt.Printf("Field %d:\n", i)
        fmt.Printf("  Name: %s\n", field.Name)
        fmt.Printf("  Type: %v\n", field.Type)
        fmt.Printf("  Value: %v\n", value.Interface())
        fmt.Printf("  Tag: %s\n", field.Tag)
        fmt.Println()
    }
}

按名称访问字段 #

package main

import (
    "fmt"
    "reflect"
)

type User struct {
    ID       int    `db:"id"`
    Username string `db:"username"`
    Password string `db:"password"`
    IsActive bool   `db:"is_active"`
}

func main() {
    user := User{
        ID:       1,
        Username: "john_doe",
        Password: "secret123",
        IsActive: true,
    }

    v := reflect.ValueOf(user)
    t := reflect.TypeOf(user)

    // 按名称获取字段
    fieldNames := []string{"ID", "Username", "IsActive"}

    for _, fieldName := range fieldNames {
        field := v.FieldByName(fieldName)
        if !field.IsValid() {
            fmt.Printf("Field %s not found\n", fieldName)
            continue
        }

        fieldType, _ := t.FieldByName(fieldName)
        fmt.Printf("%s: %v (type: %v, tag: %s)\n",
            fieldName, field.Interface(), field.Type(), fieldType.Tag)
    }

    // 检查字段是否存在
    if field := v.FieldByName("NonExistent"); !field.IsValid() {
        fmt.Println("Field 'NonExistent' does not exist")
    }
}

修改结构体字段 #

设置字段值 #

package main

import (
    "fmt"
    "reflect"
)

type Config struct {
    Host     string
    Port     int
    Debug    bool
    Timeout  float64
    Features []string
}

func main() {
    config := &Config{} // 注意:使用指针才能修改

    v := reflect.ValueOf(config).Elem() // Elem() 获取指针指向的值
    t := reflect.TypeOf(config).Elem()

    // 设置各种类型的字段
    setFieldValue(v, "Host", "localhost")
    setFieldValue(v, "Port", 8080)
    setFieldValue(v, "Debug", true)
    setFieldValue(v, "Timeout", 30.5)
    setFieldValue(v, "Features", []string{"auth", "logging", "metrics"})

    fmt.Printf("Config: %+v\n", *config)
}

func setFieldValue(v reflect.Value, fieldName string, value interface{}) {
    field := v.FieldByName(fieldName)
    if !field.IsValid() {
        fmt.Printf("Field %s not found\n", fieldName)
        return
    }

    if !field.CanSet() {
        fmt.Printf("Field %s cannot be set\n", fieldName)
        return
    }

    valueReflect := reflect.ValueOf(value)

    // 检查类型是否匹配
    if field.Type() != valueReflect.Type() {
        // 尝试类型转换
        if valueReflect.Type().ConvertibleTo(field.Type()) {
            valueReflect = valueReflect.Convert(field.Type())
        } else {
            fmt.Printf("Cannot convert %v to %v for field %s\n",
                valueReflect.Type(), field.Type(), fieldName)
            return
        }
    }

    field.Set(valueReflect)
    fmt.Printf("Set %s = %v\n", fieldName, value)
}

批量设置字段 #

package main

import (
    "fmt"
    "reflect"
    "strconv"
    "strings"
)

type DatabaseConfig struct {
    Host     string `env:"DB_HOST" default:"localhost"`
    Port     int    `env:"DB_PORT" default:"5432"`
    Database string `env:"DB_NAME" default:"myapp"`
    Username string `env:"DB_USER" default:"postgres"`
    Password string `env:"DB_PASS" default:""`
    SSLMode  string `env:"DB_SSL" default:"disable"`
}

// 模拟环境变量
var envVars = map[string]string{
    "DB_HOST": "production-db.example.com",
    "DB_PORT": "5433",
    "DB_NAME": "production_db",
    "DB_USER": "app_user",
    "DB_PASS": "secure_password",
}

func LoadConfigFromEnv(config interface{}) error {
    v := reflect.ValueOf(config)
    if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
        return fmt.Errorf("config must be a pointer to struct")
    }

    v = v.Elem()
    t := v.Type()

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fieldValue := v.Field(i)

        if !fieldValue.CanSet() {
            continue
        }

        // 获取环境变量名和默认值
        envName := field.Tag.Get("env")
        defaultValue := field.Tag.Get("default")

        if envName == "" {
            continue
        }

        // 获取环境变量值
        envValue, exists := envVars[envName]
        if !exists {
            envValue = defaultValue
        }

        if envValue == "" {
            continue
        }

        // 根据字段类型设置值
        if err := setFieldFromString(fieldValue, envValue); err != nil {
            return fmt.Errorf("failed to set field %s: %v", field.Name, err)
        }

        fmt.Printf("Set %s = %s (from %s)\n", field.Name, envValue, envName)
    }

    return nil
}

func setFieldFromString(field reflect.Value, value string) error {
    switch field.Kind() {
    case reflect.String:
        field.SetString(value)
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        intVal, err := strconv.ParseInt(value, 10, 64)
        if err != nil {
            return err
        }
        field.SetInt(intVal)
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        uintVal, err := strconv.ParseUint(value, 10, 64)
        if err != nil {
            return err
        }
        field.SetUint(uintVal)
    case reflect.Float32, reflect.Float64:
        floatVal, err := strconv.ParseFloat(value, 64)
        if err != nil {
            return err
        }
        field.SetFloat(floatVal)
    case reflect.Bool:
        boolVal, err := strconv.ParseBool(value)
        if err != nil {
            return err
        }
        field.SetBool(boolVal)
    default:
        return fmt.Errorf("unsupported field type: %v", field.Kind())
    }
    return nil
}

func main() {
    config := &DatabaseConfig{}

    if err := LoadConfigFromEnv(config); err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }

    fmt.Printf("\nFinal config: %+v\n", *config)
}

结构体标签处理 #

解析和使用标签 #

package main

import (
    "fmt"
    "reflect"
    "strings"
)

type Product struct {
    ID          int     `json:"id" db:"product_id" validate:"required"`
    Name        string  `json:"name" db:"product_name" validate:"required,min=1,max=100"`
    Price       float64 `json:"price" db:"price" validate:"required,min=0"`
    Description string  `json:"description,omitempty" db:"description" validate:"max=500"`
    InStock     bool    `json:"in_stock" db:"in_stock"`
}

// TagInfo 存储标签信息
type TagInfo struct {
    JSONName     string
    DBColumn     string
    Validations  []string
    IsOmitEmpty  bool
}

func parseStructTags(structType reflect.Type) map[string]TagInfo {
    tagMap := make(map[string]TagInfo)

    for i := 0; i < structType.NumField(); i++ {
        field := structType.Field(i)

        tagInfo := TagInfo{}

        // 解析 JSON 标签
        if jsonTag := field.Tag.Get("json"); jsonTag != "" {
            parts := strings.Split(jsonTag, ",")
            tagInfo.JSONName = parts[0]

            // 检查是否有 omitempty
            for _, part := range parts[1:] {
                if part == "omitempty" {
                    tagInfo.IsOmitEmpty = true
                }
            }
        } else {
            tagInfo.JSONName = field.Name
        }

        // 解析数据库标签
        if dbTag := field.Tag.Get("db"); dbTag != "" {
            tagInfo.DBColumn = dbTag
        } else {
            tagInfo.DBColumn = strings.ToLower(field.Name)
        }

        // 解析验证标签
        if validateTag := field.Tag.Get("validate"); validateTag != "" {
            tagInfo.Validations = strings.Split(validateTag, ",")
        }

        tagMap[field.Name] = tagInfo
    }

    return tagMap
}

func main() {
    product := Product{
        ID:          1,
        Name:        "Laptop",
        Price:       999.99,
        Description: "High-performance laptop",
        InStock:     true,
    }

    t := reflect.TypeOf(product)
    tagMap := parseStructTags(t)

    fmt.Println("Struct Tag Analysis:")
    fmt.Println("===================")

    for fieldName, tagInfo := range tagMap {
        fmt.Printf("Field: %s\n", fieldName)
        fmt.Printf("  JSON Name: %s\n", tagInfo.JSONName)
        fmt.Printf("  DB Column: %s\n", tagInfo.DBColumn)
        fmt.Printf("  Omit Empty: %v\n", tagInfo.IsOmitEmpty)
        fmt.Printf("  Validations: %v\n", tagInfo.Validations)
        fmt.Println()
    }
}

基于标签的序列化 #

package main

import (
    "fmt"
    "reflect"
    "strconv"
    "strings"
)

type User struct {
    ID       int    `serialize:"id,int"`
    Name     string `serialize:"name,string"`
    Email    string `serialize:"email,string"`
    Age      int    `serialize:"age,int"`
    IsActive bool   `serialize:"active,bool"`
    Balance  float64 `serialize:"balance,float"`
    Tags     []string `serialize:"tags,slice"`
}

// CustomSerializer 自定义序列化器
type CustomSerializer struct{}

func (cs *CustomSerializer) Serialize(obj interface{}) (map[string]string, error) {
    result := make(map[string]string)

    v := reflect.ValueOf(obj)
    t := reflect.TypeOf(obj)

    // 如果是指针,获取其指向的值
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
        t = t.Elem()
    }

    if v.Kind() != reflect.Struct {
        return nil, fmt.Errorf("expected struct, got %v", v.Kind())
    }

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fieldValue := v.Field(i)

        // 获取序列化标签
        tag := field.Tag.Get("serialize")
        if tag == "" {
            continue
        }

        parts := strings.Split(tag, ",")
        if len(parts) != 2 {
            continue
        }

        key := parts[0]
        dataType := parts[1]

        // 根据类型序列化值
        serializedValue, err := cs.serializeValue(fieldValue, dataType)
        if err != nil {
            return nil, fmt.Errorf("failed to serialize field %s: %v", field.Name, err)
        }

        result[key] = serializedValue
    }

    return result, nil
}

func (cs *CustomSerializer) serializeValue(value reflect.Value, dataType string) (string, error) {
    switch dataType {
    case "int":
        return strconv.FormatInt(value.Int(), 10), nil
    case "float":
        return strconv.FormatFloat(value.Float(), 'f', -1, 64), nil
    case "string":
        return value.String(), nil
    case "bool":
        return strconv.FormatBool(value.Bool()), nil
    case "slice":
        if value.Kind() != reflect.Slice {
            return "", fmt.Errorf("expected slice, got %v", value.Kind())
        }

        var elements []string
        for i := 0; i < value.Len(); i++ {
            elem := value.Index(i)
            elements = append(elements, elem.String())
        }
        return strings.Join(elements, ","), nil
    default:
        return "", fmt.Errorf("unsupported data type: %s", dataType)
    }
}

func (cs *CustomSerializer) Deserialize(data map[string]string, obj interface{}) error {
    v := reflect.ValueOf(obj)
    if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
        return fmt.Errorf("obj must be a pointer to struct")
    }

    v = v.Elem()
    t := v.Type()

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fieldValue := v.Field(i)

        if !fieldValue.CanSet() {
            continue
        }

        // 获取序列化标签
        tag := field.Tag.Get("serialize")
        if tag == "" {
            continue
        }

        parts := strings.Split(tag, ",")
        if len(parts) != 2 {
            continue
        }

        key := parts[0]
        dataType := parts[1]

        // 获取数据
        stringValue, exists := data[key]
        if !exists {
            continue
        }

        // 反序列化值
        if err := cs.deserializeValue(fieldValue, stringValue, dataType); err != nil {
            return fmt.Errorf("failed to deserialize field %s: %v", field.Name, err)
        }
    }

    return nil
}

func (cs *CustomSerializer) deserializeValue(field reflect.Value, value string, dataType string) error {
    switch dataType {
    case "int":
        intVal, err := strconv.ParseInt(value, 10, 64)
        if err != nil {
            return err
        }
        field.SetInt(intVal)
    case "float":
        floatVal, err := strconv.ParseFloat(value, 64)
        if err != nil {
            return err
        }
        field.SetFloat(floatVal)
    case "string":
        field.SetString(value)
    case "bool":
        boolVal, err := strconv.ParseBool(value)
        if err != nil {
            return err
        }
        field.SetBool(boolVal)
    case "slice":
        if value == "" {
            return nil
        }
        elements := strings.Split(value, ",")
        slice := reflect.MakeSlice(field.Type(), len(elements), len(elements))
        for i, elem := range elements {
            slice.Index(i).SetString(elem)
        }
        field.Set(slice)
    default:
        return fmt.Errorf("unsupported data type: %s", dataType)
    }
    return nil
}

func main() {
    user := User{
        ID:       123,
        Name:     "John Doe",
        Email:    "[email protected]",
        Age:      30,
        IsActive: true,
        Balance:  1234.56,
        Tags:     []string{"admin", "premium", "verified"},
    }

    serializer := &CustomSerializer{}

    // 序列化
    serialized, err := serializer.Serialize(user)
    if err != nil {
        fmt.Printf("Serialization error: %v\n", err)
        return
    }

    fmt.Println("Serialized data:")
    for key, value := range serialized {
        fmt.Printf("  %s: %s\n", key, value)
    }

    // 反序列化
    var newUser User
    err = serializer.Deserialize(serialized, &newUser)
    if err != nil {
        fmt.Printf("Deserialization error: %v\n", err)
        return
    }

    fmt.Printf("\nDeserialized user: %+v\n", newUser)
}

嵌套结构体处理 #

处理嵌套结构体 #

package main

import (
    "fmt"
    "reflect"
)

type Address struct {
    Street  string `json:"street"`
    City    string `json:"city"`
    Country string `json:"country"`
    ZipCode string `json:"zip_code"`
}

type Contact struct {
    Phone string `json:"phone"`
    Email string `json:"email"`
}

type Person struct {
    Name    string  `json:"name"`
    Age     int     `json:"age"`
    Address Address `json:"address"`
    Contact Contact `json:"contact"`
}

func printStructFields(obj interface{}, prefix string) {
    v := reflect.ValueOf(obj)
    t := reflect.TypeOf(obj)

    // 如果是指针,获取其指向的值
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
        t = t.Elem()
    }

    if v.Kind() != reflect.Struct {
        fmt.Printf("%s%v (type: %v)\n", prefix, v.Interface(), t)
        return
    }

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fieldValue := v.Field(i)

        fieldName := field.Name
        if jsonTag := field.Tag.Get("json"); jsonTag != "" {
            fieldName = jsonTag
        }

        fmt.Printf("%s%s:\n", prefix, fieldName)

        if fieldValue.Kind() == reflect.Struct {
            // 递归处理嵌套结构体
            printStructFields(fieldValue.Interface(), prefix+"  ")
        } else {
            fmt.Printf("%s  %v (type: %v)\n", prefix, fieldValue.Interface(), fieldValue.Type())
        }
    }
}

func flattenStruct(obj interface{}) map[string]interface{} {
    result := make(map[string]interface{})
    flattenStructHelper(obj, "", result)
    return result
}

func flattenStructHelper(obj interface{}, prefix string, result map[string]interface{}) {
    v := reflect.ValueOf(obj)
    t := reflect.TypeOf(obj)

    if v.Kind() == reflect.Ptr {
        v = v.Elem()
        t = t.Elem()
    }

    if v.Kind() != reflect.Struct {
        result[prefix] = v.Interface()
        return
    }

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fieldValue := v.Field(i)

        fieldName := field.Name
        if jsonTag := field.Tag.Get("json"); jsonTag != "" {
            fieldName = jsonTag
        }

        key := fieldName
        if prefix != "" {
            key = prefix + "." + fieldName
        }

        if fieldValue.Kind() == reflect.Struct {
            flattenStructHelper(fieldValue.Interface(), key, result)
        } else {
            result[key] = fieldValue.Interface()
        }
    }
}

func main() {
    person := Person{
        Name: "Alice Johnson",
        Age:  28,
        Address: Address{
            Street:  "123 Oak Street",
            City:    "San Francisco",
            Country: "USA",
            ZipCode: "94102",
        },
        Contact: Contact{
            Phone: "+1-555-0123",
            Email: "[email protected]",
        },
    }

    fmt.Println("Nested Structure:")
    fmt.Println("================")
    printStructFields(person, "")

    fmt.Println("\nFlattened Structure:")
    fmt.Println("===================")
    flattened := flattenStruct(person)
    for key, value := range flattened {
        fmt.Printf("%s: %v\n", key, value)
    }
}

结构体验证器 #

基于反射的验证器 #

package main

import (
    "fmt"
    "reflect"
    "regexp"
    "strconv"
    "strings"
)

type ValidationError struct {
    Field   string
    Value   interface{}
    Rule    string
    Message string
}

func (e ValidationError) Error() string {
    return fmt.Sprintf("validation failed for field '%s': %s (value: %v, rule: %s)",
        e.Field, e.Message, e.Value, e.Rule)
}

type Validator struct {
    emailRegex *regexp.Regexp
}

func NewValidator() *Validator {
    return &Validator{
        emailRegex: regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`),
    }
}

func (v *Validator) Validate(obj interface{}) []ValidationError {
    var errors []ValidationError

    val := reflect.ValueOf(obj)
    typ := reflect.TypeOf(obj)

    if val.Kind() == reflect.Ptr {
        val = val.Elem()
        typ = typ.Elem()
    }

    if val.Kind() != reflect.Struct {
        return []ValidationError{{
            Field:   "root",
            Value:   obj,
            Rule:    "struct",
            Message: "expected struct type",
        }}
    }

    for i := 0; i < typ.NumField(); i++ {
        field := typ.Field(i)
        fieldValue := val.Field(i)

        validateTag := field.Tag.Get("validate")
        if validateTag == "" {
            continue
        }

        rules := strings.Split(validateTag, ",")
        for _, rule := range rules {
            if err := v.validateRule(field.Name, fieldValue, rule); err != nil {
                errors = append(errors, *err)
            }
        }
    }

    return errors
}

func (v *Validator) validateRule(fieldName string, fieldValue reflect.Value, rule string) *ValidationError {
    rule = strings.TrimSpace(rule)

    switch {
    case rule == "required":
        return v.validateRequired(fieldName, fieldValue)
    case strings.HasPrefix(rule, "min="):
        return v.validateMin(fieldName, fieldValue, rule)
    case strings.HasPrefix(rule, "max="):
        return v.validateMax(fieldName, fieldValue, rule)
    case rule == "email":
        return v.validateEmail(fieldName, fieldValue)
    case strings.HasPrefix(rule, "len="):
        return v.validateLength(fieldName, fieldValue, rule)
    default:
        return &ValidationError{
            Field:   fieldName,
            Value:   fieldValue.Interface(),
            Rule:    rule,
            Message: "unknown validation rule",
        }
    }
}

func (v *Validator) validateRequired(fieldName string, fieldValue reflect.Value) *ValidationError {
    if v.isEmpty(fieldValue) {
        return &ValidationError{
            Field:   fieldName,
            Value:   fieldValue.Interface(),
            Rule:    "required",
            Message: "field is required",
        }
    }
    return nil
}

func (v *Validator) validateMin(fieldName string, fieldValue reflect.Value, rule string) *ValidationError {
    minStr := strings.TrimPrefix(rule, "min=")
    min, err := strconv.ParseFloat(minStr, 64)
    if err != nil {
        return &ValidationError{
            Field:   fieldName,
            Value:   fieldValue.Interface(),
            Rule:    rule,
            Message: "invalid min rule format",
        }
    }

    var value float64
    switch fieldValue.Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        value = float64(fieldValue.Int())
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        value = float64(fieldValue.Uint())
    case reflect.Float32, reflect.Float64:
        value = fieldValue.Float()
    case reflect.String:
        value = float64(len(fieldValue.String()))
    case reflect.Slice, reflect.Array:
        value = float64(fieldValue.Len())
    default:
        return &ValidationError{
            Field:   fieldName,
            Value:   fieldValue.Interface(),
            Rule:    rule,
            Message: "min validation not supported for this type",
        }
    }

    if value < min {
        return &ValidationError{
            Field:   fieldName,
            Value:   fieldValue.Interface(),
            Rule:    rule,
            Message: fmt.Sprintf("value must be at least %v", min),
        }
    }

    return nil
}

func (v *Validator) validateMax(fieldName string, fieldValue reflect.Value, rule string) *ValidationError {
    maxStr := strings.TrimPrefix(rule, "max=")
    max, err := strconv.ParseFloat(maxStr, 64)
    if err != nil {
        return &ValidationError{
            Field:   fieldName,
            Value:   fieldValue.Interface(),
            Rule:    rule,
            Message: "invalid max rule format",
        }
    }

    var value float64
    switch fieldValue.Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        value = float64(fieldValue.Int())
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        value = float64(fieldValue.Uint())
    case reflect.Float32, reflect.Float64:
        value = fieldValue.Float()
    case reflect.String:
        value = float64(len(fieldValue.String()))
    case reflect.Slice, reflect.Array:
        value = float64(fieldValue.Len())
    default:
        return &ValidationError{
            Field:   fieldName,
            Value:   fieldValue.Interface(),
            Rule:    rule,
            Message: "max validation not supported for this type",
        }
    }

    if value > max {
        return &ValidationError{
            Field:   fieldName,
            Value:   fieldValue.Interface(),
            Rule:    rule,
            Message: fmt.Sprintf("value must be at most %v", max),
        }
    }

    return nil
}

func (v *Validator) validateEmail(fieldName string, fieldValue reflect.Value) *ValidationError {
    if fieldValue.Kind() != reflect.String {
        return &ValidationError{
            Field:   fieldName,
            Value:   fieldValue.Interface(),
            Rule:    "email",
            Message: "email validation only applies to strings",
        }
    }

    email := fieldValue.String()
    if email != "" && !v.emailRegex.MatchString(email) {
        return &ValidationError{
            Field:   fieldName,
            Value:   fieldValue.Interface(),
            Rule:    "email",
            Message: "invalid email format",
        }
    }

    return nil
}

func (v *Validator) validateLength(fieldName string, fieldValue reflect.Value, rule string) *ValidationError {
    lenStr := strings.TrimPrefix(rule, "len=")
    expectedLen, err := strconv.Atoi(lenStr)
    if err != nil {
        return &ValidationError{
            Field:   fieldName,
            Value:   fieldValue.Interface(),
            Rule:    rule,
            Message: "invalid len rule format",
        }
    }

    var actualLen int
    switch fieldValue.Kind() {
    case reflect.String:
        actualLen = len(fieldValue.String())
    case reflect.Slice, reflect.Array:
        actualLen = fieldValue.Len()
    default:
        return &ValidationError{
            Field:   fieldName,
            Value:   fieldValue.Interface(),
            Rule:    rule,
            Message: "len validation not supported for this type",
        }
    }

    if actualLen != expectedLen {
        return &ValidationError{
            Field:   fieldName,
            Value:   fieldValue.Interface(),
            Rule:    rule,
            Message: fmt.Sprintf("length must be exactly %d", expectedLen),
        }
    }

    return nil
}

func (v *Validator) isEmpty(fieldValue reflect.Value) bool {
    switch fieldValue.Kind() {
    case reflect.String:
        return fieldValue.String() == ""
    case reflect.Slice, reflect.Array, reflect.Map:
        return fieldValue.Len() == 0
    case reflect.Ptr, reflect.Interface:
        return fieldValue.IsNil()
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        return fieldValue.Int() == 0
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        return fieldValue.Uint() == 0
    case reflect.Float32, reflect.Float64:
        return fieldValue.Float() == 0
    case reflect.Bool:
        return !fieldValue.Bool()
    default:
        return false
    }
}

// 测试结构体
type RegisterRequest struct {
    Username string   `validate:"required,min=3,max=20"`
    Email    string   `validate:"required,email"`
    Password string   `validate:"required,min=8"`
    Age      int      `validate:"required,min=18,max=120"`
    Tags     []string `validate:"max=5"`
    Code     string   `validate:"len=6"`
}

func main() {
    validator := NewValidator()

    // 测试有效数据
    validRequest := RegisterRequest{
        Username: "john_doe",
        Email:    "[email protected]",
        Password: "securepassword123",
        Age:      25,
        Tags:     []string{"developer", "golang"},
        Code:     "ABC123",
    }

    fmt.Println("Validating valid request:")
    if errors := validator.Validate(validRequest); len(errors) > 0 {
        for _, err := range errors {
            fmt.Printf("  Error: %v\n", err)
        }
    } else {
        fmt.Println("  Validation passed!")
    }

    // 测试无效数据
    invalidRequest := RegisterRequest{
        Username: "jo",                    // 太短
        Email:    "invalid-email",         // 无效邮箱
        Password: "123",                   // 太短
        Age:      15,                      // 太小
        Tags:     []string{"a", "b", "c", "d", "e", "f"}, // 太多
        Code:     "12345",                 // 长度不对
    }

    fmt.Println("\nValidating invalid request:")
    if errors := validator.Validate(invalidRequest); len(errors) > 0 {
        for _, err := range errors {
            fmt.Printf("  Error: %v\n", err)
        }
    } else {
        fmt.Println("  Validation passed!")
    }
}

通过本节的学习,您已经掌握了如何使用反射来操作结构体,包括字段访问、修改、标签处理和验证等高级技术。这些技能在开发框架、ORM、配置管理等场景中非常有用。在下一节中,我们将学习如何使用反射来动态调用方法和函数。