dynamicstruct

package module
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Feb 15, 2021 License: MIT Imports: 4 Imported by: 56

README

Golang dynamic struct

Package dynamic struct provides possibility to dynamically, in runtime, extend or merge existing defined structs or to provide completely new struct.

Main features:

  • Building completely new struct in runtime
  • Extending existing struct in runtime
  • Merging multiple structs in runtime
  • Adding new fields into struct
  • Removing existing fields from struct
  • Modifying fields' types and tags
  • Easy reading of dynamic structs
  • Mapping dynamic struct with set values to existing struct
  • Make slices and maps of dynamic structs

Works out-of-the-box with:

Benchmarks

Environment:

  • MacBook Pro (13-inch, Early 2015), 2,7 GHz Intel Core i5
  • go version go1.11 darwin/amd64
goos: darwin
goarch: amd64
pkg: github.com/ompluscator/dynamic-struct
BenchmarkClassicWay_NewInstance-4                 2000000000     0.34 ns/op
BenchmarkNewStruct_NewInstance-4                    10000000      141 ns/op
BenchmarkNewStruct_NewInstance_Parallel-4           20000000     89.6 ns/op
BenchmarkExtendStruct_NewInstance-4                 10000000      135 ns/op
BenchmarkExtendStruct_NewInstance_Parallel-4        20000000     89.5 ns/op
BenchmarkMergeStructs_NewInstance-4                 10000000      140 ns/op
BenchmarkMergeStructs_NewInstance_Parallel-4        20000000     94.3 ns/op

Add new struct

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/ompluscator/dynamic-struct"
)

func main() {
	instance := dynamicstruct.NewStruct().
		AddField("Integer", 0, `json:"int"`).
		AddField("Text", "", `json:"someText"`).
		AddField("Float", 0.0, `json:"double"`).
		AddField("Boolean", false, "").
		AddField("Slice", []int{}, "").
		AddField("Anonymous", "", `json:"-"`).
		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]}
}

Extend existing struct

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/ompluscator/dynamic-struct"
)

type Data struct {
	Integer int `json:"int"`
}

func main() {
	instance := dynamicstruct.ExtendStruct(Data{}).
		AddField("Text", "", `json:"someText"`).
		AddField("Float", 0.0, `json:"double"`).
		AddField("Boolean", false, "").
		AddField("Slice", []int{}, "").
		AddField("Anonymous", "", `json:"-"`).
		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]}
}

Merge existing structs

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/ompluscator/dynamic-struct"
)

type DataOne struct {
	Integer int     `json:"int"`
	Text    string  `json:"someText"`
	Float   float64 `json:"double"`
}

type DataTwo struct {
	Boolean bool
	Slice []int
	Anonymous string `json:"-"`
}

func main() {
	instance := dynamicstruct.MergeStructs(DataOne{}, DataTwo{}).
		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]}
}

Read dynamic struct

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/ompluscator/dynamic-struct"
)

type DataOne struct {
	Integer int     `json:"int"`
	Text    string  `json:"someText"`
	Float   float64 `json:"double"`
}

type DataTwo struct {
	Boolean bool
	Slice []int
	Anonymous string `json:"-"`
}

func main() {
	instance := dynamicstruct.MergeStructs(DataOne{}, DataTwo{}).
		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)
	}

	reader := dynamicstruct.NewReader(instance)

	fmt.Println("Integer", reader.GetField("Integer").Int())
	fmt.Println("Text", reader.GetField("Text").String())
	fmt.Println("Float", reader.GetField("Float").Float64())
	fmt.Println("Boolean", reader.GetField("Boolean").Bool())
	fmt.Println("Slice", reader.GetField("Slice").Interface().([]int))
	fmt.Println("Anonymous", reader.GetField("Anonymous").String())

	var dataOne DataOne
	err = reader.ToStruct(&dataOne)
	fmt.Println(err, dataOne)

	var dataTwo DataTwo
	err = reader.ToStruct(&dataTwo)
	fmt.Println(err, dataTwo)
	// Out:
	// Integer 123
	// Text example
	// Float 123.45
	// Boolean true
	// Slice [1 2 3]
	// Anonymous
	// <nil> {123 example 123.45}
	// <nil> {true [1 2 3] }
}

Make a slice of dynamic struct

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/ompluscator/dynamic-struct"
)

type Data struct {
	Integer   int     `json:"int"`
	Text      string  `json:"someText"`
	Float     float64 `json:"double"`
	Boolean   bool
	Slice     []int
	Anonymous string `json:"-"`
}

func main() {
	definition := dynamicstruct.ExtendStruct(Data{}).Build()

	slice := definition.NewSliceOfStructs()

	data := []byte(`
[
	{
		"int": 123,
		"someText": "example",
		"double": 123.45,
		"Boolean": true,
		"Slice": [1, 2, 3],
		"Anonymous": "avoid to read"
	}
]
`)

	err := json.Unmarshal(data, &slice)
	if err != nil {
		log.Fatal(err)
	}

	data, err = json.Marshal(slice)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(data))
	// Out:
	// [{"Boolean":true,"Slice":[1,2,3],"int":123,"someText":"example","double":123.45}]

	reader := dynamicstruct.NewReader(slice)
	readersSlice := reader.ToSliceOfReaders()
	for k, v := range readersSlice {
		var value Data
		err := v.ToStruct(&value)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Println(k, value)
	}
	// Out:
	// 0 {123 example 123.45 true [1 2 3] }
}

Make a map of dynamic struct

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/ompluscator/dynamic-struct"
)

type Data struct {
	Integer   int     `json:"int"`
	Text      string  `json:"someText"`
	Float     float64 `json:"double"`
	Boolean   bool
	Slice     []int
	Anonymous string `json:"-"`
}

func main() {
	definition := dynamicstruct.ExtendStruct(Data{}).Build()

	mapWithStringKey := definition.NewMapOfStructs("")

	data := []byte(`
{
	"element": {
		"int": 123,
		"someText": "example",
		"double": 123.45,
		"Boolean": true,
		"Slice": [1, 2, 3],
		"Anonymous": "avoid to read"
	}
}
`)

	err := json.Unmarshal(data, &mapWithStringKey)
	if err != nil {
		log.Fatal(err)
	}

	data, err = json.Marshal(mapWithStringKey)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(data))
	// Out:
	// {"element":{"int":123,"someText":"example","double":123.45,"Boolean":true,"Slice":[1,2,3]}}

	reader := dynamicstruct.NewReader(mapWithStringKey)
	readersMap := reader.ToMapReaderOfReaders()
	for k, v := range readersMap {
		var value Data
		err := v.ToStruct(&value)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Println(k, value)
	}
	// Out:
	// element {123 example 123.45 true [1 2 3] }
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Builder

type Builder interface {
	// AddField creates new struct's field.
	// It expects field's name, type and string.
	// Type is provided as an instance of some golang type.
	// Tag is provided as classical golang field tag.
	//
	// builder.AddField("SomeFloatField", 0.0, `json:"boolean" validate:"gte=10"`)
	//
	AddField(name string, typ interface{}, tag string) Builder
	// RemoveField removes existing struct's field.
	//
	// builder.RemoveField("SomeFloatField")
	//
	RemoveField(name string) Builder
	// HasField checks if struct has a field with a given name.
	//
	// if builder.HasField("SomeFloatField") { ...
	//
	HasField(name string) bool
	// GetField returns struct's field definition.
	// If there is no such field, it returns nil.
	// Usable to edit existing struct's field.
	//
	// field := builder.GetField("SomeFloatField")
	//
	GetField(name string) FieldConfig
	// Build returns definition for dynamic struct.
	// Definition can be used to create new instances.
	//
	// dStruct := builder.Build()
	//
	Build() DynamicStruct
}

Builder holds all fields' definitions for desired structs. It gives opportunity to add or remove fields, or to edit existing ones. In the end, it provides definition for dynamic structs, used to create new instances.

func ExtendStruct

func ExtendStruct(value interface{}) Builder

ExtendStruct extends existing instance of struct and returns new instance of Builder interface.

builder := dynamicstruct.MergeStructs(MyStruct{})

func MergeStructs

func MergeStructs(values ...interface{}) Builder

MergeStructs merges a list of existing instances of structs and returns new instance of Builder interface.

builder := dynamicstruct.MergeStructs(MyStructOne{}, MyStructTwo{}, MyStructThree{})

func NewStruct

func NewStruct() Builder

NewStruct returns new clean instance of Builder interface for defining fresh dynamic struct.

builder := dynamicstruct.NewStruct()

type DynamicStruct

