dynamic-struct/builder.go

278 lines
9.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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),
}
}