282 lines
8.2 KiB
Go
282 lines
8.2 KiB
Go
package dynamicstruct
|
|
|
|
import (
|
|
"fmt"
|
|
"git.zhangdeman.cn/zhangdeman/wrapper"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
type (
|
|
// Builder 运行时动态生成结构体的接口约束
|
|
Builder interface {
|
|
// AddField 添加结构体字段
|
|
AddField(name string, pkg string, typ any, tag string, anonymous bool) Builder
|
|
// RemoveField 移除指定名称的结构体字段
|
|
RemoveField(name string) Builder
|
|
// HasField 检测指定名称的结构体字段是否存在
|
|
HasField(name string) bool
|
|
// GetField 根据名称获取结构体字段定义
|
|
GetField(name string) FieldConfig
|
|
// Build 返回动态定义的结构体.
|
|
Build() DynamicStruct
|
|
}
|
|
|
|
// FieldConfig 结构体字段的定义.
|
|
FieldConfig interface {
|
|
// SetType 设置字段类型.
|
|
SetType(typ any) FieldConfig
|
|
// SetTag 设置字段 tag.
|
|
SetTag(tag string) FieldConfig
|
|
}
|
|
|
|
// DynamicStruct contains defined dynamic struct.
|
|
// This definition can't be changed anymore, once is built.
|
|
// It provides a method for creating new instances of same defintion.
|
|
DynamicStruct interface {
|
|
// New 获取结构体实例, 所有字段值均为对应类型的初始零值
|
|
New() any
|
|
|
|
// NewSliceOfStructs slice实例化
|
|
NewSliceOfStructs() any
|
|
|
|
// NewMapOfStructs map 或者 struct实例化
|
|
NewMapOfStructs(key any) any
|
|
}
|
|
|
|
nestedStruct struct {
|
|
Field string
|
|
Builder Builder
|
|
JsonTag string
|
|
}
|
|
|
|
builderImpl struct {
|
|
fields []*fieldConfigImpl
|
|
nestedStructTable map[string]nestedStruct // 嵌套结构体的定义, 父级路径 => 父级路径下挂接的子路径
|
|
maxFieldDeep int // 字段嵌套最大深度
|
|
}
|
|
|
|
fieldConfigImpl struct {
|
|
name string
|
|
pkg string
|
|
typ any
|
|
tag string
|
|
anonymous bool
|
|
}
|
|
|
|
dynamicStructImpl struct {
|
|
structFields []reflect.StructField
|
|
definition reflect.Type
|
|
}
|
|
)
|
|
|
|
// NewStruct 获取builder实例
|
|
func NewStruct() Builder {
|
|
return &builderImpl{
|
|
fields: []*fieldConfigImpl{},
|
|
nestedStructTable: make(map[string]nestedStruct),
|
|
}
|
|
}
|
|
|
|
// ExtendStruct 基于已有结构体, 生成动态结构体(相当于继承指定的结构体属性)
|
|
func ExtendStruct(value ...any) Builder {
|
|
return MergeStructs(value...)
|
|
}
|
|
|
|
// MergeStructs 多个结构体合并成一个动态结构体
|
|
func MergeStructs(values ...any) Builder {
|
|
builder := NewStruct()
|
|
|
|
for _, value := range values {
|
|
valueOf := reflect.Indirect(reflect.ValueOf(value))
|
|
typeOf := valueOf.Type()
|
|
|
|
for i := 0; i < valueOf.NumField(); i++ {
|
|
fVal := valueOf.Field(i)
|
|
fTyp := typeOf.Field(i)
|
|
builder.(*builderImpl).AddField(fTyp.Name, fTyp.PkgPath, fVal.Interface(), string(fTyp.Tag), fTyp.Anonymous)
|
|
}
|
|
}
|
|
|
|
return builder
|
|
}
|
|
|
|
// IsNestedField 判断是否是嵌套结构体
|
|
func (b *builderImpl) parseNestedField(fieldName string) ([]string, bool) {
|
|
fieldNameArr := strings.Split(strings.Trim(fieldName, "."), ".")
|
|
return fieldNameArr, len(fieldNameArr) > 1
|
|
}
|
|
|
|
// AddField 添加结构体字段
|
|
func (b *builderImpl) AddField(name string, pkg string, typ any, tag string, anonymous bool) Builder {
|
|
// 瞎话线转驼峰, 传入的name实际对应序列化时的json tag
|
|
// name = wrapper.String(name).SnakeCaseToCamel()
|
|
// 判断是否嵌套结构体
|
|
fieldNameArr, isNestedField := b.parseNestedField(name)
|
|
if !isNestedField {
|
|
// 普通字段
|
|
b.addNormalField(name, pkg, typ, tag, anonymous)
|
|
return b
|
|
}
|
|
// 添加嵌套的结构体
|
|
b.addNestedField(fieldNameArr, pkg, typ, tag, anonymous)
|
|
return b
|
|
}
|
|
|
|
// addNormalField 添加普通无嵌套的字段
|
|
func (b *builderImpl) addNormalField(name string, pkg string, typ any, tag string, anonymous bool) {
|
|
name = wrapper.String(name).SnakeCaseToCamel()
|
|
if existFieldCfg := b.GetField(name); nil != existFieldCfg {
|
|
// 说明已存在指定名称字段
|
|
// 重复添加, 则会议后面的标签以及类型, 覆盖前面的值
|
|
existFieldCfg.SetTag(tag)
|
|
existFieldCfg.SetType(typ)
|
|
return
|
|
}
|
|
b.fields = append(b.fields, &fieldConfigImpl{
|
|
name: name,
|
|
typ: typ,
|
|
tag: tag,
|
|
anonymous: anonymous,
|
|
pkg: pkg,
|
|
})
|
|
}
|
|
|
|
// addNestedField 添加嵌套字段
|
|
func (b *builderImpl) addNestedField(nameArr []string, pkg string, typ any, tag string, anonymous bool) {
|
|
if len(nameArr) == 1 {
|
|
// 说明已经是最顶层结构了
|
|
b.addNormalField(nameArr[0], pkg, typ, tag, anonymous)
|
|
return
|
|
}
|
|
if len(nameArr) > b.maxFieldDeep {
|
|
// 设置字段嵌套的最大深度, 由于生成结构体确认深度使用
|
|
b.maxFieldDeep = len(nameArr)
|
|
}
|
|
for i := len(nameArr) - 1; i > 0; i-- {
|
|
jsonTag := nameArr[i]
|
|
fieldName := wrapper.String(jsonTag).SnakeCaseToCamel()
|
|
parentName := strings.Join(nameArr[:i], ".")
|
|
parentJsonTag := nameArr[i-1]
|
|
parentFieldName := wrapper.String(parentJsonTag).SnakeCaseToCamel()
|
|
if len(parentName) > 0 {
|
|
if _, exist := b.nestedStructTable[parentName]; !exist {
|
|
b.nestedStructTable[parentName] = nestedStruct{
|
|
Field: parentFieldName,
|
|
Builder: NewStruct(),
|
|
JsonTag: parentJsonTag,
|
|
}
|
|
}
|
|
}
|
|
if i == len(nameArr)-1 {
|
|
// 最深层此字段, 直接追加到他的父级结构体中即可
|
|
b.nestedStructTable[parentName].Builder.AddField(fieldName, pkg, typ, tag, anonymous)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
// RemoveField 根据名称移除结构体字段
|
|
func (b *builderImpl) RemoveField(name string) Builder {
|
|
newFieldList := make([]*fieldConfigImpl, 0)
|
|
for i := range b.fields {
|
|
if b.fields[i].name == name {
|
|
continue
|
|
}
|
|
newFieldList = append(newFieldList, b.fields[i])
|
|
}
|
|
b.fields = newFieldList
|
|
return b
|
|
}
|
|
|
|
// HasField 是否存在指定字段
|
|
func (b *builderImpl) HasField(name string) bool {
|
|
for i := range b.fields {
|
|
if b.fields[i].name == name {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GetField 根据名称获取字段配置, 不存在, 返回nil
|
|
func (b *builderImpl) GetField(name string) FieldConfig {
|
|
for i := range b.fields {
|
|
if b.fields[i].name == name {
|
|
return b.fields[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Build 构建动态结构体
|
|
func (b *builderImpl) Build() DynamicStruct {
|
|
// 按照嵌套深度, 进行一次回溯处理
|
|
for deep := b.maxFieldDeep - 1; deep > 0; deep-- {
|
|
// 嵌套数据结构
|
|
for parentIndex, builderCfg := range b.nestedStructTable {
|
|
parentNameArr := strings.Split(parentIndex, ".")
|
|
if len(parentNameArr) != deep {
|
|
// 从深度最深处向上处理
|
|
continue
|
|
}
|
|
/*if deep == b.maxFieldDeep-1 {
|
|
// 说明最深层嵌套结构
|
|
parentJsonTag := parentNameArr[deep-1]
|
|
parentField := wrapper.String(parentJsonTag).SnakeCaseToCamel()
|
|
b.AddField(parentField, "", builderCfg.Builder.Build().New(), fmt.Sprintf(`json:"%v"`, parentJsonTag), false)
|
|
continue
|
|
}*/
|
|
if deep == 1 {
|
|
// 说明是顶层了
|
|
b.AddField(builderCfg.Field, "", builderCfg.Builder.Build().New(), fmt.Sprintf(`json:"%v"`, builderCfg.JsonTag), false)
|
|
} else {
|
|
// (非顶层) 父级结构存在, 将其追加到父级结构中即可, 向前看一步即为父级结构
|
|
b.nestedStructTable[strings.Join(parentNameArr[:len(parentNameArr)-1], ".")].Builder.AddField(builderCfg.Field, "", builderCfg.Builder.Build().New(), fmt.Sprintf(`json:"%v"`, builderCfg.JsonTag), false)
|
|
}
|
|
}
|
|
}
|
|
// 一级字段属性
|
|
var structFields []reflect.StructField
|
|
for _, field := range b.fields {
|
|
structFields = append(structFields, reflect.StructField{
|
|
Name: field.name,
|
|
PkgPath: field.pkg,
|
|
Type: reflect.TypeOf(field.typ),
|
|
Tag: reflect.StructTag(field.tag),
|
|
Anonymous: field.anonymous,
|
|
})
|
|
}
|
|
return &dynamicStructImpl{
|
|
structFields: structFields,
|
|
definition: reflect.StructOf(structFields),
|
|
}
|
|
}
|
|
|
|
// SetType 设置字段类型
|
|
func (f *fieldConfigImpl) SetType(typ any) FieldConfig {
|
|
f.typ = typ
|
|
return f
|
|
}
|
|
|
|
// SetTag 设置字段标签
|
|
func (f *fieldConfigImpl) SetTag(tag string) FieldConfig {
|
|
f.tag = tag
|
|
return f
|
|
}
|
|
|
|
// New 创建动态结构体实例
|
|
func (ds *dynamicStructImpl) New() any {
|
|
return reflect.New(ds.definition).Interface()
|
|
}
|
|
|
|
// NewSliceOfStructs 创建动态结构体切片实例
|
|
func (ds *dynamicStructImpl) NewSliceOfStructs() any {
|
|
return reflect.New(reflect.SliceOf(ds.definition)).Interface()
|
|
}
|
|
|
|
// NewMapOfStructs 创建动态结构体map实例
|
|
func (ds *dynamicStructImpl) NewMapOfStructs(key any) any {
|
|
return reflect.New(reflect.MapOf(reflect.Indirect(reflect.ValueOf(key)).Type(), ds.definition)).Interface()
|
|
}
|