diff --git a/fieldx/schema.go b/fieldx/schema.go index a20cb1f..b2f9482 100644 --- a/fieldx/schema.go +++ b/fieldx/schema.go @@ -3,9 +3,9 @@ package fieldx import ( "encoding/json" "fmt" - "strings" "git.fsdpf.net/go/reflux" + "git.fsdpf.net/go/reflux/valuex" ) // FieldType 定义字段类型 @@ -31,6 +31,10 @@ type Field struct { Value string `json:"value,omitempty"` // Fields 当 Type 为 object 时,包含嵌套的字段定义 Fields Schema `json:"fields,omitempty"` + // Required 标识字段是否必须存在,仅对 FieldTypeField 类型有效 + // 如果为 true,字段不存在时会返回错误 + // 如果为 false(默认),字段不存在时返回 nil + Required bool `json:"required,omitempty"` } // Schema 定义字段映射表 @@ -48,17 +52,23 @@ func (s Schema) Generate(source any) (result map[string]any, err error) { } }() - // 使用 reflux 包装源数据,提供统一的访问接口 - rfx := reflux.New(source) + var lkp valuex.Lookuper = valuex.Nil + + if v, ok := source.(valuex.Lookuper); ok { + lkp = v + } else if source != nil { + // 使用 reflux 包装源数据,提供统一的访问接口 + lkp = reflux.New(source) + } result = make(map[string]any) - err = s.generateFields(rfx, result) + err = s.generateFields(lkp, result) return result, err } // generateFields 递归生成字段 -func (s Schema) generateFields(source reflux.R, target map[string]any) error { +func (s Schema) generateFields(source valuex.Lookuper, target map[string]any) error { for fieldName, field := range s { value, err := field.generateValue(source) if err != nil { @@ -70,12 +80,12 @@ func (s Schema) generateFields(source reflux.R, target map[string]any) error { } // generateValue 生成字段值 -func (f Field) generateValue(source reflux.R) (any, error) { +func (f Field) generateValue(source valuex.Lookuper) (any, error) { switch f.Type { case FieldTypeString: return f.Value, nil case FieldTypeField: - return getFieldValue(f.Value, source) + return getFieldValue(f.Value, source, f.Required) case FieldTypeObject: // 嵌套对象 nestedResult := make(map[string]any) @@ -91,17 +101,16 @@ func (f Field) generateValue(source reflux.R) (any, error) { // getFieldValue 从源对象获取字段值 // 支持点号分隔的路径,如 "user.name" -func getFieldValue(path string, source reflux.R) (any, error) { - // 分割路径 - parts := strings.Split(path, ".") +// required: 如果为 true,字段不存在时返回错误;如果为 false,字段不存在时返回 nil +func getFieldValue(path string, source valuex.Lookuper, required bool) (any, error) { + accessor, ok := source.Lookup(path) // 检查字段是否存在 - if !source.Exists(parts...) { + // 只有 required 为 true 时才返回错误 + if !ok && required { return nil, fmt.Errorf("field not found: %s", path) } - // 获取值 - accessor := source.Get(parts...) return accessor.Any(), nil } @@ -153,6 +162,13 @@ func fieldFromMap(data any) (Field, error) { } } + // 解析 required + if required, ok := m["required"]; ok { + if requiredBool, ok := required.(bool); ok { + field.Required = requiredBool + } + } + // 解析 fields (嵌套) if fields, ok := m["fields"]; ok { if fieldsMap, ok := fields.(map[string]any); ok { diff --git a/rfx.go b/rfx.go index a0ec788..4e980f8 100644 --- a/rfx.go +++ b/rfx.go @@ -566,6 +566,16 @@ func (r *rfx) Lookup(path string) (valuex.Accessor, bool) { return valuex.Nil, false } +// MustLookup 根据路径查找并直接返回对应值的访问器 +// 如果路径不存在,返回 valuex.Nil 访问器(所有方法返回零值) +func (r *rfx) MustLookup(path string) valuex.Accessor { + v := r.Get(path) + if v.Exists() { + return v + } + return valuex.Nil +} + // Ptr 返回指向当前值的指针 func (r *rfx) Ptr() any { v := r.value diff --git a/valuex/accessor.go b/valuex/accessor.go index 65b0a3b..8ca61c6 100644 --- a/valuex/accessor.go +++ b/valuex/accessor.go @@ -2,11 +2,20 @@ package valuex import "reflect" -// Accessor 为参数值转换相关接口 -type Accessor interface { +// Lookuper 提供基于路径查找值的能力 +type Lookuper interface { // Lookup 根据路径查找并返回对应值的访问器 Lookup(path string) (Accessor, bool) + // MustLookup 根据路径查找并直接返回对应值的访问器 + // 如果路径不存在,返回 Nil 访问器(所有方法返回零值) + MustLookup(path string) Accessor +} + +// Accessor 为参数值转换相关接口 +type Accessor interface { + Lookuper + // Raw 返回底层的 reflect.Value Raw() reflect.Value diff --git a/valuex/nil_accessor.go b/valuex/nil_accessor.go index b8eed78..03fe967 100644 --- a/valuex/nil_accessor.go +++ b/valuex/nil_accessor.go @@ -14,6 +14,11 @@ func (n *nilAccessor) Lookup(path string) (Accessor, bool) { return n, false } +// MustLookup 总是返回自身,表示路径不存在 +func (n *nilAccessor) MustLookup(path string) Accessor { + return n +} + func (n *nilAccessor) Raw() reflect.Value { return reflect.Value{} }