# Reflux Reflux 是一个 Go 语言包,提供了统一的接口用于访问和操作嵌套的结构体字段、切片元素和映射值。通过字符串路径的方式,可以方便地访问和修改深层嵌套的数据结构。 ## 特性 - 🔍 **统一访问**: 使用字符串路径访问结构体、Map、切片中的任意嵌套值 - 🎯 **点号路径**: 支持点号分割的路径语法,如 `Get("Address.City")` - 🔄 **类型转换**: 基于 [spf13/cast](https://github.com/spf13/cast) 的强大类型转换支持 - ✏️ **修改数据**: 支持通过路径设置和删除值 - 🔗 **链式调用**: Set 和 Delete 方法支持链式调用,如 `rfx.Set("name", "Alice").Delete("age").Set("city", "Beijing")` - 📋 **深度克隆**: Scope 方法和值传递模式创建深度克隆,修改不影响原始数据 - 🎭 **灵活模式**: 支持指针和值两种传递方式,提供不同的数据操作语义 - 🔌 **Interface 支持**: 自动解析 `interface{}` 类型到实际类型 - 🔤 **大小写不敏感**: 支持使用小写字段名访问和修改结构体字段 - 🔑 **键名提取**: Keys 方法可获取结构体或 Map 的所有键名 - 📝 **JSON 集成**: Ptr() 方法返回的指针可直接用于 json.Unmarshal(),实现动态 JSON 反序列化 - 🎯 **类型安全**: 使用反射但保证类型安全 - 🚀 **高性能**: 优化的反射操作,低内存开销 - 📦 **零依赖**: 仅依赖 Go 标准库和 spf13/cast ## 安装 ```bash go get git.fsdpf.net/go/reflux ``` ## 快速开始 ```go package main import ( "fmt" "git.fsdpf.net/go/reflux" ) type User struct { Name string Age int Address struct { City string } } func main() { user := User{ Name: "Alice", Age: 30, } user.Address.City = "Beijing" rfx := reflux.New(&user) // 获取值 name := rfx.Get("Name").String() age := rfx.Get("Age").Int() city := rfx.Get("Address", "City").String() fmt.Printf("Name: %s, Age: %d, City: %s\n", name, age, city) // 链式设置值 rfx.Set("Name", "Bob").Set("Age", 35).Set("Address.City", "Shanghai") fmt.Printf("Updated: %+v\n", user) } ``` ## 核心功能 ### 1. 创建 Reflux Reflux 支持**指针**和**值**两种传递方式,提供不同的数据操作语义: #### 指针模式 - 直接修改原始数据 ```go user := User{Name: "Alice"} rfx := reflux.New(&user) // 传入指针 rfx.Set("Name", "Bob") // user.Name 已被修改为 "Bob" ``` #### 值模式 - 深度克隆,不影响原始数据 ```go user := User{Name: "Alice"} rfx := reflux.New(user) // 传入值 rfx.Set("Name", "Bob") // user.Name 仍然是 "Alice" // rfx 内部是独立的深度克隆 ``` #### Interface 类型支持 Reflux 支持 `interface{}` (或 `any`) 类型,会自动解析到实际类型: ```go // interface{} 包装的值 - 创建深度克隆 var config any = map[string]string{"host": "localhost"} rfx := reflux.New(config) rfx.Set("host", "127.0.0.1") // 原始 config 不受影响 // interface{} 包装的指针 - 直接修改 var data any = &config rfx := reflux.New(data) rfx.Set("host", "127.0.0.1") // config 已被修改 ``` #### 支持的类型 | 类型 | 说明 | 示例 | |-----|------|------| | **Struct** | 结构体 | `New(Person{})` 或 `New(&person)` | | **Map** | 映射 | `New(map[string]any{})` 或 `New(&m)` | | **Slice** | 切片 | `New([]string{})` 或 `New(&s)` | | **Array** | 数组 | `New([3]int{})` 或 `New(&a)` | | **Interface** | 接口 | `New(anyValue)` - 自动解析 | **重要说明**: - ✅ **指针模式**: 传入指针(如 `&user`)时,所有修改都会**直接影响原始数据** - ✅ **值模式**: 传入值(如 `user`)时,会创建**深度克隆**,修改不影响原始数据 - ✅ **深度克隆**: 对于 map 和 slice 等引用类型,值模式也会创建完全独立的副本 - ❌ **不支持**: 基本类型(int, string, bool)、chan、func 等不是容器的类型 ### 2. 获取值 (Get) ```go // 获取顶层字段 name := rfx.Get("Name").String() // 获取嵌套字段 - 使用多个参数 city := rfx.Get("Address", "City").String() // 获取嵌套字段 - 使用点号路径 city := rfx.Get("Address.City").String() // 深层嵌套 - 点号路径 ceoCity := rfx.Get("Company.CEO.Address.City").String() // 混合使用点号和参数 value := rfx.Get("User.Profile", "Settings", "Theme").String() // 获取切片元素 - 使用索引 tag := rfx.Get("Tags", "0").String() // 获取切片元素 - 使用点号 tag := rfx.Get("Tags.0").String() // 获取 Map 值 - 使用多个参数 value := rfx.Get("Meta", "key").String() // 获取 Map 值 - 使用点号 value := rfx.Get("Meta.key").String() // 大小写不敏感访问 (自动转换为正确的字段名) name := rfx.Get("name").String() // 等同于 Get("Name") city := rfx.Get("address.city").String() // 等同于 Get("Address.City") ``` ### 3. 作用域 (Scope) `Scope` 方法创建指定路径的**深度克隆**,在克隆上的修改不会影响原始数据: ```go type Address struct { City string Street string } type Person struct { Name string Address Address } person := Person{ Name: "Alice", Address: Address{ City: "Beijing", Street: "Main St", }, } rfx := reflux.New(&person) // 创建 Address 字段的深度克隆 addressScope := rfx.Scope("Address") // 在克隆上修改值 addressScope.Set("City", "Shanghai") addressScope.Set("Street", "New St") // 原始数据不受影响 fmt.Println(person.Address.City) // 输出: Beijing fmt.Println(person.Address.Street) // 输出: Main St // 克隆数据已修改 fmt.Println(addressScope.Get("City").String()) // 输出: Shanghai fmt.Println(addressScope.Get("Street").String()) // 输出: New St // 不传参数时,克隆整个对象 clone := rfx.Scope() clone.Set("Name", "Bob") fmt.Println(person.Name) // 输出: Alice (原始数据不变) ``` **重要**: `Scope` 返回的是深度克隆,与原始数据完全独立。 ### 4. 设置值 (Set) `Set` 方法使用新的 API 签名: `Set(key string, v any) Reflux`,支持链式调用: ```go // 链式设置多个值 rfx.Set("Name", "Bob").Set("Age", 35).Set("Address.City", "Shanghai") // 设置顶层字段 rfx.Set("Name", "Bob") // 设置嵌套字段 - 使用点号路径 rfx.Set("Address.City", "Shanghai") // 深层嵌套设置 rfx.Set("Company.CEO.Address.City", "Guangzhou") // 设置 Map 值 rfx.Set("Meta.key", "value") // 设置切片元素 rfx.Set("Tags.0", "newValue") // 大小写不敏感设置 rfx.Set("name", "Bob") // 等同于 Set("Name", "Bob") rfx.Set("address.city", "Shanghai") // 等同于 Set("Address.City", "Shanghai") // 自动类型转换 rfx.Set("Age", int32(35)) // int32 -> int // Map 自动初始化 rfx.Set("NewMap.key", "value") // 如果 NewMap 是 nil,会自动初始化 ``` **注意**: 如果设置失败(路径不存在、类型不匹配等),会 panic。 ### 5. 删除值 (Delete) `Delete` 方法支持链式调用,返回 Reflux 自身: ```go // 删除 Map 键 - 使用多个参数 rfx.Delete("Meta", "key") // 删除 Map 键 - 使用点号 rfx.Delete("Meta.key") // 删除切片元素 - 使用索引 rfx.Delete("Tags", "1") // 删除索引 1 的元素 // 删除切片元素 - 使用点号 rfx.Delete("Tags.1") // 链式调用 - 删除多个值 rfx.Delete("Meta.key1").Delete("Meta.key2").Set("Meta.key3", "value") ``` **注意**: 如果删除失败(路径不存在、类型不支持删除等),会 panic。 ### 6. 检查存在性 (Exists) ```go if rfx.Exists("Name") { fmt.Println("Name field exists") } // 使用多个参数检查嵌套字段 if rfx.Exists("Address", "City") { fmt.Println("Nested field exists") } // 使用点号路径检查嵌套字段 if rfx.Exists("Address.City") { fmt.Println("Nested field exists") } // 检查深层嵌套 if rfx.Exists("Company.CEO.Address.City") { fmt.Println("Deep nested field exists") } ``` ### 7. 获取键名 (Keys) ```go type User struct { Name string Age int } user := User{Name: "Alice", Age: 30} rfx := reflux.New(&user) // 获取结构体的所有字段名 keys := rfx.Keys() fmt.Println(keys) // 输出: [Name Age] // 对于 Map config := map[string]any{ "host": "localhost", "port": 8080, } sm2 := reflux.New(&config) keys2 := sm2.Keys() fmt.Println(keys2) // 输出: [host port] // 获取嵌套对象的键名 addressKeys := rfx.Get("Address").Keys() ``` ### 8. 数组操作 (Array) ```go // 将切片转换为 Reflux 数组 tags := rfx.Get("Tags").Array() for i, tag := range tags { fmt.Printf("Tag[%d]: %s\n", i, tag.String()) } // 可以对数组元素进行进一步操作 users := rfx.Get("Users").Array() for i, user := range users { name := user.Get("Name").String() age := user.Get("Age").Int() fmt.Printf("User[%d]: %s, %d\n", i, name, age) } ``` ### 9. 类型转换 #### 基本类型 ```go age := rfx.Get("Age").Int() // int age64 := rfx.Get("Age").Int64() // int64 age32 := rfx.Get("Age").Int32() // int32 age16 := rfx.Get("Age").Int16() // int16 age8 := rfx.Get("Age").Int8() // int8 uage := rfx.Get("Age").Uint() // uint uage64 := rfx.Get("Age").Uint64() // uint64 uage32 := rfx.Get("Age").Uint32() // uint32 uage16 := rfx.Get("Age").Uint16() // uint16 uage8 := rfx.Get("Age").Uint8() // uint8 fage := rfx.Get("Age").Float64() // float64 fage32 := rfx.Get("Age").Float32() // float32 sage := rfx.Get("Age").String() // string active := rfx.Get("Active").Bool() // bool ``` **注意**: 类型转换使用 [spf13/cast](https://github.com/spf13/cast) 库,支持智能类型转换。转换失败会 panic。 #### Map 类型 ```go // map[string]string strMap := rfx.Get("Meta").StringMapString() // map[string]int intMap := rfx.Get("Scores").StringMapInt() // map[string]int64 int64Map := rfx.Get("Scores").StringMapInt64() // map[string]bool boolMap := rfx.Get("Flags").StringMapBool() // map[string]any anyMap := rfx.Get("Data").StringMap() // map[string][]string sliceMap := rfx.Get("Tags").StringMapStringSlice() ``` #### 切片类型 ```go // []string tags := rfx.Get("Tags").StringSlice() // []int scores := rfx.Get("Scores").IntSlice() // []bool flags := rfx.Get("Flags").BoolSlice() // []any items := rfx.Get("Items").Slice() ``` ### 10. 底层访问 ```go // 获取 reflect.Value val := rfx.Get("Name").Value() // 获取指针 ptr := rfx.Get("Name").Ptr() // 获取 any 类型 any := rfx.Get("Name").Any() ``` #### 与 json.Unmarshal() 配合使用 `Ptr()` 方法返回的指针可以直接用于 `json.Unmarshal()`,实现动态的 JSON 反序列化: ```go import ( "encoding/json" "git.fsdpf.net/go/reflux" ) type Person struct { Name string Age int Address Address Meta map[string]string Tags []string } type Address struct { City string Street string ZipCode int } func main() { p := Person{ Name: "Alice", Address: Address{City: "OldCity"}, Meta: make(map[string]string), } rfx := reflux.New(&p) // 1. 更新嵌套结构体 addressJSON := []byte(`{ "City": "Shanghai", "Street": "Nanjing Road", "ZipCode": 200000 }`) json.Unmarshal(addressJSON, rfx.Get("Address").Ptr()) // p.Address 已被完整更新 // 2. 更新 Map 字段 metaJSON := []byte(`{ "key1": "value1", "key2": "value2" }`) json.Unmarshal(metaJSON, rfx.Get("Meta").Ptr()) // p.Meta 已被填充 // 3. 更新切片字段 tagsJSON := []byte(`["go", "rust", "python"]`) json.Unmarshal(tagsJSON, rfx.Get("Tags").Ptr()) // p.Tags 已被替换 // 4. 更新整个对象 personJSON := []byte(`{ "Name": "Bob", "Age": 35 }`) json.Unmarshal(personJSON, rfx.Ptr()) // 整个 Person 对象被更新 } ``` **使用场景**: - 动态配置更新: 从 JSON 文件或 API 响应更新配置的特定部分 - 部分数据刷新: 只更新对象的某个嵌套字段,其他字段保持不变 - 插件系统: 动态加载和更新插件配置 - 热更新: 在运行时更新应用配置而无需重启 ## 使用场景 ### 1. 配置文件处理 ```go type Config struct { Database struct { Host string Port int } Redis struct { Addr string } } config := loadConfig() rfx := reflux.New(&config) // 动态读取配置 dbHost := rfx.Get("Database", "Host").String() dbPort := rfx.Get("Database", "Port").Int() // 链式修改配置 rfx.Set("Database.Host", "localhost").Set("Database.Port", 3306) ``` ### 2. API 响应处理 ```go response := map[string]any{ "user": map[string]any{ "name": "Alice", "age": 30, }, "status": "success", } rfx := reflux.New(&response) userName := rfx.Get("user", "name").String() userAge := rfx.Get("user", "age").Int() // 修改响应数据 rfx.Set("user.name", "Bob").Set("status", "updated") ``` ### 3. 动态表单数据 ```go formData := map[string]any{ "name": "Bob", "email": "bob@example.com", "age": "25", // 字符串形式的数字 } rfx := reflux.New(&formData) // 自动类型转换 name := rfx.Get("name").String() age := rfx.Get("age").Int() // "25" -> 25 // 验证和修改 if age < 18 { rfx.Set("verified", false) } ``` ### 4. 测试数据构建 ```go testUser := User{} rfx := reflux.New(&testUser) // 链式快速设置测试数据 rfx.Set("Name", "TestUser"). Set("Age", 25). Set("Address.City", "Beijing"). Set("Address.Street", "Test St") ``` ### 5. 数据克隆和隔离 ```go original := Config{Host: "localhost"} rfx := reflux.New(&original) // 创建深度克隆进行测试 testConfig := rfx.Scope() testConfig.Set("Host", "test-server").Set("Port", 9999) // 原始配置不受影响 fmt.Println(original.Host) // 输出: localhost ``` ### 6. JSON 动态更新 使用 `Ptr()` 方法配合 `json.Unmarshal()` 实现动态更新: ```go type AppConfig struct { Server ServerConfig Database DatabaseConfig Features map[string]bool } type ServerConfig struct { Host string Port int } type DatabaseConfig struct { Driver string DSN string } func main() { config := AppConfig{ Server: ServerConfig{ Host: "localhost", Port: 8080, }, Features: make(map[string]bool), } rfx := reflux.New(&config) // 从配置文件或 API 只更新 Server 配置 serverJSON := []byte(`{ "Host": "production.example.com", "Port": 443 }`) json.Unmarshal(serverJSON, rfx.Get("Server").Ptr()) // 动态启用功能开关 featuresJSON := []byte(`{ "newFeature": true, "experimentalUI": false }`) json.Unmarshal(featuresJSON, rfx.Get("Features").Ptr()) // config.Server 已更新,config.Database 保持不变 fmt.Printf("%+v\n", config) } ``` 这种方式特别适合: - **微服务配置**: 从配置中心动态更新特定模块配置 - **A/B 测试**: 实时更新功能开关 - **插件热加载**: 更新插件配置而无需重启 - **API 部分响应**: 只处理 API 返回的部分字段 ### 7. 泛型数据处理 (Interface 类型) 使用 interface{} 类型处理未知类型的数据: ```go // 函数返回 any 类型 func loadFromAPI() any { // 可能返回 map 或 struct return map[string]any{ "user": map[string]any{ "name": "Alice", "age": 30, }, } } func processData(data any) { // 自动解析 interface{} 到实际类型 rfx := reflux.New(data) // 值模式,创建深度克隆 // 安全地修改数据 rfx.Set("user.name", "Bob") rfx.Set("user.age", 35) // 原始数据不受影响 } // 或者使用指针模式 func updateData(dataPtr any) { // interface{} 包装指针,直接修改 rfx := reflux.New(dataPtr) rfx.Set("user.name", "Charlie") // 原始数据已被修改 } ``` 使用场景: - **插件系统**: 处理未知结构的插件配置 - **泛型配置**: 统一处理不同格式的配置数据 - **API 适配**: 适配多种 API 响应格式 - **数据转换**: 在不同数据结构间转换 ## 路径语法 Reflux 支持灵活的点号路径语法: ```go // 以下调用是等价的: rfx.Get("a.b.c") rfx.Get("a.b", "c") rfx.Get("a", "b.c") rfx.Get("a", "b", "c") // 空段会被忽略 rfx.Get("a..b") // 等价于 rfx.Get("a", "b") rfx.Get(".a.b.") // 等价于 rfx.Get("a", "b") // 点号路径适用于所有方法 rfx.Set("a.b.c", value) rfx.Delete("a.b.c") rfx.Exists("a.b.c") rfx.Scope("a.b.c") // 访问切片元素 tag := rfx.Get("Tags.0").String() // 访问 Map 值 value := rfx.Get("Config.Database.Host").String() ``` ## API 文档 ### Reflux 接口 ```go type Reflux interface { // 路径操作 Get(p ...string) Reflux Scope(p ...string) Reflux // 修改操作 (支持链式调用) Set(key string, v any) Reflux Delete(p ...string) Reflux Exists(p ...string) bool // 数组和键操作 Array() []Reflux Keys() []string // 底层访问 Value() reflect.Value Ptr() any Any() any // 基本类型转换 Bool() bool Int() int Int8() int8 Int16() int16 Int32() int32 Int64() int64 Uint() uint Uint8() uint8 Uint16() uint16 Uint32() uint32 Uint64() uint64 Float32() float32 Float64() float64 String() string // Map 类型转换 StringMapString() map[string]string StringMapStringSlice() map[string][]string StringMapBool() map[string]bool StringMapInt() map[string]int StringMapInt64() map[string]int64 StringMap() map[string]any // Slice 类型转换 Slice() []any BoolSlice() []bool StringSlice() []string IntSlice() []int } ``` ## 注意事项 1. **指针 vs 值传递**: - 传入**指针** (`New(&data)`) 时,修改会影响原始数据 - 传入**值** (`New(data)`) 时,会创建深度克隆,修改不影响原始数据 - 对于 map 和 slice 等引用类型,值模式也会完全克隆 2. **Interface 支持**: 支持 `interface{}` 类型,会自动解析到实际类型并保持正确的指针/值语义 3. **Scope 行为**: `Scope` 返回深度克隆,修改不会影响原始数据 4. **链式调用**: Set 和 Delete 方法都支持链式调用,返回 Reflux 自身 5. **类型转换**: 使用 spf13/cast 进行类型转换,转换失败会 panic 6. **切片删除**: 删除切片元素会创建新切片并重新赋值 7. **Map 初始化**: 对 nil map 调用 Set 会自动初始化 map 8. **大小写不敏感**: 支持使用小写字段名访问结构体字段 9. **JSON 集成**: Ptr() 返回的指针可以安全地用于 json.Unmarshal() 10. **并发安全**: Reflux 本身不是并发安全的,需要外部同步 11. **错误处理**: 大多数转换、设置和删除操作失败时会 panic,请确保路径和类型正确