A high-performance Go library for binding data to structs using reflection with configurable field mapping and type conversion.
- High Performance: Optimized reflection usage with pre-allocated maps and efficient search algorithms
- Flexible Binding: Support for maps, slices, channels, and custom iterators
- Configurable Tags: Custom struct tags for field mapping and validation
- Type Safety: Built-in type conversion with comprehensive error handling
- Multiple Sources: Bind from various data sources with unified interface
- Required Fields: Built-in validation for required field checking
go get github.com/Bofry/structprotoimport (
"fmt"
"time"
"github.com/Bofry/structproto"
"github.com/Bofry/structproto/valuebinder"
)
type Character struct {
Name string `demo:"*NAME"`
Age *int `demo:"*AGE"`
Alias []string `demo:"ALIAS"`
DateOfBirth time.Time `demo:"DATE_OF_BIRTH;the character's birth of date"`
Remark string `demo:"REMARK;note the character's personal favor"`
}
func main() {
c := Character{}
prototype, err := structproto.Prototypify(&c,
&structproto.StructProtoResolveOption{
TagName: "demo",
})
if err != nil {
panic(err)
}
err = prototype.BindMap(map[string]interface{}{
"NAME": "luffy",
"AGE": "19",
"ALIAS": "lucy",
"DATE_OF_BIRTH": "2020-05-05T00:00:00Z",
}, valuebinder.BuildStringArgsBinder)
if err != nil {
panic(err)
}
fmt.Printf("Name : %q\n", c.Name)
fmt.Printf("Age : %d\n", *c.Age)
fmt.Printf("Alias : %q\n", c.Alias)
fmt.Printf("DateOfBirth: %q\n", c.DateOfBirth)
fmt.Printf("Remark : %q\n", c.Remark)
// Output:
// Name : "luffy"
// Age : 19
// Alias : ["lucy"]
// DateOfBirth: "2020-05-05 00:00:00 +0000 UTC"
// Remark : ""
}import (
"fmt"
"time"
"github.com/Bofry/structproto"
"github.com/Bofry/structproto/valuebinder"
)
type Character struct {
Name string `demo:"*NAME"`
Age *int `demo:"*AGE"`
Alias []string `demo:"ALIAS"`
DateOfBirth time.Time `demo:"DATE_OF_BIRTH;the character's birth of date"`
Remark string `demo:"REMARK;note the character's personal favor"`
}
func main() {
c := Character{}
prototype, err := structproto.Prototypify(&c,
&structproto.StructProtoResolveOption{
TagName: "demo",
})
if err != nil {
panic(err)
}
err = prototype.BindMap(structproto.FieldValueMap{
"NAME": "luffy",
"AGE": "19",
"ALIAS": "lucy",
"DATE_OF_BIRTH": "2020-05-05T00:00:00Z",
}, valuebinder.BuildStringArgsBinder)
if err != nil {
panic(err)
}
fmt.Printf("Name : %q\n", c.Name)
fmt.Printf("Age : %d\n", *c.Age)
fmt.Printf("Alias : %q\n", c.Alias)
fmt.Printf("DateOfBirth: %q\n", c.DateOfBirth)
fmt.Printf("Remark : %q\n", c.Remark)
// Output:
// Name : "luffy"
// Age : 19
// Alias : ["lucy"]
// DateOfBirth: "2020-05-05 00:00:00 +0000 UTC"
// Remark : ""
}import (
"fmt"
"time"
"github.com/Bofry/structproto"
"github.com/Bofry/structproto/valuebinder"
)
type Character struct {
Name string `demo:"*NAME"`
Age *int `demo:"*AGE"`
Alias []string `demo:"ALIAS"`
DateOfBirth time.Time `demo:"DATE_OF_BIRTH;the character's birth of date"`
Remark string `demo:"REMARK;note the character's personal favor"`
}
type FieldValue struct {
Name string
Value string
}
func main() {
c := Character{}
prototype, err := structproto.Prototypify(&c,
&structproto.StructProtoResolveOption{
TagName: "demo",
})
if err != nil {
panic(err)
}
err = prototype.BindFields([]structproto.FieldValueEntity{
{Field: "NAME", Value: "luffy"},
{Field: "AGE", Value: "19"},
{Field: "ALIAS", Value: "lucy"},
{Field: "DATE_OF_BIRTH", Value: "2020-05-05T00:00:00Z"},
}, valuebinder.BuildStringArgsBinder)
if err != nil {
panic(err)
}
fmt.Printf("Name : %q\n", c.Name)
fmt.Printf("Age : %d\n", *c.Age)
fmt.Printf("Alias : %q\n", c.Alias)
fmt.Printf("DateOfBirth: %q\n", c.DateOfBirth)
fmt.Printf("Remark : %q\n", c.Remark)
// Output:
// Name : "luffy"
// Age : 19
// Alias : ["lucy"]
// DateOfBirth: "2020-05-05 00:00:00 +0000 UTC"
// Remark : ""
}import (
"fmt"
"time"
"github.com/Bofry/structproto"
"github.com/Bofry/structproto/valuebinder"
)
type Character struct {
Name string `demo:"*NAME"`
Age *int `demo:"*AGE"`
Alias []string `demo:"ALIAS"`
DateOfBirth time.Time `demo:"DATE_OF_BIRTH;the character's birth of date"`
Remark string `demo:"REMARK;note the character's personal favor"`
}
type FieldValue struct {
Name string
Value string
}
func main() {
c := Character{}
prototype, err := structproto.Prototypify(&c,
&structproto.StructProtoResolveOption{
TagName: "demo",
})
if err != nil {
panic(err)
}
err = prototype.BindChan(func() <-chan structproto.FieldValueEntity {
c := make(chan structproto.FieldValueEntity, 1)
go func() {
for _, v := range fieldValues {
c <- v
}
close(c)
}()
return c
}(), valuebinder.BuildStringArgsBinder)
if err != nil {
panic(err)
}
fmt.Printf("Name : %q\n", c.Name)
fmt.Printf("Age : %d\n", *c.Age)
fmt.Printf("Alias : %q\n", c.Alias)
fmt.Printf("DateOfBirth: %q\n", c.DateOfBirth)
fmt.Printf("Remark : %q\n", c.Remark)
// Output:
// Name : "luffy"
// Age : 19
// Alias : ["lucy"]
// DateOfBirth: "2020-05-05 00:00:00 +0000 UTC"
// Remark : ""
}import (
"fmt"
"time"
"github.com/Bofry/structproto"
"github.com/Bofry/structproto/valuebinder"
)
type Character struct {
Name string `demo:"*NAME"`
Age *int `demo:"*AGE"`
Alias []string `demo:"ALIAS"`
DateOfBirth time.Time `demo:"DATE_OF_BIRTH;the character's birth of date"`
Remark string `demo:"REMARK;note the character's personal favor"`
}
var _ structproto.Iterator = EntitySet(nil)
type EntitySet [][]string
func (set EntitySet) Iterate() <-chan structproto.FieldValueEntity {
c := make(chan structproto.FieldValueEntity, 1)
go func() {
for _, v := range set {
c <- structproto.FieldValueEntity{
Field: v[0],
Value: v[1],
}
}
close(c)
}()
return c
}
func main() {
c := Character{}
prototype, err := structproto.Prototypify(&c,
&structproto.StructProtoResolveOption{
TagName: "demo",
})
if err != nil {
panic(err)
}
err = prototype.BindIterator(EntitySet{
{"NAME", "luffy"},
{"AGE", "19"},
{"ALIAS", "lucy"},
{"DATE_OF_BIRTH", "2020-05-05T00:00:00Z"},
{"NUMBERS", "5,12"},
}, valuebinder.BuildStringArgsBinder)
if err != nil {
panic(err)
}
fmt.Printf("Name : %q\n", c.Name)
fmt.Printf("Age : %d\n", *c.Age)
fmt.Printf("Alias : %q\n", c.Alias)
fmt.Printf("DateOfBirth: %q\n", c.DateOfBirth)
fmt.Printf("Remark : %q\n", c.Remark)
// Output:
// Name : "luffy"
// Age : 19
// Alias : ["lucy"]
// DateOfBirth: "2020-05-05 00:00:00 +0000 UTC"
// Remark : ""
}import (
"fmt"
"time"
"github.com/Bofry/structproto"
"github.com/Bofry/structproto/valuebinder"
)
type Character struct {
Name string `demo:"*NAME"`
Age *int `demo:"*AGE"`
Alias []string `demo:"ALIAS"`
DateOfBirth time.Time `demo:"DATE_OF_BIRTH;the character's birth of date"`
Remark string `demo:"REMARK;note the character's personal favor"`
}
type MapBinder struct {
values map[string]string
}
func (b *MapBinder) Init(context *StructProtoContext) error {
return nil
}
func (b *MapBinder) Bind(field FieldInfo, rv reflect.Value) error {
name := field.Name()
if v, ok := b.values[name]; ok {
return valuebinder.StringArgsBinder(rv).Bind(v)
}
return nil
}
func (b *MapBinder) Deinit(context *StructProtoContext) error {
// validate missing required fields
return context.CheckIfMissingRequiredFields(func() <-chan string {
c := make(chan string)
go func() {
for k := range b.values {
c <- k
}
close(c)
}()
return c
})
}
func main() {
c := Character{}
prototype, err := structproto.Prototypify(&c,
&structproto.StructProtoResolveOption{
TagName: "demo",
})
if err != nil {
panic(err)
}
binder := &MapBinder{
values: map[string]string{
"NAME": "luffy",
"AGE": "19",
"ALIAS": "lucy",
"DATE_OF_BIRTH": "2020-05-05T00:00:00Z",
},
}
err = prototype.Bind(binder)
if err != nil {
panic(err)
}
fmt.Printf("Name : %q\n", c.Name)
fmt.Printf("Age : %d\n", *c.Age)
fmt.Printf("Alias : %q\n", c.Alias)
fmt.Printf("DateOfBirth: %q\n", c.DateOfBirth)
fmt.Printf("Remark : %q\n", c.Remark)
// Output:
// Name : "luffy"
// Age : 19
// Alias : ["lucy"]
// DateOfBirth: "2020-05-05 00:00:00 +0000 UTC"
// Remark : ""
}The main resolver for creating struct prototypes.
type StructProtoResolver struct {
// Configuration fields
}
func NewStructProtoResolver(option *StructProtoResolveOption) *StructProtoResolver
func (r *StructProtoResolver) Resolve(target interface{}) (*Struct, error)Represents a resolved struct prototype with binding capabilities.
type Struct struct {
// Internal fields
}
// Binding methods
func (s *Struct) BindMap(values map[string]interface{}, buildValueBinder ValueBindProvider) error
func (s *Struct) BindFields(values []FieldValueEntity, buildValueBinder ValueBindProvider) error
func (s *Struct) BindChan(iterator <-chan FieldValueEntity, buildValueBinder ValueBindProvider) error
func (s *Struct) BindIterator(iterator Iterator, buildValueBinder ValueBindProvider) error
func (s *Struct) Bind(binder StructBinder) error
func (s *Struct) Map(mapper StructMapper) error
func (s *Struct) Visit(visitor StructVisitor)type StructProtoResolveOption struct {
TagName string // Custom tag name for field mapping
TagResolver TagResolver // Custom tag resolution logic
CheckDuplicateNames bool // Enable duplicate field name checking
}Fields can be configured using struct tags:
type Example struct {
RequiredField string `demo:"*FIELD_NAME"` // Required field
OptionalField string `demo:"FIELD_NAME"` // Optional field
WithDesc string `demo:"FIELD;description here"` // Field with description
}*prefix marks required fields;separates field name from description
This library has been optimized for high-performance scenarios:
- Pre-allocated Maps: Reduces memory allocations during struct initialization
- Efficient Search: Optimized binary search for field lookups
- Minimal Goroutines: Eliminates unnecessary concurrency overhead
- Reflection Caching: Reuses reflection metadata where possible
// Run benchmarks
go test -bench=. -benchmem- Go 1.21 or later
- No external dependencies (except for testing)
# Run all tests
go test ./...
# Run with coverage
go test -cover ./...
# Run specific package tests
go test ./valuebinder -v- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for your changes
- Ensure all tests pass (
go test ./...) - Run linting (
go fmt ./... && go vet ./...) - Commit your changes (
git commit -am 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
For more comprehensive examples, check out the example_test.go file.