Tags: #go
// READ THIS FOR MANY MORE EXAMPLES!
// https://medium.com/capital-one-tech/learning-to-use-go-reflection-822a0aed74b7
package main
import (
"fmt"
"reflect"
)
type Foo struct {
A int `tag1:"First Tag" tag2:"Second Tag"`
B string
}
func main() {
greeting := "hello"
f := Foo{A: 10, B: "Salutations"}
gVal := reflect.ValueOf(greeting)
// not a pointer so all we can do is read it
fmt.Println("ORIGINAL GREETING:", gVal.Interface())
gpVal := reflect.ValueOf(&greeting)
// it’s a pointer, so we can change it, and it changes the underlying variable
gpVal.Elem().SetString("goodbye")
fmt.Println("NEW GREETING:", greeting)
fType := reflect.TypeOf(f)
fVal := reflect.New(fType)
fVal.Elem().Field(0).SetInt(20)
fVal.Elem().Field(1).SetString("Greetings")
f2 := fVal.Elem().Interface().(Foo)
fmt.Printf("ORIGINAL struct: %+v, %d, %s\n", f, f.A, f.B)
fmt.Printf("NEW struct: %+v, %d, %s\n", f2, f2.A, f2.B)
}
// https://play.golang.org/p/iIf7OF_lPBG
//
// all this good work is done by https://github.com/Ompluscator/dynamic-struct
// which offers much more functionality than I had stripped out from that library
//
// I was working on something where I wanted only standard library implementation code
// so I decided to just strip out the code I needed
//
package main
import (
"encoding/json"
"fmt"
"reflect"
)
// 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 DynamicStruct interface {
// New provides new instance of defined dynamic struct.
//
// value := dStruct.New()
//
New() interface{}
}
// Builder holds all fields' definitions for desired structs.
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
// Build returns definition for dynamic struct.
// Definition can be used to create new instances.
//
// dStruct := builder.Build()
//
Build() DynamicStruct
}
type dynamicStructImpl struct {
definition reflect.Type
}
type fieldConfigImpl struct {
typ interface{}
tag string
}
type builderImpl struct {
fields map[string]*fieldConfigImpl
}
func (b *builderImpl) AddField(name string, typ interface{}, tag string) Builder {
b.fields[name] = &fieldConfigImpl{
typ: typ,
tag: tag,
}
return b
}
func (b *builderImpl) Build() DynamicStruct {
var structFields []reflect.StructField
for name, field := range b.fields {
structFields = append(structFields, reflect.StructField{
Name: name,
Type: reflect.TypeOf(field.typ),
Tag: reflect.StructTag(field.tag),
})
}
return &dynamicStructImpl{
definition: reflect.StructOf(structFields),
}
}
func (ds *dynamicStructImpl) New() interface{} {
return reflect.New(ds.definition).Interface()
}
// NewStruct returns new clean instance of Builder interface
// for defining fresh dynamic struct.
//
// builder := NewStruct()
//
func NewStruct() Builder {
return &builderImpl{
fields: map[string]*fieldConfigImpl{},
}
}
func main() {
instance := 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()
fmt.Printf("empty struct instance:\n%+v\n\n", instance)
data := []byte(`
{
"int": 123,
"someText": "example",
"double": 123.45,
"Boolean": true,
"Slice": [1, 2, 3],
"Anonymous": "avoid to read"
}
`)
json.Unmarshal(data, &instance)
fmt.Printf("struct data:\n%+v\n", instance)
}
// https://play.golang.org/p/0cfY0LZ0NdL
//
// all this good work is done by https://github.com/Ompluscator/dynamic-struct
// which offers much more functionality than I had stripped out from that library
//
// I was working on something where I wanted only standard library implementation code
// so I decided to just strip out the code I needed
//
package main
import (
"encoding/json"
"fmt"
"reflect"
)
// 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 DynamicStruct interface {
// New provides new instance of defined dynamic struct.
//
// value := dStruct.New()
//
New() interface{}
}
// Builder holds all fields' definitions for desired structs.
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
// Build returns definition for dynamic struct.
// Definition can be used to create new instances.
//
// dStruct := builder.Build()
//
Build() DynamicStruct
}
type dynamicStructImpl struct {
definition reflect.Type
}
type fieldConfigImpl struct {
typ interface{}
tag string
}
type builderImpl struct {
fields map[string]*fieldConfigImpl
}
func (b *builderImpl) AddField(name string, typ interface{}, tag string) Builder {
b.fields[name] = &fieldConfigImpl{
typ: typ,
tag: tag,
}
return b
}
func (b *builderImpl) Build() DynamicStruct {
var structFields []reflect.StructField
for name, field := range b.fields {
structFields = append(structFields, reflect.StructField{
Name: name,
Type: reflect.TypeOf(field.typ),
Tag: reflect.StructTag(field.tag),
})
}
return &dynamicStructImpl{
definition: reflect.StructOf(structFields),
}
}
func (ds *dynamicStructImpl) New() interface{} {
return reflect.New(ds.definition).Interface()
}
// NewStruct returns new clean instance of Builder interface
// for defining fresh dynamic struct.
//
// builder := NewStruct()
//
func NewStruct() Builder {
return &builderImpl{
fields: map[string]*fieldConfigImpl{},
}
}
func main() {
instance := NewStruct()
// now you can loop a data structure to generate these calls...
instance.AddField("Integer", 0, `json:"int"`)
instance.AddField("Text", "", `json:"someText"`)
instance.AddField("Float", 0.0, `json:"double"`)
instance.AddField("Boolean", false, "")
instance.AddField("Slice", []int{}, "")
instance.AddField("Anonymous", "", `json:"-"`)
value := instance.Build().New()
data := []byte(`
{
"int": 123,
"someText": "example",
"double": 123.45,
"Boolean": true,
"Slice": [1, 2, 3],
"Anonymous": "avoid to read"
}
`)
json.Unmarshal(data, &value)
fmt.Printf("struct data:\n%+v\n", value)
}