From c1b8a812a81a5e6e0b07aec058f3b1c4eaaf3c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 19 Mar 2025 18:42:09 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E8=A7=84=E5=88=92=E7=94=9F=E6=88=90?= =?UTF-8?q?=E5=B5=8C=E5=A5=97=E7=BB=93=E6=9E=84=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builder.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/builder.go b/builder.go index 95a44ff..3662954 100644 --- a/builder.go +++ b/builder.go @@ -1,6 +1,10 @@ package dynamicstruct -import "reflect" +import ( + "fmt" + "reflect" + "strings" +) type ( // Builder 运行时动态生成结构体的接口约束 @@ -86,8 +90,28 @@ func MergeStructs(values ...any) Builder { 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 { + // 判断是否嵌套结构体 + fieldNameArr, isNestedField := b.parseNestedField(name) + if !isNestedField { + // 普通字段 + b.addNormalField(name, pkg, typ, tag, anonymous) + return b + } + // TODO : 添加嵌套的结构体 + fmt.Println(fieldNameArr) + return b +} + +// addNormalField 添加普通无嵌套的字段 +func (b *builderImpl) addNormalField(name string, pkg string, typ any, tag string, anonymous bool) Builder { if existFieldCfg := b.GetField(name); nil != existFieldCfg { // 说明已存在指定名称字段 // 重复添加, 则会议后面的标签以及类型, 覆盖前面的值 @@ -102,7 +126,6 @@ func (b *builderImpl) AddField(name string, pkg string, typ any, tag string, ano anonymous: anonymous, pkg: pkg, }) - return b } // RemoveField 根据名称移除结构体字段 From 22dad8bd35ccb9f1b05505146ccdfeb69c272135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 19 Mar 2025 18:47:15 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E8=A7=A3=E5=86=B3return=20=E5=80=BC?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder.go b/builder.go index 3662954..d705cbf 100644 --- a/builder.go +++ b/builder.go @@ -111,13 +111,13 @@ func (b *builderImpl) AddField(name string, pkg string, typ any, tag string, ano } // addNormalField 添加普通无嵌套的字段 -func (b *builderImpl) addNormalField(name string, pkg string, typ any, tag string, anonymous bool) Builder { +func (b *builderImpl) addNormalField(name string, pkg string, typ any, tag string, anonymous bool) { if existFieldCfg := b.GetField(name); nil != existFieldCfg { // 说明已存在指定名称字段 // 重复添加, 则会议后面的标签以及类型, 覆盖前面的值 existFieldCfg.SetTag(tag) existFieldCfg.SetType(typ) - return b + return } b.fields = append(b.fields, &fieldConfigImpl{ name: name, From 4e072ebcec8478c6269e9237a7410c3fbcc77ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 19 Mar 2025 18:56:28 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E8=A7=84=E5=88=92=E5=B5=8C=E5=A5=97?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E4=BD=93=E7=94=9F=E6=88=90=E7=9A=84=E5=87=BD?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builder.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/builder.go b/builder.go index d705cbf..ecf1859 100644 --- a/builder.go +++ b/builder.go @@ -1,7 +1,6 @@ package dynamicstruct import ( - "fmt" "reflect" "strings" ) @@ -106,7 +105,7 @@ func (b *builderImpl) AddField(name string, pkg string, typ any, tag string, ano return b } // TODO : 添加嵌套的结构体 - fmt.Println(fieldNameArr) + b.addNestedField(fieldNameArr, pkg, typ, tag, anonymous) return b } @@ -128,6 +127,10 @@ func (b *builderImpl) addNormalField(name string, pkg string, typ any, tag strin }) } +// addNestedField 添加嵌套字段 +func (b *builderImpl) addNestedField(nameArr []string, pkg string, typ any, tag string, anonymous bool) { +} + // RemoveField 根据名称移除结构体字段 func (b *builderImpl) RemoveField(name string) Builder { newFieldList := make([]*fieldConfigImpl, 0) From 972562f6bd98902fa8a80b826f823134df21fb5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Thu, 20 Mar 2025 18:50:33 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=B5=8C=E5=A5=97?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E4=BD=93=E8=A7=A3=E6=9E=90=EF=BC=81=EF=BC=81?= =?UTF-8?q?=EF=BC=81=EF=BC=81=EF=BC=81=EF=BC=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builder.go | 83 ++++++++++++++++++++++++++++++++++++++++++++----- builder_test.go | 25 +++++++++------ go.mod | 16 ++++++++++ go.sum | 28 +++++++++++++++++ 4 files changed, 136 insertions(+), 16 deletions(-) create mode 100644 go.sum diff --git a/builder.go b/builder.go index ecf1859..08ce811 100644 --- a/builder.go +++ b/builder.go @@ -1,6 +1,8 @@ package dynamicstruct import ( + "fmt" + "git.zhangdeman.cn/zhangdeman/wrapper" "reflect" "strings" ) @@ -42,8 +44,16 @@ type ( NewMapOfStructs(key any) any } + nestedStruct struct { + Field string + Builder Builder + JsonTag string + } + builderImpl struct { - fields []*fieldConfigImpl + fields []*fieldConfigImpl + nestedStructTable map[string]nestedStruct // 嵌套结构体的定义, 父级路径 => 父级路径下挂接的子路径 + maxFieldDeep int // 字段嵌套最大深度 } fieldConfigImpl struct { @@ -55,14 +65,16 @@ type ( } dynamicStructImpl struct { - definition reflect.Type + structFields []reflect.StructField + definition reflect.Type } ) // NewStruct 获取builder实例 func NewStruct() Builder { return &builderImpl{ - fields: []*fieldConfigImpl{}, + fields: []*fieldConfigImpl{}, + nestedStructTable: make(map[string]nestedStruct), } } @@ -97,6 +109,8 @@ func (b *builderImpl) parseNestedField(fieldName string) ([]string, bool) { // 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 { @@ -104,7 +118,7 @@ func (b *builderImpl) AddField(name string, pkg string, typ any, tag string, ano b.addNormalField(name, pkg, typ, tag, anonymous) return b } - // TODO : 添加嵌套的结构体 + // 添加嵌套的结构体 b.addNestedField(fieldNameArr, pkg, typ, tag, anonymous) return b } @@ -129,6 +143,36 @@ func (b *builderImpl) addNormalField(name string, pkg string, typ any, tag strin // 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 根据名称移除结构体字段 @@ -166,8 +210,33 @@ func (b *builderImpl) GetField(name string) FieldConfig { // 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, @@ -177,9 +246,9 @@ func (b *builderImpl) Build() DynamicStruct { Anonymous: field.anonymous, }) } - return &dynamicStructImpl{ - definition: reflect.StructOf(structFields), + structFields: structFields, + definition: reflect.StructOf(structFields), } } diff --git a/builder_test.go b/builder_test.go index 73bc22b..66274e6 100644 --- a/builder_test.go +++ b/builder_test.go @@ -1,6 +1,6 @@ // Package dynamicstruct ... // -// Description : dynamicstruct ... +// Description : dynamic struct ... // // Author : go_developer@163.com<白茶清欢> // @@ -10,20 +10,24 @@ package dynamicstruct import ( "encoding/json" "fmt" - "reflect" "testing" ) func Test_dynamicStructImpl_New(t *testing.T) { instance := NewStruct(). - AddField("Integer", "", 0, `json:"int"`, false). + /*AddField("Integer", "", 0, `json:"int"`, false). AddField("Text", "", "", `json:"someText"`, false). AddField("Float", "", 0.0, `json:"double"`, false). AddField("Boolean", "", false, "", false). AddField("Slice", "", []int{}, "", false). - AddField("Anonymous", "", "", `json:"-"`, false). - Build(). - New() + AddField("Anonymous", "", "", `json:"-"`, false).*/ + AddField("user.base.age", "", 20, `json:"age"`, false). + AddField("user.base.name", "", "", `json:"name"`, false). + AddField("user.job.address", "", "", `json:"address"`, false). + AddField("user.job.company.name", "", "", `json:"name"`, false). + Build() + + val := instance.New() data := []byte(` { @@ -32,10 +36,13 @@ func Test_dynamicStructImpl_New(t *testing.T) { "double": 123.45, "Boolean": true, "Slice": [1, 2, 3], + "user": {"job":{"address":"beijing","company":{"name":"unknown"}}, "base":{"age": 1800, "name":"baicha"}}, "Anonymous": "avoid to read" } `) - err := json.Unmarshal(data, &instance) - fmt.Println(err) - fmt.Println(reflect.ValueOf(instance).Elem().FieldByName("Integer").Interface()) + err := json.Unmarshal(data, &val) + fmt.Println(err, val) + valByte, _ := json.Marshal(val) + fmt.Println(string(valByte)) + // fmt.Println(reflect.ValueOf(val).Elem().FieldByName("Integer").Interface()) } diff --git a/go.mod b/go.mod index 138da8a..b027de9 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,19 @@ module git.zhangdeman.cn/zhangdeman/dynamic-struct go 1.24.1 + +require ( + git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250227040546-863c03f34bb8 // indirect + git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 // indirect + git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd // indirect + git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e // indirect + git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250302133417-c1588abcb436 // indirect + github.com/BurntSushi/toml v1.5.0 // indirect + github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect + github.com/go-ini/ini v1.67.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..443b25d --- /dev/null +++ b/go.sum @@ -0,0 +1,28 @@ +git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250227040546-863c03f34bb8 h1:VEifPc+vkpEQoX9rj7zxmT1m+IA81XjOxe7+Z1aqWNM= +git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250227040546-863c03f34bb8/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= +git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 h1:gUDlQMuJ4xNfP2Abl1Msmpa3fASLWYkNlqDFF/6GN0Y= +git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0/go.mod h1:VHb9qmhaPDAQDcS6vUiDCamYjZ4R5lD1XtVsh55KsMI= +git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd h1:q7GG14qgXKB4MEXQFOe7/UYebsqMfPaSX80TcPdOosI= +git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd/go.mod h1:+D6uPSljwHywjVY5WSBY4TRVMj26TN5f5cFGEYMldjs= +git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e h1:Q973S6CcWr1ICZhFI1STFOJ+KUImCl2BaIXm6YppBqI= +git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI= +git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250302133417-c1588abcb436 h1:SM4zc54W2wmM72+4pMNQ8iS371H6lj4J8rj8KJKf7pw= +git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250302133417-c1588abcb436/go.mod h1:YJ1FlvFgkfAHkxkt3l5rKKUqEpQkNMbCFDzDmgteEU8= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ= +github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 5ad9c3ba2e0a5477cc9767c0d9462cd69b4fcfc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Thu, 20 Mar 2025 18:58:14 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E4=BD=93,=E4=B8=80=E5=B1=82=E7=BB=93?= =?UTF-8?q?=E6=9E=84=E5=AD=97=E6=AE=B5=E6=9C=AA=E5=AF=BC=E5=87=BA=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builder.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builder.go b/builder.go index 08ce811..9b95f12 100644 --- a/builder.go +++ b/builder.go @@ -125,6 +125,7 @@ func (b *builderImpl) AddField(name string, pkg string, typ any, tag string, ano // 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 { // 说明已存在指定名称字段 // 重复添加, 则会议后面的标签以及类型, 覆盖前面的值 From 38c706ee7bb1d0fdda0394646ea83a4e009fbdb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Fri, 21 Mar 2025 12:30:52 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=A4=96=E9=83=A8?= =?UTF-8?q?=E4=BC=A0=E5=85=A5=E4=BB=BB=E4=B8=80=E5=B1=82=E5=8F=8A=E7=9A=84?= =?UTF-8?q?=E5=AD=97=E6=AE=B5tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builder.go | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/builder.go b/builder.go index 9b95f12..f5147ba 100644 --- a/builder.go +++ b/builder.go @@ -47,13 +47,14 @@ type ( nestedStruct struct { Field string Builder Builder - JsonTag string + Tag string } builderImpl struct { fields []*fieldConfigImpl nestedStructTable map[string]nestedStruct // 嵌套结构体的定义, 父级路径 => 父级路径下挂接的子路径 maxFieldDeep int // 字段嵌套最大深度 + structTagTable map[string]string // 字段路径 => json tag,最高优先级, 没传的时候会使用AddField的tag, 也为空使用手搓json tag } fieldConfigImpl struct { @@ -71,10 +72,12 @@ type ( ) // NewStruct 获取builder实例 -func NewStruct() Builder { +// 传入的tag映射表: 字段路径 => json tag,最高优先级, 没传的时候会使用AddField的tag, 也为空使用手搓json tag +func NewStruct(structTagTable map[string]string) Builder { return &builderImpl{ fields: []*fieldConfigImpl{}, nestedStructTable: make(map[string]nestedStruct), + structTagTable: structTagTable, } } @@ -85,7 +88,7 @@ func ExtendStruct(value ...any) Builder { // MergeStructs 多个结构体合并成一个动态结构体 func MergeStructs(values ...any) Builder { - builder := NewStruct() + builder := NewStruct(map[string]string{}) for _, value := range values { valueOf := reflect.Indirect(reflect.ValueOf(value)) @@ -111,6 +114,13 @@ func (b *builderImpl) parseNestedField(fieldName string) ([]string, bool) { func (b *builderImpl) AddField(name string, pkg string, typ any, tag string, anonymous bool) Builder { // 瞎话线转驼峰, 传入的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 { @@ -159,12 +169,16 @@ func (b *builderImpl) addNestedField(nameArr []string, pkg string, typ any, tag 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(), - JsonTag: parentJsonTag, + Builder: NewStruct(b.structTagTable), + Tag: fieldTag, } } } @@ -220,19 +234,12 @@ func (b *builderImpl) Build() DynamicStruct { // 从深度最深处向上处理 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) + b.AddField(builderCfg.Field, "", builderCfg.Builder.Build().New(), builderCfg.Tag, 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) + b.nestedStructTable[strings.Join(parentNameArr[:len(parentNameArr)-1], ".")].Builder.AddField(builderCfg.Field, "", builderCfg.Builder.Build().New(), builderCfg.Tag, false) } } }