type DynamicStruct interface {
	// New provides new instance of defined dynamic struct.
	//
	// value := dStruct.New()
	//
	New() interface{}

	// NewSliceOfStructs provides new slice of defined dynamic struct, with 0 length and capacity.
	//
	// value := dStruct.NewSliceOfStructs()
	//
	NewSliceOfStructs() interface{}

	// New provides new map of defined dynamic struct with desired key type.
	//
	// value := dStruct.NewMapOfStructs("")
	//
	NewMapOfStructs(key interface{}) interface{}
}

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.

type Field

type Field interface {
	// GetName returns field's name in struct.
	//
	// name := field.GetName()
	//
	Name() string
	// PointerInt returns a pointer for instance of int type.
	// It panics if field's value can't be casted to desired type.
	//
	// number := reader.GetField("SomeField").PointerInt()
	//
	PointerInt() *int
	// Int returns an instance of int type.
	// It panics if field's value can't be casted to desired type.
	//
	// number := reader.GetField("SomeField").Int()
	//
	Int() int
	// PointerInt8 returns a pointer for instance of int8 type.
	// It panics if field's value can't be casted to desired type.
	//
	// number := reader.GetField("SomeField").PointerInt8()
	//
	PointerInt8() *int8
	// Int8 returns aan instance of int8 type.
	// It panics if field's value can't be casted to desired type.
	//
	// number := reader.GetField("SomeField").Int8()
	//
	Int8() int8
	// PointerInt16 returns a pointer for instance of int16 type.
	// It panics if field's value can't be casted to desired type.
	//
	// number := reader.GetField("SomeField").PointerInt16()
	//
	PointerInt16() *int16
	// Int16 returns an instance of int16 type.
	// It panics if field's value can't be casted to desired type.
	//
	// number := reader.GetField("SomeField").Int16()
	//
	Int16() int16
	// PointerInt32 returns a pointer for instance of int32 type.
	// It panics if field's value can't be casted to desired type.
	//
	// number := reader.GetField("SomeField").PointerInt32()
	//
	PointerInt32() *int32
	// Int32 returns an instance of int32 type.
	// It panics if field's value can't be casted to desired type.
	//
	// number := reader.GetField("SomeField").Int32()
	//
	Int32() int32
	// PointerInt64 returns a pointer for instance of int64 type.
	// It panics if field's value can't be casted to desired type.
	//
	// number := reader.GetField("SomeField").PointerInt64()
	//
	PointerInt64() *int64
	// Int64 returns an instance of int64 type.
	// It panics if field's value can't be casted to desired type.
	//
	// number := reader.GetField("SomeField").Int64()
	//
	Int64() int64
	// PointerUint returns a pointer for instance of uint type.
	// It panics if field's value can't be casted to desired type.
	//
	// unsigned := reader.GetField("SomeField").PointerUint()
	//
	PointerUint() *uint
	// Uint returns an instance of uint type.
	// It panics if field's value can't be casted to desired type.
	//
	// unsigned := reader.GetField("SomeField").Uint()
	//
	Uint() uint
	// PointerUint8 returns a pointer for instance of uint8 type.
	// It panics if field's value can't be casted to desired type.
	//
	// unsigned := reader.GetField("SomeField").PointerUint8()
	//
	PointerUint8() *uint8
	// Uint8 returns an instance of uint8 type.
	// It panics if field's value can't be casted to desired type.
	//
	// unsigned := reader.GetField("SomeField").Uint8()
	//
	Uint8() uint8
	// PointerUint16 returns a pointer for instance of uint16 type.
	// It panics if field's value can't be casted to desired type.
	//
	// unsigned := reader.GetField("SomeField").PointerUint16()
	//
	PointerUint16() *uint16
	// Uint16 returns an instance of uint16 type.
	// It panics if field's value can't be casted to desired type.
	//
	// unsigned := reader.GetField("SomeField").Uint16()
	//
	Uint16() uint16
	// PointerUint32 returns a pointer for instance of uint32 type.
	// It panics if field's value can't be casted to desired type.
	//
	// unsigned := reader.GetField("SomeField").PointerUint32()
	//
	PointerUint32() *uint32
	// Uint32 returns an instance of uint32 type.
	// It panics if field's value can't be casted to desired type.
	//
	// unsigned := reader.GetField("SomeField").Uint32()
	//
	Uint32() uint32
	// PointerUint64 returns a pointer for instance of uint64 type.
	// It panics if field's value can't be casted to desired type.
	//
	// unsigned := reader.GetField("SomeField").PointerUint64()
	//
	PointerUint64() *uint64
	// Uint64 returns an instance of uint64 type.
	// It panics if field's value can't be casted to desired type.
	//
	// unsigned := reader.GetField("SomeField").Uint64()
	//
	Uint64() uint64
	// PointerFloat32 returns a pointer for instance of float32 type.
	// It panics if field's value can't be casted to desired type.
	//
	// boolean := reader.GetField("SomeField").PointerFloat32()
	//
	PointerFloat32() *float32
	// Float32 returns an of float32 type.
	// It panics if field's value can't be casted to desired type.
	//
	// boolean := reader.GetField("SomeField").Float32()
	//
	Float32() float32
	// PointerFloat64 returns a pointer for instance of float64 type.
	// It panics if field's value can't be casted to desired type.
	//
	// boolean := reader.GetField("SomeField").PointerFloat64()
	//
	PointerFloat64() *float64
	// Float64 returns an instance of float64 type.
	// It panics if field's value can't be casted to desired type.
	//
	// boolean := reader.GetField("SomeField").Float64()
	//
	Float64() float64
	// PointerString returns a pointer for instance of string type.
	// It panics if field's value can't be casted to desired type.
	//
	// text := reader.GetField("SomeField").PointerString()
	//
	PointerString() *string
	// String returns aan instance of string type.
	// It panics if field's value can't be casted to desired type.
	//
	// text := reader.GetField("SomeField").String()
	//
	String() string
	// PointerBool returns a pointer for instance of bool type.
	// It panics if field's value can't be casted to desired type.
	//
	// boolean := reader.GetField("SomeField").PointerBool()
	//
	PointerBool() *bool
	// Bool returns an instance of bool type.
	// It panics if field's value can't be casted to desired type.
	//
	// boolean := reader.GetField("SomeField").Bool()
	//
	Bool() bool
	// PointerTime returns a pointer for instance of time.Time{} type.
	// It panics if field's value can't be casted to desired type.
	//
	// dateTime := reader.GetField("SomeField").PointerTime()
	//
	PointerTime() *time.Time
	// Time returns an instance of time.Time{} type.
	// It panics if field's value can't be casted to desired type.
	//
	// dateTime := reader.GetField("SomeField").Time()
	//
	Time() time.Time
	// Interface returns an interface which represents field's value.
	// Useful for casting value into desired type.
	//
	// slice, ok := reader.GetField("SomeField").Interface().([]int)
	//
	Interface() interface{}
}

