structil
structil copied to clipboard
Go struct utilities with reflection for JSON data decoding, map-liked data accessing, dynamic struct building and more
structil 
struct + util = structil, for runtime and dynamic environment in Go.
Why?
I'd like to ...
- conveniently handle and decode the known or unknown formatted JSON/YAML
- conveniently dive into the specific field in nested struct
- simply verify if a field with the specified name and type exists in object
- etc
with Go reflection package experimentally.
*** JSON and YAML format is known or unknown ***
JSON →→→→→→→→→→→→→→→→↓ →→ (known format) struct →→→→→→→→→→→↓→→→ (use struct directly)
↓ ↑ ↓
↓→→ map →→→ (unknown format) "DynamicStruct" →→→→→→ "Getter", "Finder"
↑
YAML →→→→→→→→→→→→→→→→↑
↑
(and other formats) →↑
Please see my medium post as well.
Sample Usecase
Try printing the struct definition from the unknown formatted JSON decoding.
package main
import (
"fmt"
"github.com/goldeneggg/structil/dynamicstruct/decoder"
)
func main() {
unknownJSON := []byte(`
{
"string_field":"かきくけこ",
"int_field":45678,
"bool_field":false,
"object_field":{
"id":12,
"name":"the name",
"nested_object_field": {
"address": "Tokyo",
"is_manager": true
}
},
"array_string_field":[
"array_str_1",
"array_str_2"
],
"array_struct_field":[
{
"kkk":"kkk1",
"vvvv":"vvv1"
},
{
"kkk":"kkk2",
"vvvv":"vvv2"
}
],
"null_field":null
}
`)
// create `Decoder` from JSON
dec, err := decoder.FromJSON(unknownJSON)
if err != nil {
panic(err)
}
// - If `nest` is true, nested object attributes will be also decoded to struct recursively
// - If `nest` is false, nested object attributes will be decoded to `map[string]interface{}`
nest := true
// - If `useTag` is true, JSON Struct tags are defined
useTag := true
// create `DynamicStruct` from `Decoder`
ds, err := dec.DynamicStruct(nest, useTag)
if err != nil {
panic(err)
}
// print struct definition from `DynamicStruct`
fmt.Println(ds.Definition())
}
This program will print a Go struct definition string as follows.
// - Type name is "DynamicStruct" (raname is available)
// - Field names are automatically camelized from input json attribute names
// - Fields are ordered by field name
type DynamicStruct struct {
ArrayStringField []string `json:"array_string_field"`
ArrayStructField []struct {
Kkk string `json:"kkk"`
Vvvv string `json:"vvvv"`
} `json:"array_struct_field"`
BoolField bool `json:"bool_field"`
IntField float64 `json:"int_field"`
NullField interface {} `json:"null_field"`
ObjectField struct {
Id float64 `json:"id"`
Name string `json:"name"`
NestedObjectField struct {
Address string `json:"address"`
IsManager bool `json:"is_manager"`
} `json:"nested_object_field"`
} `json:"object_field"`
StringField string `json:"string_field"`
}
And see example code.
More Examples
From JSON to Getter
We can convert from the unknown formatted JSON to Getter via DynamicStruct with decoder.JSONToGetter function.
See example code.
What is Getter?
We can access a struct using field name string, like (typed) map with structil.NewGetter function.
g, err := structil.NewGetter(structOrStructPointerVariable)
// get num of struct fields
g.NumField()
// names of struct fields
g.Names()
// return true if struct has a "fName" field
g.Has(fName)
// get "fName" field value of the original struct as string
g.String(fName)
// return true if "fName" field value of the original struct is float64
g.IsFloat64(fName)
// convert from struct to map[string]interface{}
g.ToMap()
// get as `Getter` if "fName" field is a (nested) struct
gNest, ok := g.GetGetter(fName)
gNest.NumField()
gNest.Names()
See example code
Getter.MapGet method
Getter.MapGet method provides the Map collection function for slice of struct
See example code
From JSON to DynamicStruct
We can convert from the unknown formatted JSON to DynamicStruct with Decoder (from decoder.FromJSON function) and Decoder.DynamicStruct method.
See example code.
What is DynamicStruct?
We can create the dynamic and runtime struct.
See example code
Finder
We can access usefully nested struct fields using field name string.
See example code
With config file? use FinderKeys
We can create a Finder from the configuration file that have some finding target keys. We support some file formats of configuration file such as yaml, json, toml and more.
See example code
Thanks for the awesome configuration management library spf13/viper.
Benchmark
See this file
It's the latest benchmark result that is executed on GitHub Actions runner instance.