feat: 添加 fieldx 包,支持基于 Schema 的对象生成
新增 fieldx 包,提供基于预定义 Schema 生成 map[string]any 对象的功能。 主要特性: - 支持固定值字段 (string)、字段引用 (field) 和嵌套对象 (object) - 支持点号分隔的嵌套路径访问 (如 "user.name") - 提供多种 Schema 创建方式 (JSON、Map、编程方式) - 完善的错误处理和文档示例
This commit is contained in:
162
fieldx/schema.go
Normal file
162
fieldx/schema.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package fieldx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.fsdpf.net/go/reflux"
|
||||
)
|
||||
|
||||
// FieldType 定义字段类型
|
||||
type FieldType string
|
||||
|
||||
const (
|
||||
// FieldTypeString 固定字符串值
|
||||
FieldTypeString FieldType = "string"
|
||||
// FieldTypeField 从传入对象中获取字段值
|
||||
FieldTypeField FieldType = "field"
|
||||
// FieldTypeObject 嵌套对象类型
|
||||
FieldTypeObject FieldType = "object"
|
||||
)
|
||||
|
||||
// Field 定义单个字段的 Schema
|
||||
type Field struct {
|
||||
// Type 字段类型: string, field, object
|
||||
Type FieldType `json:"type"`
|
||||
// Value 字段值,根据 Type 不同含义不同:
|
||||
// - string: 固定字符串值
|
||||
// - field: 要获取的字段路径
|
||||
// - object: 忽略,使用 Fields
|
||||
Value string `json:"value,omitempty"`
|
||||
// Fields 当 Type 为 object 时,包含嵌套的字段定义
|
||||
Fields Schema `json:"fields,omitempty"`
|
||||
}
|
||||
|
||||
// Schema 定义字段映射表
|
||||
type Schema map[string]Field
|
||||
|
||||
// Generate 根据 Schema 生成对象
|
||||
// source: 源数据对象,用于 FieldTypeField 类型获取字段值
|
||||
// 返回生成的 map[string]any 对象
|
||||
func (s Schema) Generate(source map[string]any) (map[string]any, error) {
|
||||
// 使用 reflux 包装源数据,提供统一的访问接口
|
||||
rfx := reflux.New(source)
|
||||
|
||||
result := make(map[string]any)
|
||||
err := s.generateFields(rfx, result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// generateFields 递归生成字段
|
||||
func (s Schema) generateFields(source reflux.R, target map[string]any) error {
|
||||
for fieldName, field := range s {
|
||||
value, err := field.generateValue(source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate field %s: %w", fieldName, err)
|
||||
}
|
||||
target[fieldName] = value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateValue 生成字段值
|
||||
func (f Field) generateValue(source reflux.R) (any, error) {
|
||||
switch f.Type {
|
||||
case FieldTypeString:
|
||||
return f.Value, nil
|
||||
case FieldTypeField:
|
||||
return getFieldValue(f.Value, source)
|
||||
case FieldTypeObject:
|
||||
// 嵌套对象
|
||||
nestedResult := make(map[string]any)
|
||||
err := f.Fields.generateFields(source, nestedResult)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nestedResult, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported field type: %s", f.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// getFieldValue 从源对象获取字段值
|
||||
// 支持点号分隔的路径,如 "user.name"
|
||||
func getFieldValue(path string, source reflux.R) (any, error) {
|
||||
// 分割路径
|
||||
parts := strings.Split(path, ".")
|
||||
|
||||
// 检查字段是否存在
|
||||
if !source.Exists(parts...) {
|
||||
return nil, fmt.Errorf("field not found: %s", path)
|
||||
}
|
||||
|
||||
// 获取值
|
||||
accessor := source.Get(parts...)
|
||||
return accessor.Any(), nil
|
||||
}
|
||||
|
||||
// SchemaFromJSON 从 JSON 字符串创建 Schema
|
||||
// 这是最简单直接的方式
|
||||
func SchemaFromJSON(jsonStr string) (Schema, error) {
|
||||
var schema Schema
|
||||
err := json.Unmarshal([]byte(jsonStr), &schema)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||
}
|
||||
return schema, nil
|
||||
}
|
||||
|
||||
// SchemaFromMap 从 map[string]any 创建 Schema
|
||||
// 支持从 JSON 反序列化后的 map 转换
|
||||
func SchemaFromMap(data map[string]any) (Schema, error) {
|
||||
schema := make(Schema)
|
||||
for key, value := range data {
|
||||
field, err := fieldFromMap(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema[key] = field
|
||||
}
|
||||
return schema, nil
|
||||
}
|
||||
|
||||
// fieldFromMap 从 map[string]any 创建 Field
|
||||
func fieldFromMap(data any) (Field, error) {
|
||||
m, ok := data.(map[string]any)
|
||||
if !ok {
|
||||
return Field{}, fmt.Errorf("invalid field data: expected map[string]any, got %T", data)
|
||||
}
|
||||
|
||||
field := Field{}
|
||||
|
||||
// 解析 type
|
||||
if typeVal, ok := m["type"]; ok {
|
||||
if typeStr, ok := typeVal.(string); ok {
|
||||
field.Type = FieldType(typeStr)
|
||||
}
|
||||
}
|
||||
|
||||
// 解析 value
|
||||
if value, ok := m["value"]; ok {
|
||||
if valueStr, ok := value.(string); ok {
|
||||
field.Value = valueStr
|
||||
}
|
||||
}
|
||||
|
||||
// 解析 fields (嵌套)
|
||||
if fields, ok := m["fields"]; ok {
|
||||
if fieldsMap, ok := fields.(map[string]any); ok {
|
||||
nestedSchema, err := SchemaFromMap(fieldsMap)
|
||||
if err != nil {
|
||||
return Field{}, err
|
||||
}
|
||||
field.Fields = nestedSchema
|
||||
}
|
||||
}
|
||||
|
||||
return field, nil
|
||||
}
|
||||
Reference in New Issue
Block a user