dynamicstruct

package module
v0.0.0-...-fc340b9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 29, 2025 License: MIT Imports: 6 Imported by: 1

README

Golang 运行时动态结构体

简介

主要提供在运行时动态生成结构体的能力, 同时支持合并多个已有结构体,生成一个新的结构体

主要功能如下:

  • 运行时动态生成结构体, 更灵活
  • 运行时继承已有结构体的结构
  • 运行时合并多个结构体
  • 向结构体中新增字段
  • 移除结构体中指定字段
  • 修改已存在字段的类型以及Tag标签
  • 读取动态结构体字段的Helper
  • 动态结构体的值, 解析到一个已定义的结构体中
  • 动态结构体生成slice或者map实例
  • 支持嵌套的结构体
  • 支持N维数组
  • 支持嵌套的结构体数组

开发背景

开发个人网关项目时, 面临一个问题: 如何验证网关请求参数的合法性? 在Golang生态中,比较知名的参数验证库是 https://github.com/go-playground/validator。 其支持的参数验证方式如下:

  • 单个值按照指定规则进行验证, 无上下文信息
  • map数据验证, 无上下文信息, 可以看做是单个值的验证的批量版本
  • 结构体数据验证, 支持上下文信息, 可以跨结构体字段进行关联验证

网关参数验证的需求背景如下:

  • 基础的参数验证, 如取值范围、长度等
  • 数据格式校验,如:IP地址、URL、邮箱、手机号等
  • 数据关联校验,如:两个字段的值必须相等、A字段不存在,则B字段必须存在等
  • ......

综合数据验证库提供的验证能力, 以及业务场景的复杂性, 需要使用结构体数据进行参数验证。那么, 此时则面临另外一个问题: 不同的网关接口是有不同的参数结构的, 如何在运行时动态生成结构体呢? 这就需要用到本文所介绍的动态结构体生成能力。

快速开始

# 安装
go get -v -u github.com/liuxiaobopro/golang-dynamic-struct
package main
import (
	"fmt"
	dynamicstruct "git.zhangdeman.cn/zhangdeman/dynamic-struct"
	"github.com/go-playground/validator/v10"
	"fmt"
)
func main() {
	// 动态生成一个结构体
	ds := dynamicstruct.NewStruct()
	ds = ds.AddField("name", "", `json:"name" validate:"required"`)
	ds = ds.AddField("age", 0, `json:"age" validate:"gte=0,lte=130"`)
	ds = ds.AddField("email", "", `json:"email" validate:"required,email"`)
	// 生成结构体实例
	user := ds.Build().New()
	userTestData := `{
		"name": "张三",
		"age": 180,
		"email": "test@qq.com",
	}`
	validatorInstance = validator.New()
	validatorInstance.SetTagName("validate")
	err := validatorInstance.Struct(user)
	fmt.Println(err)
}

实现原理

无论任何语言, 但凡出现 动态 这一类字眼, 实现原理一定脱离不了 反射 , 且 反射是核心实现技术 , 本库中的实现原理也不例外, 也是基于反射进行实现的。核心逻辑是利用 reflect.New 这一泛实例化数据的机制进行实现. 确定原理方向, 抽象的需求运行时动态生成不定结构的结构体 已经转化为具体功能诉求, 如何利用reflect.New这一机制, 动态实例化结构体

具体实现

核心就是一句代码:

structFields := []reflect.StructField
reflect.New(reflect.StructOf(structFields))

其中, structFields 是一个 一个结构体字段配置列表,其可配置的属性具体查看即可, reflect.StructOf 则是根据结构体字段配置反射出结构体类型, 利用 reflect.New 这一机制, 可以动态实例化一个 结构体

核心逻辑就是这么简单, 但是, 动态生成结构体的过程中, 还需要考虑到很多其他的问题, 如:

  • 如何动态生成嵌套的结构体
  • 如何动态生成嵌套的结构体数组
  • 如何动态生成N维数组
  • 如何动态生成结构体数组

其实上述逻辑无非是基于最原始的基础逻辑, 递归进行实现 , 所以, 具体实现细节, 可以查看源码。

Documentation

Overview

Package dynamicstruct ...

Description : dynamicstruct ...

Author : go_developer@163.com<白茶清欢>

Date : 2025-03-23 13:34

Package dynamicstruct ...

Description : dynamicstruct ...

Author : go_developer@163.com<白茶清欢>

Date : 2025-03-23 14:14

Package dynamicstruct ...

Description : dynamicstruct ...

Author : go_developer@163.com<白茶清欢>

Date : 2025-03-23 14:07

Index

Constants

View Source
const (
	// ArraySplit is the string used to split array elements
	ArraySplit = "[]"
	// ArrayRootFlag 传入路径直接以数组开头
	ArrayRootFlag = "__RootArrayFlag"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Field

type 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
}

Field 对结构体字段的操作

type IBuilder

type IBuilder interface {
	// AddField 添加结构体字段
	AddField(name string, pkg string, typ any, tag string, anonymous bool) IBuilder
	// RemoveField 移除指定名称的结构体字段
	RemoveField(name string) IBuilder
	// HasField 检测指定名称的结构体字段是否存在
	HasField(name string) bool
	// GetField 根据名称获取结构体字段定义
	GetField(name string) IFieldConfig
	// Build 返回动态定义的结构体.
	Build() IDynamicStruct
}

IBuilder 运行时动态生成结构体的接口约束

func ExtendStruct

func ExtendStruct(value ...any) IBuilder

ExtendStruct 基于已有结构体, 生成动态结构体(相当于继承指定的结构体属性)

func MergeStructs

func MergeStructs(values ...any) IBuilder

MergeStructs 多个结构体合并成一个动态结构体

func NewStruct

func NewStruct(structTagTable map[string]string) IBuilder

NewStruct 获取builder实例 传入的tag映射表: 字段路径 => json tag,最高优先级, 没传的时候会使用AddField的tag, 也为空使用手搓json tag

type IDynamicStruct

type IDynamicStruct interface {
	// New 获取结构体实例, 所有字段值均为对应类型的初始零值
	New() any

	// NewSliceOfStructs slice实例化
	NewSliceOfStructs() any

	// NewMapOfStructs map 或者 struct实例化
	NewMapOfStructs(key any) any
}

IDynamicStruct 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.

type IFieldConfig

type IFieldConfig interface {
	// SetType 设置字段类型.
	SetType(typ any) IFieldConfig
	// SetTag 设置字段 tag.
	SetTag(tag string) IFieldConfig
	// GetType 获取类型
	GetType() any
	// GetTag 获取tag
	GetTag() string
}

IFieldConfig 结构体字段的定义.

type Reader

type 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
}

Reader 通过反射读取结构体信息

func NewReader

func NewReader(value any) Reader

NewReader reads struct instance and provides instance of Reader interface to give possibility to read all fields' values.

Directories

Path Synopsis
Package wrapper ...
Package wrapper ...

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL