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、配置管理等场景中非常有用。在下一节中,我们将学习如何使用反射来动态调用方法和函数。