268 lines
8.1 KiB
Go
268 lines
8.1 KiB
Go
// Package dynamicstruct ...
|
|
//
|
|
// Description : dynamic struct ...
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 2025-03-19 14:48
|
|
package dynamicstruct
|
|
|
|
import (
|
|
"encoding/json"
|
|
"reflect"
|
|
"testing"
|
|
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
)
|
|
|
|
/*func Test_dynamicStructImpl_New(t *testing.T) {
|
|
instance := NewStruct(map[string]string{}).
|
|
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).
|
|
AddField("arr2.[].[].item.name", "", "", `json:"name"`, false).
|
|
Build()
|
|
|
|
val := instance.New()
|
|
data := []byte(`
|
|
{
|
|
"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",
|
|
"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)
|
|
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)
|
|
fmt.Println(string(valByte))
|
|
// fmt.Println(reflect.ValueOf(val).Elem().FieldByName("Integer").Interface())
|
|
}
|
|
*/
|
|
// TestStruct : 测试结构体
|
|
func TestStruct(t *testing.T) {
|
|
Convey("普通结构体,无嵌套", t, func() {
|
|
data := []byte(`
|
|
{
|
|
"age": 123,
|
|
"name": "example",
|
|
"double": 123.45,
|
|
"tag_map": "ttt",
|
|
"bool": true,
|
|
"slice": [1, 2, 3]
|
|
}
|
|
`)
|
|
instance := NewStruct(map[string]string{
|
|
"test_tag_map": `json:"tag_map"`,
|
|
}).
|
|
AddField("age", "", 0, `json:"age"`, false).
|
|
AddField("name", "", "", `json:"name"`, false).
|
|
AddField("test_tag_map", "", "", "", false).
|
|
AddField("double", "", 0.0, `json:"double"`, false).
|
|
AddField("bool", "", false, "", false).
|
|
AddField("slice", "", []int{}, `json:"slice"`, false).Build()
|
|
val := instance.New()
|
|
err := json.Unmarshal(data, &val)
|
|
So(err, ShouldBeNil)
|
|
reflectType := reflect.TypeOf(val)
|
|
// 需要是结构体类型
|
|
So(reflectType.Elem().Kind(), ShouldEqual, reflect.Struct)
|
|
// 验证数据
|
|
// 数据序列化成功
|
|
targetByte, targetErr := json.Marshal(val)
|
|
So(targetErr, ShouldBeNil)
|
|
// 数据转map成功
|
|
var res map[string]any
|
|
targetUnmarshalErr := json.Unmarshal(targetByte, &res)
|
|
So(targetUnmarshalErr, ShouldBeNil)
|
|
// 验证数据
|
|
So(len(res), ShouldEqual, 6)
|
|
So(res["age"], ShouldEqual, float64(123))
|
|
So(res["name"], ShouldEqual, "example")
|
|
So(res["tag_map"], ShouldEqual, "ttt")
|
|
So(res["double"], ShouldEqual, 123.45)
|
|
So(res["bool"], ShouldEqual, true)
|
|
So(res["slice"], ShouldEqual, []any{float64(1), float64(2), float64(3)})
|
|
})
|
|
}
|
|
|
|
// TestNestedStruct : 测试嵌套结构体
|
|
func TestNestedStruct(t *testing.T) {
|
|
Convey("嵌套结构体", t, func() {
|
|
data := []byte(`
|
|
{
|
|
"user": {"info": {
|
|
"age": 123,
|
|
"name": "example",
|
|
"double": 123.45,
|
|
"bool": true,
|
|
"slice": [1, 2, 3]
|
|
}}}
|
|
`)
|
|
instance := NewStruct(map[string]string{
|
|
"test_tag_map": `json:"tag_map"`,
|
|
}).
|
|
AddField("user.info.age", "", 0, `json:"age"`, false).
|
|
AddField("user.info.name", "", "", `json:"name"`, false).
|
|
Build()
|
|
val := instance.New()
|
|
err := json.Unmarshal(data, &val)
|
|
So(err, ShouldBeNil)
|
|
reflectType := reflect.TypeOf(val)
|
|
// 需要是结构体类型
|
|
So(reflectType.Elem().Kind(), ShouldEqual, reflect.Struct)
|
|
// 验证数据
|
|
// 数据序列化成功
|
|
targetByte, targetErr := json.Marshal(val)
|
|
So(targetErr, ShouldBeNil)
|
|
// 数据转map成功
|
|
var res map[string]any
|
|
targetUnmarshalErr := json.Unmarshal(targetByte, &res)
|
|
So(targetUnmarshalErr, ShouldBeNil)
|
|
So(len(res), ShouldEqual, 1)
|
|
So(res["user"], ShouldNotBeEmpty)
|
|
userMap, ok := res["user"].(map[string]any)
|
|
So(ok, ShouldBeTrue)
|
|
So(len(userMap), ShouldEqual, 1)
|
|
userInfoMap, ok := userMap["info"].(map[string]any)
|
|
So(ok, ShouldBeTrue)
|
|
So(userInfoMap["age"], ShouldEqual, float64(123))
|
|
So(userInfoMap["name"], ShouldEqual, "example")
|
|
})
|
|
}
|
|
|
|
// TestStruct : 测试数组
|
|
func TestArray(t *testing.T) {
|
|
Convey("嵌套结构体带多级数组", t, func() {
|
|
data := []byte(`
|
|
{
|
|
"user": {"list":[[[{
|
|
"age": 123,
|
|
"name": "example",
|
|
"double": 123.45,
|
|
"bool": true,
|
|
"slice": [1, 2, 3]
|
|
}]]]}
|
|
}
|
|
`)
|
|
instance := NewStruct(map[string]string{
|
|
"test_tag_map": `json:"tag_map"`,
|
|
}).
|
|
AddField("user.list.[].[].[].age", "", 0, `json:"age"`, false).
|
|
AddField("user.list.[].[].[].name", "", "", `json:"name"`, false).
|
|
Build()
|
|
val := instance.New()
|
|
err := json.Unmarshal(data, &val)
|
|
So(err, ShouldBeNil)
|
|
reflectType := reflect.TypeOf(val)
|
|
// 需要是结构体类型
|
|
So(reflectType.Elem().Kind(), ShouldEqual, reflect.Struct)
|
|
// 验证数据
|
|
// 数据序列化成功
|
|
targetByte, targetErr := json.Marshal(val)
|
|
So(targetErr, ShouldBeNil)
|
|
// 数据转map成功
|
|
var res map[string]map[string][][][]any
|
|
targetUnmarshalErr := json.Unmarshal(targetByte, &res)
|
|
So(targetUnmarshalErr, ShouldBeNil)
|
|
So(len(res), ShouldEqual, 1)
|
|
So(res["user"], ShouldNotBeEmpty)
|
|
So(len(res["user"]), ShouldEqual, 1)
|
|
So(len(res["user"]["list"][0]), ShouldEqual, 1)
|
|
So(len(res["user"]["list"][0][0]), ShouldEqual, 1)
|
|
userMap, ok := res["user"]["list"][0][0][0].(map[string]any)
|
|
So(ok, ShouldBeTrue)
|
|
So(len(userMap), ShouldEqual, 2)
|
|
So(userMap["age"], ShouldEqual, float64(123))
|
|
So(userMap["name"], ShouldEqual, "example")
|
|
So(userMap["double"], ShouldBeNil)
|
|
})
|
|
}
|
|
|
|
func TestFieldManage(t *testing.T) {
|
|
Convey("结构体字段管理", t, func() {
|
|
instance := NewStruct(map[string]string{
|
|
"test_tag_map": `json:"tag_map"`,
|
|
}).AddField("age", "", 0, `json:"age"`, false).
|
|
AddField("name", "", "", `json:"name"`, false)
|
|
So(instance.HasField("Name"), ShouldBeTrue)
|
|
So(instance.GetField("Name"), ShouldNotBeNil)
|
|
instance.RemoveField("Name")
|
|
So(instance.HasField("Name"), ShouldBeFalse)
|
|
So(instance.GetField("Name"), ShouldBeNil)
|
|
So(instance.HasField("Age"), ShouldBeTrue)
|
|
})
|
|
}
|
|
|
|
func TestExtendStruct(t *testing.T) {
|
|
Convey("结构体字段继承", t, func() {
|
|
type User struct {
|
|
Name string `json:"name"`
|
|
Age int `json:"age"`
|
|
}
|
|
type Company struct {
|
|
Address string `json:"address"`
|
|
Logo string `json:"logo"`
|
|
Age float64 `json:"company_age"` // 应该覆盖掉 User.Age
|
|
}
|
|
instance := ExtendStruct(User{}, Company{})
|
|
So(instance.HasField("Name"), ShouldBeTrue)
|
|
So(instance.HasField("Age"), ShouldBeTrue)
|
|
So(instance.HasField("Address"), ShouldBeTrue)
|
|
So(instance.HasField("Logo"), ShouldBeTrue)
|
|
So(reflect.TypeOf(instance.GetField("Age").GetType()).Kind(), ShouldEqual, reflect.Float64)
|
|
So(instance.GetField("Age").GetTag(), ShouldEqual, `json:"company_age"`)
|
|
})
|
|
}
|
|
|
|
func TestArrayRoot(t *testing.T) {
|
|
Convey("嵌套结构体带多级数组", t, func() {
|
|
data := []byte(`
|
|
[[[{
|
|
"age": 123,
|
|
"name": "example",
|
|
"double": 123.45,
|
|
"bool": true,
|
|
"slice": [1, 2, 3]
|
|
}]]]
|
|
`)
|
|
instance := NewStruct(map[string]string{
|
|
"test_tag_map": `json:"tag_map"`,
|
|
}).
|
|
AddField("[].[].[].age", "", 0, `json:"age"`, false).
|
|
AddField("[].[].[].name", "", "", `json:"name"`, false).
|
|
Build()
|
|
val := instance.New()
|
|
err := json.Unmarshal(data, &val)
|
|
So(err, ShouldBeNil)
|
|
reflectType := reflect.TypeOf(val)
|
|
// 需要是数组类型
|
|
So(reflectType.Kind(), ShouldEqual, reflect.Ptr)
|
|
So(reflectType.Elem().Kind(), ShouldEqual, reflect.Slice)
|
|
// 验证数据
|
|
// 数据序列化成功
|
|
targetByte, targetErr := json.Marshal(val)
|
|
So(targetErr, ShouldBeNil)
|
|
// 数据转map成功
|
|
var res [][][]any
|
|
targetUnmarshalErr := json.Unmarshal(targetByte, &res)
|
|
So(targetUnmarshalErr, ShouldBeNil)
|
|
So(len(res), ShouldEqual, 1)
|
|
userMap, ok := res[0][0][0].(map[string]any)
|
|
So(ok, ShouldBeTrue)
|
|
So(len(userMap), ShouldEqual, 2)
|
|
So(userMap["age"], ShouldEqual, float64(123))
|
|
So(userMap["name"], ShouldEqual, "example")
|
|
So(userMap["double"], ShouldBeNil)
|
|
})
|
|
}
|