Golang运行时动态生成结构体

This commit is contained in:
白茶清欢 2025-03-19 15:27:14 +08:00
commit eab2a7abde
7 changed files with 771 additions and 0 deletions

25
.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
# Created by .ignore support plugin (hsz.mobi)
### Go template
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
.idea
.vscode
.fleet
release
logs
.scannerwork
.env
__debug*

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2019 Marko Milojevic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

63
README.md Normal file
View File

@ -0,0 +1,63 @@
# Golang 运行时动态结构体
主要提供在运行时动态生成结构体的能力, 同时支持合并多个已有结构体,生成一个新的结构体
主要功能如下:
* 运行时动态生成结构体, 更灵活
* 运行时继承已有结构体的结构
* 运行时合并多个结构体
* 向结构体中新增字段
* 移除结构体中指定字段
* 修改已存在字段的类型以及Tag标签
* 读取动态结构体字段的Helper
* 动态结构体的值, 解析到一个已定义的结构体中
* 懂动态结构体生成slice或者map实例
## 使用示例
```go
package main
import (
"encoding/json"
"fmt"
"log"
"git.zhangdeman.cn/zhangdeman/dynamic-struct"
)
func main() {
instance := NewStruct().
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()
data := []byte(`
{
"int": 123,
"someText": "example",
"double": 123.45,
"Boolean": true,
"Slice": [1, 2, 3],
"Anonymous": "avoid to read"
}
`)
err := json.Unmarshal(data, &instance)
if err != nil {
log.Fatal(err)
}
data, err = json.Marshal(instance)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(data))
// Out:
// {"int":123,"someText":"example","double":123.45,"Boolean":true,"Slice":[1,2,3]}
}
```

185
builder.go Normal file
View File

