From 95946c187cefb6f9585e9d2852030996354fd949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Sat, 22 Mar 2025 18:50:56 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E8=B0=83=E8=AF=95=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E6=95=B0=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builder.go | 25 +++++++++++++++++++++++-- builder_test.go | 17 +++++++++-------- define.go | 6 ++++++ 3 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 define.go diff --git a/builder.go b/builder.go index f5147ba..80d64da 100644 --- a/builder.go +++ b/builder.go @@ -167,6 +167,10 @@ func (b *builderImpl) addNestedField(nameArr []string, pkg string, typ any, tag jsonTag := nameArr[i] fieldName := wrapper.String(jsonTag).SnakeCaseToCamel() parentName := strings.Join(nameArr[:i], ".") + if strings.HasSuffix(parentName, "."+ArraySplit) { + // 数组部分处理 + continue + } parentJsonTag := nameArr[i-1] parentFieldName := wrapper.String(parentJsonTag).SnakeCaseToCamel() fieldTag := fmt.Sprintf(`json:"%s"`, parentJsonTag) @@ -238,8 +242,25 @@ func (b *builderImpl) Build() DynamicStruct { // 说明是顶层了 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(), builderCfg.Tag, false) + loopCnt := 0 + buildInstance := builderCfg.Builder.Build() + val := buildInstance.New() + for { + // (非顶层) 父级结构存在, 将其追加到父级结构中即可, 向前看一步即为父级结构 + parentName := strings.Join(parentNameArr[:len(parentNameArr)-1-loopCnt], ".") + if strings.HasSuffix(parentName, "."+ArraySplit) { + // 说明父级是数组 + loopCnt++ + val = reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(val)), 1, 1).Interface() + fmt.Println(val) + // b.nestedStructTable[parentName].Builder.AddField(builderCfg.Field, "", val, builderCfg.Tag, false) + } else { + // 父级非数组 + b.nestedStructTable[parentName].Builder.AddField(builderCfg.Field, "", val, builderCfg.Tag, false) + break + } + } + } } } diff --git a/builder_test.go b/builder_test.go index 66274e6..f851174 100644 --- a/builder_test.go +++ b/builder_test.go @@ -14,23 +14,24 @@ import ( ) func Test_dynamicStructImpl_New(t *testing.T) { - instance := NewStruct(). + instance := NewStruct(map[string]string{}). /*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).*/ - 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). + 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() - + testByte, _ := json.Marshal(val) + fmt.Println(string(testByte)) data := []byte(` -{ +{"user": [{ "int": 123, "someText": "example", "double": 123.45, @@ -38,7 +39,7 @@ func Test_dynamicStructImpl_New(t *testing.T) { "Slice": [1, 2, 3], "user": {"job":{"address":"beijing","company":{"name":"unknown"}}, "base":{"age": 1800, "name":"baicha"}}, "Anonymous": "avoid to read" -} +}]} `) err := json.Unmarshal(data, &val) fmt.Println(err, val) diff --git a/define.go b/define.go new file mode 100644 index 0000000..a9e497c --- /dev/null +++ b/define.go @@ -0,0 +1,6 @@ +package dynamicstruct + +const ( + // ArraySplit is the string used to split array elements + ArraySplit = "[]" +) -- 2.36.6 From 45b13811c4e1414eec617757b01564f4ff04066c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Sat, 22 Mar 2025 23:29:59 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=97=B6=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E7=BB=93=E6=9E=84=E4=BD=93=E6=94=AF=E6=8C=81=E6=95=B0?= =?UTF-8?q?=E7=BB=84=EF=BC=81=EF=BC=81=EF=BC=81=EF=BC=81=EF=BC=81=EF=BC=81?= =?UTF-8?q?=EF=BC=81=EF=BC=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builder.go | 58 ++++++++++++++++++++++++++++++++----------------- builder_test.go | 21 ++++++++++-------- 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/builder.go b/builder.go index 80d64da..e2633bf 100644 --- a/builder.go +++ b/builder.go @@ -167,10 +167,6 @@ func (b *builderImpl) addNestedField(nameArr []string, pkg string, typ any, tag jsonTag := nameArr[i] fieldName := wrapper.String(jsonTag).SnakeCaseToCamel() parentName := strings.Join(nameArr[:i], ".") - if strings.HasSuffix(parentName, "."+ArraySplit) { - // 数组部分处理 - continue - } parentJsonTag := nameArr[i-1] parentFieldName := wrapper.String(parentJsonTag).SnakeCaseToCamel() fieldTag := fmt.Sprintf(`json:"%s"`, parentJsonTag) @@ -242,31 +238,52 @@ func (b *builderImpl) Build() DynamicStruct { // 说明是顶层了 b.AddField(builderCfg.Field, "", builderCfg.Builder.Build().New(), builderCfg.Tag, false) } else { - loopCnt := 0 - buildInstance := builderCfg.Builder.Build() - val := buildInstance.New() - for { - // (非顶层) 父级结构存在, 将其追加到父级结构中即可, 向前看一步即为父级结构 - parentName := strings.Join(parentNameArr[:len(parentNameArr)-1-loopCnt], ".") - if strings.HasSuffix(parentName, "."+ArraySplit) { - // 说明父级是数组 - loopCnt++ - val = reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(val)), 1, 1).Interface() - fmt.Println(val) - // b.nestedStructTable[parentName].Builder.AddField(builderCfg.Field, "", val, builderCfg.Tag, false) - } else { - // 父级非数组 - b.nestedStructTable[parentName].Builder.AddField(builderCfg.Field, "", val, builderCfg.Tag, false) + // 处理数组 + arrDeep := 0 + 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()).Elem().Interface() + for i := 0; i < arrDeep; i++ { + val = reflect.New(reflect.SliceOf(reflect.TypeOf(val))).Interface() + } + // 解除指针引用 + val = reflect.ValueOf(val).Elem().Interface() + newParamIndex := strings.Join(newParentNameArr, ".") + isTopIndex := len(newParentNameArr) == 1 + if isTopIndex { + // 顶层结构, 数组类型不存在还有其他属性情况, 直接追加字段, 并移除嵌套表里的定义 + b.AddField(b.nestedStructTable[newParamIndex].Field, "", val, b.nestedStructTable[newParamIndex].Tag, false) + delete(b.nestedStructTable, newParamIndex) + // b.nestedStructTable[newParamIndex].Builder.AddField(b.nestedStructTable[newParamIndex].Field, "", val, b.nestedStructTable[newParamIndex].Tag, false) + } else { + // 非顶层结构, 再上探一级 + b.nestedStructTable[strings.Join(newParentNameArr[:len(newParentNameArr)-1], ".")].Builder.AddField(b.nestedStructTable[newParamIndex].Field, "", val, b.nestedStructTable[newParamIndex].Tag, false) + } + } else { + // 非数组 + // (非顶层) 父级结构存在, 将其追加到父级结构中即可, 向前看一步即为父级结构 + b.nestedStructTable[strings.Join(parentNameArr[:len(parentNameArr)-1], ".")].Builder.AddField(builderCfg.Field, "", builderCfg.Builder.Build().New(), builderCfg.Tag, false) } - } } } // 一级字段属性 var structFields []reflect.StructField for _, field := range b.fields { + if strings.Contains(field.name, ArraySplit) { + // TODO : 临时过滤, 后续需要在正确逻辑处正确移除 + continue + } structFields = append(structFields, reflect.StructField{ Name: field.name, PkgPath: field.pkg, @@ -295,6 +312,7 @@ func (f *fieldConfigImpl) SetTag(tag string) FieldConfig { // New 创建动态结构体实例 func (ds *dynamicStructImpl) New() any { + // 不要指针,全部解引用 return reflect.New(ds.definition).Interface() } diff --git a/builder_test.go b/builder_test.go index f851174..5b6bda8 100644 --- a/builder_test.go +++ b/builder_test.go @@ -10,6 +10,7 @@ package dynamicstruct import ( "encoding/json" "fmt" + "reflect" "testing" ) @@ -21,26 +22,28 @@ func Test_dynamicStructImpl_New(t *testing.T) { AddField("Boolean", "", false, "", false). AddField("Slice", "", []int{}, "", false). 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). + 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). + AddField("arr.[].item.name", "", "", `json:"name"`, false). Build() val := instance.New() - testByte, _ := json.Marshal(val) - fmt.Println(string(testByte)) data := []byte(` -{"user": [{ +{ "int": 123, "someText": "example", "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" -}]} + "Anonymous": "avoid to read", + "arr": [{"item":{"name":"item1","test":1}},{"item":{"name":"item2", "test":2}}] +} `) + tType := reflect.TypeOf(val) + fmt.Println(tType, tType.Kind(), tType.Elem().Kind(), tType.Elem().Kind().String()) err := json.Unmarshal(data, &val) fmt.Println(err, val) valByte, _ := json.Marshal(val) -- 2.36.6 From 5289d9278ce489434f0b95e4de1826913d76bfe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Sat, 22 Mar 2025 23:32:09 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=8C=E7=BB=B4=E6=95=B0=E7=BB=84,=20?= =?UTF-8?q?=E7=90=86=E8=AE=BA=E4=B8=8A=E6=94=AF=E6=8C=81=E6=97=A0=E7=BA=BF?= =?UTF-8?q?=E5=A4=9A=E7=BB=B4=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_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/builder_test.go b/builder_test.go index 5b6bda8..3db1c17 100644 --- a/builder_test.go +++ b/builder_test.go @@ -27,6 +27,7 @@ func Test_dynamicStructImpl_New(t *testing.T) { AddField("user.job.address", "", "", `json:"address"`, false). AddField("user.job.company.name", "", "", `json:"name"`, false). AddField("arr.[].item.name", "", "", `json:"name"`, false). + AddField("arr2.[].[].item.name", "", "", `json:"name"`, false). Build() val := instance.New() @@ -39,7 +40,8 @@ func Test_dynamicStructImpl_New(t *testing.T) { "Slice": [1, 2, 3], "user": {"job":{"address":"beijing","company":{"name":"unknown"}}, "base":{"age": 1800, "name":"baicha"}}, "Anonymous": "avoid to read", - "arr": [{"item":{"name":"item1","test":1}},{"item":{"name":"item2", "test":2}}] + "arr": [{"item":{"name":"item1","test":1}},{"item":{"name":"item2", "test":2}}], + "arr2": [[{"item":{"name":"item1","test":1}},{"item":{"name":"item2", "test":2}}]] } `) tType := reflect.TypeOf(val) -- 2.36.6