278 lines
9.1 KiB
Go
278 lines
9.1 KiB
Go
package dynamicstruct
|
||
|
||
import (
|
||
"fmt"
|
||
"git.zhangdeman.cn/zhangdeman/wrapper"
|
||
"reflect"
|
||
"strings"
|
||
)
|
||
|
||
type nestedStruct struct {
|
||
Field string
|
||
Builder IBuilder
|
||
Tag string
|
||
}
|
||
|
||
type builderImpl struct {
|
||
fields []*fieldConfigImpl
|
||
nestedStructTable map[string]nestedStruct // 嵌套结构体的定义, 父级路径 => 父级路径下挂接的子路径
|
||
maxFieldDeep int // 字段嵌套最大深度
|
||
structTagTable map[string]string // 字段路径 => json tag,最高优先级, 没传的时候会使用AddField的tag, 也为空使用手搓json tag
|
||
}
|
||
|
||
// NewStruct 获取builder实例
|
||
// 传入的tag映射表: 字段路径 => json tag,最高优先级, 没传的时候会使用AddField的tag, 也为空使用手搓json tag
|
||
func NewStruct(structTagTable map[string]string) IBuilder {
|
||
return &builderImpl{
|
||
fields: []*fieldConfigImpl{},
|
||
nestedStructTable: make(map[string]nestedStruct),
|
||
structTagTable: structTagTable,
|
||
}
|
||
}
|
||
|
||
// ExtendStruct 基于已有结构体, 生成动态结构体(相当于继承指定的结构体属性)
|
||
func ExtendStruct(value ...any) IBuilder {
|
||
return MergeStructs(value...)
|
||
}
|
||
|
||
// MergeStructs 多个结构体合并成一个动态结构体
|
||
func MergeStructs(values ...any) IBuilder {
|
||
builder := NewStruct(map[string]string{})
|
||
|
||
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) IBuilder {
|
||
// 下划线转驼峰, 传入的name实际对应序列化时的json tag
|
||
// name = wrapper.String(name).SnakeCaseToCamel()
|
||
if cfgTag, exist := b.structTagTable[name]; exist && len(cfgTag) > 0 {
|
||
tag = cfgTag
|
||
} else {
|
||
if len(tag) == 0 {
|
||
tag = fmt.Sprintf(`json:"%s"`, name)
|
||
}
|
||
}
|
||
// 判断是否嵌套结构体
|
||
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()
|
||
fieldTag := fmt.Sprintf(`json:"%s"`, parentJsonTag)
|
||
if tagCfg, exist := b.structTagTable[parentName]; exist && len(tagCfg) > 0 {
|
||
fieldTag = tagCfg
|
||
}
|
||
if len(parentName) > 0 {
|
||
if _, exist := b.nestedStructTable[parentName]; !exist {
|
||
b.nestedStructTable[parentName] = nestedStruct{
|
||
Field: parentFieldName,
|
||
Builder: NewStruct(b.structTagTable),
|
||
Tag: fieldTag,
|
||
}
|
||
}
|
||
}
|
||
if i == len(nameArr)-1 {
|
||
// 最深层此字段, 直接追加到他的父级结构体中即可
|
||
b.nestedStructTable[parentName].Builder.AddField(fieldName, pkg, typ, tag, anonymous)
|
||
continue
|
||
}
|
||
}
|
||
}
|
||
|
||
// RemoveField 根据名称移除结构体字段
|
||
func (b *builderImpl) RemoveField(name string) IBuilder {
|
||
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) IFieldConfig {
|
||
for i := range b.fields {
|
||
if b.fields[i].name == name {
|
||
return b.fields[i]
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// Build 构建动态结构体
|
||
func (b *builderImpl) Build() IDynamicStruct {
|
||
// 按照嵌套深度, 进行一次回溯处理
|
||
for deep := b.maxFieldDeep - 1; deep > 0; deep-- {
|
||
// 嵌套数据结构
|
||
for parentIndex, builderCfg := range b.nestedStructTable {
|
||
if parentIndex == ArrayRootFlag {
|
||
continue
|
||
}
|
||
parentNameArr := strings.Split(parentIndex, ".")
|
||
if len(parentNameArr) != deep {
|
||
// 从深度最深处向上处理
|
||
continue
|
||
}
|
||
if deep == 1 {
|
||
// 说明是顶层了
|
||
b.AddField(builderCfg.Field, "", builderCfg.Builder.Build().New(), builderCfg.Tag, false)
|
||
} else {
|
||
// 处理数组
|
||
arrDeep := 0
|
||
var newParentNameArr []string
|
||
for i := len(parentNameArr) - 1; i >= 0; i-- {
|
||
if parentNameArr[i] != ArraySplit {
|
||
newParentNameArr = parentNameArr[:i+1]
|
||
break
|
||
}
|
||
// 删除
|
||
delete(b.nestedStructTable, strings.Join(parentNameArr[:i+1], "."))
|
||
arrDeep++
|
||
}
|
||
if arrDeep > 0 {
|
||
// 数组嵌套数组配置
|
||
val := reflect.ValueOf(builderCfg.Builder.Build().New()).Interface()
|
||
for i := 0; i < arrDeep; i++ {
|
||
val = reflect.New(reflect.SliceOf(reflect.TypeOf(val).Elem())).Interface()
|
||
// 删除数组记录
|
||
arrPath := strings.Join(parentNameArr[:len(parentNameArr)-i], ".")
|
||
delete(b.nestedStructTable, arrPath)
|
||
}
|
||
newParentIndex := strings.Join(newParentNameArr, ".")
|
||
isTopIndex := len(newParentNameArr) == 1
|
||
if isTopIndex {
|
||
// 顶层结构, 数组类型不存在还有其他属性情况, 直接追加字段, 并移除嵌套表里的定义
|
||
b.AddField(b.nestedStructTable[newParentIndex].Field, "", val, b.nestedStructTable[newParentIndex].Tag, false)
|
||
// b.nestedStructTable[newParamIndex].Builder.AddField(b.nestedStructTable[newParamIndex].Field, "", val, b.nestedStructTable[newParamIndex].Tag, false)
|
||
} else {
|
||
// 非顶层结构, 再上探一级
|
||
newParentName := ""
|
||
fieldName := ""
|
||
if len(newParentNameArr) == 0 {
|
||
// 说明传入的直接就是数组
|
||
newParentName = ArrayRootFlag
|
||
fieldName = ArrayRootFlag
|
||
b.nestedStructTable[newParentName] = nestedStruct{
|
||
Field: fieldName,
|
||
Builder: NewStruct(b.structTagTable),
|
||
Tag: "",
|
||
}
|
||
b.nestedStructTable[newParentName].Builder.AddField(b.nestedStructTable[newParentName].Field, "", reflect.ValueOf(val).Elem().Interface(), b.nestedStructTable[newParentName].Tag, false)
|
||
} else {
|
||
newParentName = strings.Join(newParentNameArr[:len(newParentNameArr)-1], ".")
|
||
b.nestedStructTable[newParentName].Builder.AddField(b.nestedStructTable[newParentIndex].Field, "", reflect.ValueOf(val).Elem().Interface(), b.nestedStructTable[newParentIndex].Tag, false)
|
||
}
|
||
}
|
||
// 嵌套结构中删除数组字段
|
||
delete(b.nestedStructTable, newParentIndex)
|
||
} else {
|
||
// 非数组
|
||
// (非顶层) 父级结构存在, 将其追加到父级结构中即可, 向前看一步即为父级结构
|
||
b.nestedStructTable[strings.Join(parentNameArr[:len(parentNameArr)-1], ".")].Builder.AddField(builderCfg.Field, "", builderCfg.Builder.Build().New(), builderCfg.Tag, false)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if nestedCfg, exist := b.nestedStructTable[ArrayRootFlag]; exist {
|
||
// 说明是传入的根路径就是数组
|
||
return &dynamicStructImpl{
|
||
structFields: nil,
|
||
definition: reflect.TypeOf(nestedCfg.Builder.GetField(ArrayRootFlag).GetType()),
|
||
}
|
||
}
|
||
|
||
// 一级字段属性
|
||
var structFields []reflect.StructField
|
||
for _, field := range b.fields {
|
||
if strings.Contains(field.name, ArraySplit) {
|
||
// 去除数组路径
|
||
continue
|
||
}
|
||
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),
|
||
}
|
||
}
|