@ -0,0 +1,185 @@
package dynamicstruct
import "reflect"
type (
// Builder 运行时动态生成结构体的接口约束
Builder interface {
// AddField 添加结构体字段
AddField(name string, pkg string, typ any, tag string, anonymous bool) Builder
// RemoveField 移除指定名称的结构体字段
RemoveField(name string) Builder
// HasField 检测指定名称的结构体字段是否存在
HasField(name string) bool
// GetField 根据名称获取结构体字段定义
GetField(name string) FieldConfig
// Build 返回动态定义的结构体.
Build() DynamicStruct
}
// FieldConfig 结构体字段的定义.
FieldConfig interface {
// SetType 设置字段类型.
SetType(typ any) FieldConfig
// SetTag 设置字段 tag.
SetTag(tag string) FieldConfig
}
// DynamicStruct contains defined dynamic struct.
// This definition can't be changed anymore, once is built.
// It provides a method for creating new instances of same defintion.
DynamicStruct interface {
// New 获取结构体实例, 所有字段值均为对应类型的初始零值
New() any
// NewSliceOfStructs slice实例化
NewSliceOfStructs() any
// NewMapOfStructs map 或者 struct实例化
NewMapOfStructs(key any) any
}
builderImpl struct {
fields []*fieldConfigImpl
}
fieldConfigImpl struct {
name string
pkg string
typ any
tag string
anonymous bool
}
dynamicStructImpl struct {
definition reflect.Type
}
)
// NewStruct 获取builder实例
func NewStruct() Builder {
return &builderImpl{
fields: []*fieldConfigImpl{},
}
}
// ExtendStruct 基于已有结构体, 生成动态结构体(相当于继承指定的结构体属性)
func ExtendStruct(value ...any) Builder {
return MergeStructs(value...)
}
// MergeStructs 多个结构体合并成一个动态结构体
func MergeStructs(values ...any) Builder {
builder := NewStruct()
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
}
// AddField 添加结构体字段
func (b *builderImpl) AddField(name string, pkg string, typ any, tag string, anonymous bool) Builder {
if existFieldCfg := b.GetField(name); nil != existFieldCfg {
// 说明已存在指定名称字段
// 重复添加, 则会议后面的标签以及类型, 覆盖前面的值
existFieldCfg.SetTag(tag)
existFieldCfg.SetType(typ)
return b
}
b.fields = append(b.fields, &fieldConfigImpl{
name: name,
typ: typ,
tag: tag,
anonymous: anonymous,
pkg: pkg,
})
return b
}
// RemoveField 根据名称移除结构体字段
func (b *builderImpl) RemoveField(name string) Builder {
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) FieldConfig {
for i := range b.fields {
if b.fields[i].name == name {
return b.fields[i]
}
}
return nil
}
// Build 构建动态结构体
func (b *builderImpl) Build() DynamicStruct {
var structFields []reflect.StructField
for _, field := range b.fields {
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{
definition: reflect.StructOf(structFields),
}
}
// SetType 设置字段类型
func (f *fieldConfigImpl) SetType(typ any) FieldConfig {
f.typ = typ
return f
}
// SetTag 设置字段标签
func (f *fieldConfigImpl) SetTag(tag string) FieldConfig {
f.tag = tag
return f
}
// New 创建动态结构体实例
func (ds *dynamicStructImpl) New() any {
return reflect.New(ds.definition).Interface()
}
// NewSliceOfStructs 创建动态结构体切片实例
func (ds *dynamicStructImpl) NewSliceOfStructs() any {
return reflect.New(reflect.SliceOf(ds.definition)).Interface()
}
// NewMapOfStructs 创建动态结构体map实例
func (ds *dynamicStructImpl) NewMapOfStructs(key any) any {
return reflect.New(reflect.MapOf(reflect.Indirect(reflect.ValueOf(key)).Type(), ds.definition)).Interface()
}

41
builder_test.go Normal file
View File

@ -0,0 +1,41 @@
// Package dynamicstruct ...
//
// Description : dynamicstruct ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-03-19 14:48
package dynamicstruct
import (
"encoding/json"
"fmt"
"reflect"
"testing"
)
func Test_dynamicStructImpl_New(t *testing.T) {
instance := NewStruct().
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()
data := []byte(`
{
"int": 123,
"someText": "example",
"double": 123.45,
"Boolean": true,
"Slice": [1, 2, 3],
"Anonymous": "avoid to read"
}
`)
err := json.Unmarshal(data, &instance)
fmt.Println(err)
fmt.Println(reflect.ValueOf(instance).Elem().FieldByName("Integer").Interface())
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module git.zhangdeman.cn/zhangdeman/dynamic-struct
go 1.24.1

433
reader.go Normal file
View File

@ -0,0 +1,433 @@
package dynamicstruct
import (
"errors"
"fmt"
"reflect"
"time"
)
type (
// Reader 通过反射读取结构体信息
Reader interface {
// HasField 是否存在指定名称的字段
HasField(name string) bool
// GetField 获取指定字段信息
GetField(name string) Field
// GetAllFields 获取全部字段
GetAllFields() []Field
// ToStruct 转结构体
ToStruct(value any) error
// ToSliceOfReaders 转slice
ToSliceOfReaders() []Reader
// ToMapReaderOfReaders returns a map of Reader interfaces if value is representation
ToMapReaderOfReaders() map[any]Reader
// GetValue 获取输入的原始值
GetValue() any
}
// Field 对结构体字段的操作
Field interface {
// Name 返回字段名称
Name() string
// PointerInt int 指针
PointerInt() *int
// Int int
Int() int
// PointerInt8 int8指针
PointerInt8() *int8
// Int8 int
Int8() int8
// PointerInt16 int16指针
PointerInt16() *int16
// Int16 int16
Int16() int16
// PointerInt32 int32指针
PointerInt32() *int32
// Int32 int32
Int32() int32
// PointerInt64 int64指针
PointerInt64() *int64
// Int64 int64
Int64() int64
// PointerUint uint指针
PointerUint() *uint
// Uint uint
Uint() uint
// PointerUint8 uint8指针
PointerUint8() *uint8
// Uint8 uint8
Uint8() uint8
// PointerUint16 uint16指针
PointerUint16() *uint16
// Uint16 uint16
Uint16() uint16
// PointerUint32 uint32指针
PointerUint32() *uint32
// Uint32 uint32
Uint32() uint32
// PointerUint64 uint64指针
PointerUint64() *uint64
// Uint64 uint64
Uint64() uint64
// PointerFloat32 float32指针
PointerFloat32() *float32
// Float32 float32
Float32() float32
// PointerFloat64 float64指针
PointerFloat64() *float64
// Float64 float64
Float64() float64
// PointerString string指针
PointerString() *string
// String string
String() string
// PointerBool bool指针
PointerBool() *bool
// Bool bool...
Bool() bool
// PointerTime time指针
PointerTime() *time.Time
// Time time...
Time() time.Time
// Any any...
Any() any
}
readImpl struct {
fields map[string]fieldImpl
value any
}
fieldImpl struct {
field reflect.StructField
value reflect.Value
}
)
// NewReader reads struct instance and provides instance of
// Reader interface to give possibility to read all fields' values.
func NewReader(value any) Reader {
fields := map[string]fieldImpl{}
valueOf := reflect.Indirect(reflect.ValueOf(value))
typeOf := valueOf.Type()
if typeOf.Kind() == reflect.Struct {
for i := 0; i < valueOf.NumField(); i++ {
field := typeOf.Field(i)
fields[field.Name] = fieldImpl{
field: field,
value: valueOf.Field(i),
}
}
}
return readImpl{
fields: fields,
value: value,
}
}
func (r readImpl) HasField(name string) bool {
_, ok := r.fields[name]
return ok
}
func (r readImpl) GetField(name string) Field {
if !r.HasField(name) {
return nil
}
return r.fields[name]
}
func (r readImpl) GetAllFields() []Field {
var fields []Field
for _, field := range r.fields {
fields = append(fields, field)
}
return fields
}
func (r readImpl) ToStruct(value any) error {
valueOf := reflect.ValueOf(value)
if valueOf.Kind() != reflect.Ptr || valueOf.IsNil() {
return errors.New("ToStruct: expected a pointer as an argument")
}
valueOf = valueOf.Elem()
typeOf := valueOf.Type()
if valueOf.Kind() != reflect.Struct {
return errors.New("ToStruct: expected a pointer to struct as an argument")
}
for i := 0; i < valueOf.NumField(); i++ {
fieldType := typeOf.Field(i)
fieldValue := valueOf.Field(i)
original, ok := r.fields[fieldType.Name]
if !ok {
continue
}
if fieldValue.CanSet() && r.haveSameTypes(original.value.Type(), fieldValue.Type()) {
fieldValue.Set(original.value)
}
}
return nil
}
func (r readImpl) ToSliceOfReaders() []Reader {
valueOf := reflect.Indirect(reflect.ValueOf(r.value))
typeOf := valueOf.Type()
if typeOf.Kind() != reflect.Slice && typeOf.Kind() != reflect.Array {
return nil
}
var readers []Reader
for i := 0; i < valueOf.Len(); i++ {
readers = append(readers, NewReader(valueOf.Index(i).Interface()))
}
return readers
}
func (r readImpl) ToMapReaderOfReaders() map[any]Reader {
valueOf := reflect.Indirect(reflect.ValueOf(r.value))
typeOf := valueOf.Type()
if typeOf.Kind() != reflect.Map {
return nil
}
readers := map[any]Reader{}
for _, keyValue := range valueOf.MapKeys() {
readers[keyValue.Interface()] = NewReader(valueOf.MapIndex(keyValue).Interface())
}
return readers
}
func (r readImpl) GetValue() any {
return r.value
}
func (r readImpl) haveSameTypes(first reflect.Type, second reflect.Type) bool {
if first.Kind() != second.Kind() {
return false
}
switch first.Kind() {
case reflect.Ptr:
return r.haveSameTypes(first.Elem(), second.Elem())
case reflect.Struct:
return first.PkgPath() == second.PkgPath() && first.Name() == second.Name()
case reflect.Slice:
return r.haveSameTypes(first.Elem(), second.Elem())
case reflect.Map:
return r.haveSameTypes(first.Elem(), second.Elem()) && r.haveSameTypes(first.Key(), second.Key())
default:
return first.Kind() == second.Kind()
}
}
func (f fieldImpl) Name() string {
return f.field.Name
}
func (f fieldImpl) PointerInt() *int {
if f.value.IsNil() {
return nil
}
value := f.Int()
return &value
}
func (f fieldImpl) Int() int {
return int(reflect.Indirect(f.value).Int())
}
func (f fieldImpl) PointerInt8() *int8 {
if f.value.IsNil() {
return nil
}
value := f.Int8()
return &value
}
func (f fieldImpl) Int8() int8 {
return int8(reflect.Indirect(f.value).Int())
}
func (f fieldImpl) PointerInt16() *int16 {
if f.value.IsNil() {
return nil
}
value := f.Int16()
return &value
}
func (f fieldImpl) Int16() int16 {
return int16(reflect.Indirect(f.value).Int())
}
func (f fieldImpl) PointerInt32() *int32 {
if f.value.IsNil() {
return nil
}
value := f.Int32()
return &value
}
func (f fieldImpl) Int32() int32 {
return int32(reflect.Indirect(f.value).Int())
}
func (f fieldImpl) PointerInt64() *int64 {
if f.value.IsNil() {
return nil
}
value := f.Int64()
return &value
}
func (f fieldImpl) Int64() int64 {
return reflect.Indirect(f.value).Int()
}
func (f fieldImpl) PointerUint() *uint {
if f.value.IsNil() {
return nil
}
value := f.Uint()
return &value
}
func (f fieldImpl) Uint() uint {
return uint(reflect.Indirect(f.value).Uint())
}
func (f fieldImpl) PointerUint8() *uint8 {
if f.value.IsNil() {
return nil
}
value := f.Uint8()
return &value
}
func (f fieldImpl) Uint8() uint8 {
return uint8(reflect.Indirect(f.value).Uint())
}
func (f fieldImpl) PointerUint16() *uint16 {
if f.value.IsNil() {
return nil
}
value := f.Uint16()
return &value
}
func (f fieldImpl) Uint16() uint16 {
return uint16(reflect.Indirect(f.value).Uint())
}
func (f fieldImpl) PointerUint32() *uint32 {
if f.value.IsNil() {
return nil
}
value := f.Uint32()
return &value
}
func (f fieldImpl) Uint32() uint32 {
return uint32(reflect.Indirect(f.value).Uint())
}
func (f fieldImpl) PointerUint64() *uint64 {
if f.value.IsNil() {
return nil
}
value := f.Uint64()
return &value
}
func (f fieldImpl) Uint64() uint64 {
return reflect.Indirect(f.value).Uint()
}
func (f fieldImpl) PointerFloat32() *float32 {
if f.value.IsNil() {
return nil
}
value := f.Float32()
return &value
}
func (f fieldImpl) Float32() float32 {
return float32(reflect.Indirect(f.value).Float())
}
func (f fieldImpl) PointerFloat64() *float64 {
if f.value.IsNil() {
return nil
}
value := f.Float64()
return &value
}
func (f fieldImpl) Float64() float64 {
return reflect.Indirect(f.value).Float()
}
func (f fieldImpl) PointerString() *string {
if f.value.IsNil() {
return nil
}
value := f.String()
return &value
}
func (f fieldImpl) String() string {
return reflect.Indirect(f.value).String()
}
func (f fieldImpl) PointerBool() *bool {
if f.value.IsNil() {
return nil
}
value := f.Bool()
return &value
}
func (f fieldImpl) Bool() bool {
return reflect.Indirect(f.value).Bool()
}
func (f fieldImpl) PointerTime() *time.Time {
if f.value.IsNil() {
return nil
}
value := f.Time()
return &value
}
func (f fieldImpl) Time() time.Time {
value, ok := reflect.Indirect(f.value).Interface().(time.Time)
if !ok {
panic(fmt.Sprintf(`field "%s" is not instance of time.Time`, f.field.Name))
}
return value
}
func (f fieldImpl) Any() any {
return f.value.Interface()
}