Field is a wrapper for struct's field's value. It provides methods for easier field's value reading.

type FieldConfig

type FieldConfig interface {
	// SetType changes field's type.
	// Expected value is an instance of golang type.
	//
	// field.SetType([]int{})
	//
	SetType(typ interface{}) FieldConfig
	// SetTag changes fields's tag.
	// Expected value is an string which represents classical
	// golang tag.
	//
	// field.SetTag(`json:"slice"`)
	//
	SetTag(tag string) FieldConfig
}

FieldConfig holds single field's definition. It provides possibility to edit field's type and tag.

type Reader

type Reader interface {
	// HasField checks if struct instance has a field with a given name.
	//
	// if reader.HasField("SomeFloatField") { ...
	//
	HasField(name string) bool
	// GetField returns struct instance's field value.
	// If there is no such field, it returns nil.
	// Usable to edit existing struct's field.
	//
	// field := reader.GetField("SomeFloatField")
	//
	GetField(name string) Field
	// GetAllFields returns a list of all struct instance's fields.
	//
	// for _, field := range reader.GetAllFields() { ...
	//
	GetAllFields() []Field
	// ToStruct maps all read values to passed instance of struct, by setting
	// all its values for fields with same names.
	// It returns an error if argument is not a pointer to a struct.
	//
	// err := reader.ToStruct(&instance)
	//
	ToStruct(value interface{}) error
	// ToSliceOfReaders returns a list of Reader interfaces if value is representation
	// of slice itself.
	//
	// readers := reader.ToReaderSlice()
	//
	ToSliceOfReaders() []Reader
	// ToMapReaderOfReaders returns a map of Reader interfaces if value is representation
	// of map itself with some key type.
	//
	// readers := reader.ToReaderMap()
	//
	ToMapReaderOfReaders() map[interface{}]Reader
	// GetValue returns original value used in reader.
	//
	// instance := reader.GetValue()
	//
	GetValue() interface{}
}

Reader is helper interface which provides possibility to access struct's fields' values, reads and provides theirs values.

func NewReader

func NewReader(value interface{}) Reader

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

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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