### Install mapstructure Go Library Source: https://github.com/mitchellh/mapstructure/blob/main/README.md Use 'go get' to install the mapstructure library. This is the standard method for obtaining Go packages. ```bash go get github.com/mitchellh/mapstructure ``` -------------------------------- ### Mapstructure Struct Tag Syntax Examples Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/quick-reference.md Examples demonstrating various mapstructure struct tag options, including basic renaming, ignoring fields, squashing embedded structs, collecting remaining keys, and omitting fields if empty. ```go // Basic field renaming Name string `mapstructure:"full_name"` ``` ```go // Ignore field Password string `mapstructure:"-"` ``` ```go // Squash embedded struct Address Address `mapstructure:",squash"` ``` ```go // Collect remaining keys Extra map[string]interface{} `mapstructure:",remain"` ``` ```go // Omit if empty (only when encoding to map) Comment string `mapstructure:",omitempty"` ``` ```go // Combined: named field + squash Addr Address `mapstructure:"address,squash"` ``` -------------------------------- ### Mapstructure Decode with Metadata Example Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/types.md Shows a practical example of decoding input data into a struct while capturing metadata. It demonstrates how to use DecodeMetadata to populate Keys, Unused, and Unset fields. ```go type Config struct { Port int `mapstructure:"port"` Host string `mapstructure:"host"` } input := map[string]interface{}{ "port": 8080, "env": "production", } var cfg Config meta := &mapstructure.Metadata{} mapstructure.DecodeMetadata(input, &cfg, meta) fmt.Println(meta.Keys) // [port] fmt.Println(meta.Unused) // [env] fmt.Println(meta.Unset) // [host] ``` -------------------------------- ### Mapstructure Decode Hook Examples Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-decoder.md Provides examples of implementing custom decode hooks. Includes a type-based hook for string to time.Duration conversion and a kind-based hook for numeric to string conversion. Shows how to integrate these hooks into the DecoderConfig. ```go // Type-based hook: convert string to time.Duration typeHook := func(from, to reflect.Type, data interface{}) (interface{}, error) { if to == reflect.TypeOf(time.Duration(0)) && from.Kind() == reflect.String { return time.ParseDuration(data.(string)) } return data, nil } // Kind-based hook: convert any numeric type to string kindHook := func(from, to reflect.Kind, data interface{}) (interface{}, error) { if to == reflect.String && (from == reflect.Int || from == reflect.Float64) { return fmt.Sprintf("%v", data), nil } return data, nil } config := &mapstructure.DecoderConfig{ Result: &target, DecodeHook: typeHook, } ``` -------------------------------- ### DecodeMetadata Example Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-functions.md Demonstrates standard decoding using DecodeMetadata and capturing decode process metadata. Use when precise decoding without type coercion is needed and insight into key/field mapping is desired. ```go type Person struct { Name string `mapstructure:"name"` Age int `mapstructure:"age"` } input := map[string]interface{}{ "name": "Bob", "city": "NYC", } var p Person meta := &mapstructure.Metadata{} err := mapstructure.DecodeMetadata(input, &p, meta) if err != nil { log.Fatalf("Decode failed: %v", err) } fmt.Println("Keys used:", meta.Keys) // [name] fmt.Println("Unused keys:", meta.Unused) // [city] fmt.Println("Unset fields:", meta.Unset) // [age] ``` -------------------------------- ### WeakDecodeMetadata Example Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-functions.md Illustrates weak decoding with type conversions and metadata tracking using WeakDecodeMetadata. This is useful when flexible type coercion is required alongside visibility into the decoding process. ```go type Settings struct { ApiKey string `mapstructure:"api_key"` Timeout int `mapstructure:"timeout"` Retries int `mapstructure:"retries"` } input := map[string]interface{}{ "api_key": "sk_live_xyz789", "timeout": "30", // string → int "unknown": "field", } var settings Settings meta := &mapstructure.Metadata{} er r := mapstructure.WeakDecodeMetadata(input, &settings, meta) if err != nil { log.Fatalf("Decode failed: %v", err) } fmt.Println("Unused:", meta.Unused) // [unknown] fmt.Println("Unset:", meta.Unset) // [retries] ``` -------------------------------- ### Mapstructure Kind-based Decode Hook Example Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/types.md An example of a kind-based hook that converts strings to integers using strconv.Atoi. ```go kindHook := func(from, to reflect.Kind, data interface{}) (interface{}, error) { // Check general kinds if from == reflect.String && to == reflect.Int { return strconv.Atoi(data.(string)) } return data, nil } ``` -------------------------------- ### WeaklyTypedHook Example Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-decode-hooks.md Demonstrates using WeaklyTypedHook to enable weak type conversions during decoding. This hook supports conversions like bool to string, float to string, int to string, uint to string, and byte slice to string. ```go type Config struct { Port int `mapstructure:"port"` Enabled bool `mapstructure:"enabled"` } input := map[string]interface{}{ "port": 5432, "enabled": true, } var cfg Config decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &cfg, DecodeHook: mapstructure.WeaklyTypedHook, }) decoder.Decode(input) ``` -------------------------------- ### Mapstructure Value-based Decode Hook Example Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/types.md An example of a value-based hook that converts strings to integers, handling empty strings by returning 0. ```go valueHook := func(from, to reflect.Value) (interface{}, error) { // Access actual values if from.Kind() == reflect.String { str := from.String() if str == "" { return 0, nil } return strconv.Atoi(str) } return from.Interface(), nil } ``` -------------------------------- ### Mapstructure Type-based Decode Hook Example Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/types.md An example of a type-based hook that transforms strings to time.Duration by parsing the string. ```go typeHook := func(from, to reflect.Type, data interface{}) (interface{}, error) { // Check specific types if from.Kind() == reflect.String && to == reflect.TypeOf(time.Duration(0)) { return time.ParseDuration(data.(string)) } return data, nil } ``` -------------------------------- ### Example JSON Structure Source: https://github.com/mitchellh/mapstructure/blob/main/README.md This JSON demonstrates a scenario where the 'type' field might determine the subsequent structure. mapstructure can help decode such dynamic data. ```json { "type": "person", "name": "Mitchell" } ``` -------------------------------- ### Compose Decode Hooks for Sequential Transformations Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/usage-patterns.md Chain multiple decode hooks to perform sequential data transformations. This example first converts a string to an integer and then validates if the integer is within a valid port range. Use when multiple transformation steps are needed or to separate concerns like conversion and validation. ```go // Hook 1: String to int conversion stringToInt := func(from, to reflect.Kind, data interface{}) (interface{}, error) { if from == reflect.String && to == reflect.Int { return strconv.Atoi(data.(string)) } return data, nil } // Hook 2: Validate port range validatePort := func(from, to reflect.Kind, data interface{}) (interface{}, error) { if to == reflect.Int { port := data.(int) if port < 1 || port > 65535 { return nil, fmt.Errorf("port %d out of range", port) } } return data, nil } // Compose hooks: first convert, then validate composed := mapstructure.ComposeDecodeHookFunc(stringToInt, validatePort) type Server struct { Port int `mapstructure:"port"` } var srv Server decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &srv, DecodeHook: composed, }) ``` -------------------------------- ### RecursiveStructToMapHookFunc Example Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-decode-hooks.md Shows how to use RecursiveStructToMapHookFunc to treat all structs as maps during decoding. This is useful for decoding structs into map targets, allowing struct fields to be represented as map entries. ```go type Person struct { Name string Age int } input := Person{ Name: "Alice", Age: 30, } var result map[string]interface{} decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &result, DecodeHook: mapstructure.RecursiveStructToMapHookFunc(), }) decoder.Decode(input) // result contains struct fields as map entries ``` -------------------------------- ### Fix Array/Slice Length Mismatch in Go Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/errors.md This example shows an error where the input slice has more elements than the target array. To fix this, either trim the input slice to match the array's length or use a slice type for the target if variable length is acceptable. ```Go type Config struct { Values [2]int `mapstructure:"values"` } input := map[string]interface{}{ "values": []int{1, 2, 3}, // 3 elements for [2]int } var cfg Config mapstructure.Decode(input, &cfg) // Error: 'values': expected source data to have length less or equal to 2, got 3 ``` -------------------------------- ### Custom Decode Hooks with mapstructure Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/examples.md Implement custom transformations during decoding using `ComposeDecodeHookFunc`. This example shows how to convert a comma-separated string to a slice of strings and parse a string into a validated float64. Ensure input types match hook expectations. ```go package main import ( "fmt" "log" "reflect" "strconv" "strings" "github.com/mitchellh/mapstructure" ) type UserProfile struct { Name string `mapstructure:"name" Email string `mapstructure:"email" Tags []string `mapstructure:"tags" Score float64 `mapstructure:"score" } func main() { // Define multiple hooks // Hook 1: String to slice (split by comma) stringToSlice := func(f, t reflect.Kind, data interface{}) (interface{}, error) { if f != reflect.String || t != reflect.Slice { return data, nil } str := data.(string) if str == "" { return []string{}, nil } parts := strings.Split(str, ",") // Trim whitespace for i := range parts { parts[i] = strings.TrimSpace(parts[i]) } return parts, nil } // Hook 2: String to float (with validation) stringToFloat := func(from, to reflect.Type, data interface{}) (interface{}, error) { if from.Kind() != reflect.String || to.Kind() != reflect.Float64 { return data, nil } val, err := strconv.ParseFloat(data.(string), 64) if err != nil { return nil, fmt.Errorf("invalid score: %s", data) } if val < 0 || val > 100 { return nil, fmt.Errorf("score must be between 0 and 100, got %f", val) } return val, nil } // Compose hooks: stringToSlice runs first, then stringToFloat composed := mapstructure.ComposeDecodeHookFunc(stringToSlice, stringToFloat) input := map[string]interface{}{ "name": "Charlie", "email": "charlie@example.com", "tags": "go, rust, python", // Will be split "score": "92.5", // Will be parsed and validated } var profile UserProfile decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &profile, DecodeHook: composed, }) if err := decoder.Decode(input); err != nil { log.Fatalf("Decode failed: %v", err) } fmt.Printf("Profile: %+v\n", profile) fmt.Printf("Tags: %v\n", profile.Tags) fmt.Printf("Score: %.1f\n", profile.Score) } ``` -------------------------------- ### Create and Use Decoder Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-decoder.md Demonstrates creating a new Decoder with a custom configuration and then using it to decode input data. Ensure the Result field in DecoderConfig is a pointer to the target struct or map. ```go config := &mapstructure.DecoderConfig{ Result: &targetStruct, TagName: "mapstructure", Metadata: &mapstructure.Metadata{}, } decoder, err := mapstructure.NewDecoder(config) if err != nil { log.Fatalf("Failed to create decoder: %v", err) } input := map[string]interface{}{ "name": "John", "age": 30, } err = decoder.Decode(input) if err != nil { log.Fatalf("Failed to decode: %v", err) } ``` -------------------------------- ### Parse Configuration File Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/examples.md Parses a configuration map into a Go struct, demonstrating the use of a custom hook to convert string durations to time.Duration. ```go package main import ( "fmt" "log" "time" "github.com/mitchellh/mapstructure" ) type DatabaseConfig struct { Host string `mapstructure:"host" Port int `mapstructure:"port" Username string `mapstructure:"username" Password string `mapstructure:"password" MaxConn int `mapstructure:"max_connections" } type ServerConfig struct { Name string `mapstructure:"name" Port int `mapstructure:"port" Database DatabaseConfig `mapstructure:"database" Timeout time.Duration `mapstructure:"timeout" Debug bool `mapstructure:"debug" } func main() { // Configuration as it would come from YAML/JSON configData := map[string]interface{}{ "name": "MyServer", "port": 8080, "database": map[string]interface{}{ "host": "db.example.com", "port": 5432, "username": "admin", "password": "secret", "max_connections": 50, }, "timeout": "30s", "debug": true, } // Add timeout hook for string to time.Duration conversion hook := mapstructure.StringToTimeDurationHookFunc() var cfg ServerConfig decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &cfg, DecodeHook: hook, }) if err != nil { log.Fatalf("Failed to create decoder: %v", err) } if err := decoder.Decode(configData); err != nil { log.Fatalf("Failed to decode configuration: %v", err) } fmt.Printf("Configuration loaded:\n") fmt.Printf(" Server: %s:%d\n", cfg.Name, cfg.Port) fmt.Printf(" Database: %s:%d\n", cfg.Database.Host, cfg.Database.Port) fmt.Printf(" Timeout: %v\n", cfg.Timeout) fmt.Printf(" Debug: %v\n", cfg.Debug) } ``` -------------------------------- ### Strict Configuration Validation Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/configuration.md Use this preset to enforce strict validation where all keys must be recognized and all fields must be provided. It's useful for ensuring complete and accurate configuration. ```go config := &mapstructure.DecoderConfig{ Result: &target, ErrorUnused: true, // All keys must be recognized ErrorUnset: true, // All fields must be provided Metadata: &meta, // Track what happened } ``` -------------------------------- ### Mapstructure Metadata Initialization Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/types.md Demonstrates how to initialize a Metadata struct and pass it to a DecoderConfig. The decoder automatically initializes empty slices for Keys, Unused, and Unset. ```go meta := &mapstructure.Metadata{} de תecoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Metadata: meta, }) // After decoder creation, meta.Keys, meta.Unused, and meta.Unset are initialized to empty []string ``` -------------------------------- ### Combine Mapstructure Tag Options Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/struct-tags.md Demonstrates combining multiple mapstructure tag options like custom key names, 'squash', and 'omitempty'. ```go type Config struct { Name string `mapstructure:"name" Addr Address `mapstructure:"address,squash" Title string `mapstructure:"title,omitempty" } ``` -------------------------------- ### Implement Custom Decode Hooks in mapstructure Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/usage-patterns.md Define custom decode hooks to transform data types during the decoding process. This example shows converting ISO 8601 timestamp strings to time.Time objects. ```go // Hook: Convert ISO 8601 timestamp strings to time.Time hook := func(from, to reflect.Type, data interface{}) (interface{}, error) { if from.Kind() != reflect.String { return data, nil } if to != reflect.TypeOf(time.Time{}) { return data, nil } str := data.(string) return time.Parse(time.RFC3339, str) } type Event struct { ID string `mapstructure:"id" Timestamp time.Time `mapstructure:"timestamp" } input := map[string]interface{}{ "id": "evt_123", "timestamp": "2024-06-15T10:30:00Z", } var event Event decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &event, DecodeHook: hook, }) if err := decoder.Decode(input); err != nil { log.Fatalf("Decode failed: %v", err) } ``` -------------------------------- ### Use Pointers for Optional Fields in Mapstructure Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/struct-tags.md Pointers are used for optional fields to distinguish between a field not being provided and a field being set to its zero value. This example shows how fields are handled when not present in the input map. ```go type User struct { Name string `mapstructure:"name"` Email *string `mapstructure:"email"` Age *int `mapstructure:"age"` } input := map[string]interface{}{ "name": "Bob", // email not provided // age not provided } var user User mapstructure.Decode(input, &user) // user.Name == "Bob" // user.Email == nil (not provided) // user.Age == nil (not provided) ``` -------------------------------- ### Basic Struct Decoding with mapstructure Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/usage-patterns.md Use this for simple struct mapping and quick configuration parsing without special type conversion needs. Ensure the input map keys match struct field tags. ```go package main import ( "log" "github.com/mitchellh/mapstructure" ) type User struct { Name string `mapstructure:"name" Email string `mapstructure:"email" Age int `mapstructure:"age" } func main() { input := map[string]interface{}{ "name": "Alice", "email": "alice@example.com", "age": 28, } var user User if err := mapstructure.Decode(input, &user); err != nil { log.Fatalf("Decode failed: %v", err) } log.Printf("User: %+v", user) } ``` -------------------------------- ### Capture Unknown Keys with Remain Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/examples.md Use the `,remain` tag to capture unknown configuration keys into a map. This is useful for extensibility, allowing plugins to accept arbitrary configuration options. ```go package main import ( "fmt" "github.com/mitchellh/mapstructure" ) type PluginConfig struct { Name string `mapstructure:"name" Version string `mapstructure:"version" // Capture any other keys Extra map[string]interface{} `mapstructure:",remain" } func main() { input := map[string]interface{}{ "name": "MyPlugin", "version": "1.0.0", "author": "John Doe", "license": "MIT", "config_path": "/etc/plugin.conf", "timeout": 30, } var cfg PluginConfig if err := mapstructure.Decode(input, &cfg); err != nil { panic(err) } fmt.Printf("Plugin: %s v%s\n", cfg.Name, cfg.Version) fmt.Printf("Additional configuration:\n") for key, value := range cfg.Extra { fmt.Printf(" %s: %v\n", key, value) } } ``` -------------------------------- ### Import mapstructure Package Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/quick-reference.md Import the mapstructure package to use its decoding functionalities. ```go import "github.com/mitchellh/mapstructure" ``` -------------------------------- ### Mapstructure Decoding Flow Diagram Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/internal-behavior.md Illustrates the main path of the decoding process, from initial configuration to recursive decoding by kind. Shows where decode hooks are applied and the dispatch mechanism for different data types. ```text Decode(input, output) ↓ NewDecoder(config) [validates config] ↓ Decoder.Decode(input) ↓ Decoder.decode(name, input, reflect.Value) [recursive] ├─ Apply DecodeHook if configured ├─ Dispatch by Kind: │ ├─ Bool → decodeBool() │ ├─ String → decodeString() │ ├─ Int/Uint → decodeInt() / decodeUint() │ ├─ Float32/Float64 → decodeFloat() │ ├─ Struct → decodeStruct() │ ├─ Map → decodeMap() │ ├─ Ptr → decodePtr() │ ├─ Slice → decodeSlice() │ ├─ Array → decodeArray() │ └─ Func → decodeFunc() └─ Return error or nil ``` -------------------------------- ### Nested Structure Decoding with Squash and Remain Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/struct-tags.md Shows how to decode nested structures using 'squash' to embed fields and 'remain' to capture unmapped fields. ```go type Database struct { Host string `mapstructure:"host" Port int `mapstructure:"port" } type Cache struct { TTL int `mapstructure:"ttl" } type AppConfig struct { Name string `mapstructure:"app_name" Database Database `mapstructure:"db" Cache Cache `mapstructure:",squash" Features map[string]interface{} `mapstructure:",remain" } ``` ```go input := map[string]interface{}{ "app_name": "myapp", "db": map[string]interface{}{ "host": "localhost", "port": 5432, }, "ttl": 3600, "enable_logging": true, } ``` ```go AppConfig{ Name: "myapp", Database: Database{ Host: "localhost", Port: 5432, }, Cache: Cache{ TTL: 3600, }, Features: map[string]interface{}{ "enable_logging": true, }, } ``` -------------------------------- ### Use Metadata for Unused/Unset Keys Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/quick-reference.md Shows how to use the Metadata field in DecoderConfig to track unused keys in the input map and fields that were not set in the target struct during decoding. ```go meta := &mapstructure.Metadata{} de תecoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &target, Metadata: meta, }) if err := decoder.Decode(input); err != nil { log.Fatalf("Decode failed: %v", err) } // Check what happened if len(meta.Unused) > 0 { log.Printf("Warning: unused keys: %v", meta.Unused) } if len(meta.Unset) > 0 { log.Printf("Info: unset fields: %v", meta.Unset) } ``` -------------------------------- ### Slice Decoding Logic Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/internal-behavior.md Illustrates the logic for decoding slices, handling empty or nil slices, allocating new slices based on input length, and recursively decoding elements. Errors are accumulated per element. ```go if dataVal.Len() == 0: if dataVal.IsNil(): val = nil else: val = empty slice (preserve empty vs nil) else: allocate slice of input length decode each element recursively handle errors per-element (accumulate) ``` -------------------------------- ### Using TextUnmarshallerHookFunc with net.URL Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-decode-hooks.md Demonstrates how to use TextUnmarshallerHookFunc to decode a string into a type that implements encoding.TextUnmarshaler (net.URL in this case). Ensure the DecoderConfig includes the TextUnmarshallerHookFunc. ```go // net.URL implements encoding.TextUnmarshaler type Config struct { WebsiteURL url.URL `mapstructure:"website_url"` } input := map[string]interface{}{ "website_url": "https://example.com", } var cfg Config decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &cfg, DecodeHook: mapstructure.TextUnmarshallerHookFunc(), }) decoder.Decode(input) fmt.Println(cfg.WebsiteURL.String()) // https://example.com ``` -------------------------------- ### NewDecoder Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-decoder.md Creates a new Decoder instance with the specified configuration. This function is the primary way to initialize a configurable decoder. ```APIDOC ## NewDecoder Creates a new `Decoder` with the given configuration. This is the entry point for configurable decoding. ### Signature ```go func NewDecoder(config *DecoderConfig) (*Decoder, error) ``` ### Parameters #### Request Body - **config** (`*DecoderConfig`) - Required - Configuration struct for decoder behavior. The `Result` field must be a pointer to the target struct or map. ### Response #### Success Response - **Decoder** (`*Decoder`) - A new decoder instance ready to decode input - **error** (`error`) - Non-nil if validation fails ### Errors - Returns error if `config.Result` is not a pointer - Returns error if `config.Result` pointer is not addressable - Initializes `Metadata` fields (Keys, Unused, Unset) if provided ### Example ```go config := &mapstructure.DecoderConfig{ Result: &targetStruct, TagName: "mapstructure", Metadata: &mapstructure.Metadata{}, } decoder, err := mapstructure.NewDecoder(config) if err != nil { log.Fatalf("Failed to create decoder: %v", err) } input := map[string]interface{}{ "name": "John", "age": 30, } err = decoder.Decode(input) if err != nil { log.Fatalf("Failed to decode: %v", err) } ``` ``` -------------------------------- ### Type-Flexible Parsing Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/configuration.md Enable type-flexible parsing by setting WeaklyTypedInput to true and applying decode hooks, such as converting string inputs to time.Duration. ```go config := &mapstructure.DecoderConfig{ Result: &target, WeaklyTypedInput: true, DecodeHook: mapstructure.StringToTimeDurationHookFunc(), } ``` -------------------------------- ### Decode Input Data Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-decoder.md Use this method to decode raw input data into a target structure defined in the decoder's configuration. Ensure the decoder is initialized with a valid DecoderConfig, including the Result field pointing to the target variable. Errors during decoding will be returned. ```go type Config struct { Database string `mapstructure:"database"` Port int `mapstructure:"port"` Debug bool `mapstructure:"debug"` } var cfg Config decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &cfg, }) input := map[string]interface{}{ "database": "postgres", "port": 5432, "debug": true, } if err := decoder.Decode(input); err != nil { log.Printf("Decode error: %v", err) } fmt.Printf("Config: %+v\n", cfg) ``` -------------------------------- ### Enable Error Checking for Unknown Keys Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/errors.md Configure the decoder with `ErrorUnused: true` to cause a decoding error if any keys in the input data are not present in the target struct. This is useful for strict configuration validation. ```Go decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &cfg, ErrorUnused: true, // Fail on unknown keys }) if err := decoder.Decode(input); err != nil { // Handle configuration validation errors log.Fatalf("Invalid configuration: %v", err) } ``` -------------------------------- ### Enabling WeaklyTypedInput for Automatic Conversions Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/configuration.md Set WeaklyTypedInput to true to allow automatic type conversions between input and target types, useful for flexible configuration parsing. ```go type ServerConfig struct { Port int `mapstructure:"port"` Enabled bool `mapstructure:"enabled"` Endpoints []string `mapstructure:"endpoints"` } input := map[string]interface{}{ "port": "8080", // string → int "enabled": 1, // int → bool "endpoints": "api,web,admin", // string → []string (with StringToSliceHookFunc) } var cfg ServerConfig decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &cfg, WeaklyTypedInput: true, }) decoder.Decode(input) // cfg.Port == 8080 // cfg.Enabled == true ``` -------------------------------- ### Metadata Tracking During Decoding Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/configuration.md Utilize the Metadata field in DecoderConfig to capture information about the decoding process, including successfully decoded keys, unused input keys, and unset struct fields. ```go type Config struct { Port int `mapstructure:"port"` Host string `mapstructure:"host"` } input := map[string]interface{}{ "port": 8080, "debug": true, } var cfg Config meta := &mapstructure.Metadata{} decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &cfg, Metadata: meta, }) decoder.Decode(input) log.Printf("Keys: %v", meta.Keys) // [port] log.Printf("Unused: %v", meta.Unused) // [debug] log.Printf("Unset: %v", meta.Unset) // [host] ``` -------------------------------- ### Parse Environment Variables with mapstructure Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/examples.md Decode configuration from environment variables into a Go struct. Use WeakDecode when environment variables are all strings to allow type coercion. ```go package main import ( "fmt" "log" "os" "github.com/mitchellh/mapstructure" ) type AppConfig struct { Port int `mapstructure:"APP_PORT"` LogLevel string `mapstructure:"LOG_LEVEL"` EnableDebug bool `mapstructure:"DEBUG"` MaxWorkers int `mapstructure:"MAX_WORKERS"` CacheTTL int `mapstructure:"CACHE_TTL"` } func main() { // Set some environment variables for demo os.Setenv("APP_PORT", "9000") os.Setenv("LOG_LEVEL", "info") os.Setenv("DEBUG", "1") os.Setenv("MAX_WORKERS", "10") os.Setenv("CACHE_TTL", "3600") // Read from environment envData := map[string]interface{}{ "APP_PORT": os.Getenv("APP_PORT"), "LOG_LEVEL": os.Getenv("LOG_LEVEL"), "DEBUG": os.Getenv("DEBUG"), "MAX_WORKERS": os.Getenv("MAX_WORKERS"), "CACHE_TTL": os.Getenv("CACHE_TTL"), } var cfg AppConfig // Use WeakDecode because environment variables are strings err := mapstructure.WeakDecode(envData, &cfg) if err != nil { log.Fatalf("Failed to decode configuration: %v", err) } fmt.Printf("App Configuration:\n") fmt.Printf(" Port: %d\n", cfg.Port) fmt.Printf(" Log Level: %s\n", cfg.LogLevel) fmt.Printf(" Debug: %v\n", cfg.EnableDebug) fmt.Printf(" Workers: %d\n", cfg.MaxWorkers) fmt.Printf(" Cache TTL: %d seconds\n", cfg.CacheTTL) } ``` -------------------------------- ### Decoding with mapstructure and Handling Custom Errors Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/errors.md Demonstrates decoding a map into a struct using mapstructure. It shows how to check for the custom Error type, iterate through aggregated error messages, and access wrapped errors. ```go type Database struct { Host string `mapstructure:"host"` Port int `mapstructure:"port"` } input := map[string]interface{}{ "host": 123, // type mismatch "port": "invalid", // type mismatch } var db Database err := mapstructure.Decode(input, &db) if err != nil { if merr, ok := err.(*mapstructure.Error); ok { fmt.Printf("Got %d errors during decode:\n", len(merr.Errors)) for _, e := range merr.Errors { fmt.Printf(" - %s\n", e) } wrapped := merr.WrappedErrors() for _, we := range wrapped { log.Println(we) } } } ``` -------------------------------- ### Handle Mapstructure Errors Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/quick-reference.md Demonstrates how to specifically catch and handle mapstructure.Error types when decoding, differentiating them from other potential errors. ```go if err := decoder.Decode(input); err != nil { if merr, ok := err.(*mapstructure.Error); ok { // Handle mapstructure errors for _, e := range merr.Errors { log.Printf("Error: %s", e) } } else { // Handle other errors (config validation, etc.) log.Fatalf("Unexpected error: %v", err) } } ``` -------------------------------- ### Apply Custom Transformations with Hooks Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/README.md Use DecodeHooks to transform data during the decoding process. Pre-built hooks like StringToTimeDurationHookFunc are available, or you can define custom transformation logic. ```go hook := mapstructure.StringToTimeDurationHookFunc() // Pre-built hook // OR create custom hook myHook := func(from, to reflect.Type, data interface{}) (interface{}, error) { // Custom logic return transformed, nil } decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &config, DecodeHook: hook, // or myHook }) ``` -------------------------------- ### Decoder Methods Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/INDEX.md Methods available on a `Decoder` instance. ```APIDOC ## Decoder.Decode ### Description Decodes input data using the configuration of the `Decoder` instance. ### Signature `Decoder.Decode(input) error` ### See Also [api-reference-decoder.md#decoderdecode](api-reference-decoder.md#decoderdecode) ``` -------------------------------- ### Zero-Field Initialization in Go Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/usage-patterns.md Use `ZeroFields: true` in `DecoderConfig` to clear all fields in the destination struct before decoding. This ensures a complete state replacement, preventing contamination from previous values and avoiding data accumulation. ```go type State struct { Config map[string]interface{} `mapstructure:"config"` Values []int `mapstructure:"values"` } // Initial state with some values state := State{ Config: map[string]interface{}{ "old_key": "old_value", }, Values: []int{1, 2, 3}, } input := map[string]interface{}{ "config": map[string]interface{}{ "new_key": "new_value", }, "values": []int{4, 5}, } decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &state, ZeroFields: true, // Clear before decoding }) decoder.Decode(input) // state.Config contains only {"new_key": "new_value"} // state.Values contains only {4, 5} ``` -------------------------------- ### Composing Decode Hooks Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-decode-hooks.md Combines string to int conversion and positive integer validation hooks. Use this when you need to apply multiple decoding and validation steps sequentially. ```go stringToInt := func(f, t reflect.Kind, data interface{}) (interface{}, error) { if f == reflect.String && t == reflect.Int { return strconv.Atoi(data.(string)) } return data, nil } validatePositive := func(f, t reflect.Kind, data interface{}) (interface{}, error) { if f == reflect.Int && data.(int) < 0 { return nil, errors.New("must be positive") } return data, nil } composed := mapstructure.ComposeDecodeHookFunc(stringToInt, validatePositive) config := &mapstructure.DecoderConfig{ Result: &target, DecodeHook: composed, } decoder, _ := mapstructure.NewDecoder(config) ``` -------------------------------- ### Decode Hook Execution Order Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/internal-behavior.md Explains when decode hooks are executed in the decoding process, emphasizing that they run before type conversion and weak typing. This allows hooks to transform values before further processing. ```text 1. Input value received 2. Decode hook applied (if configured) 3. Hook output becomes new input 4. Type checking and conversion happens on hook output 5. Final value assigned ``` -------------------------------- ### Parse Configuration from JSON/YAML Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/README.md Decode a map[string]interface{} (typically from JSON or YAML) into a Go struct. This is a fundamental use case for configuration management. ```go // JSON/YAML parsed into map[string]interface{} input := map[string]interface{}{} var config MyConfig mapstructure.Decode(input, &config) ``` -------------------------------- ### Decode with Metadata and Validation (Go) Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/examples.md Use DecodeMetadata to track used and unused keys, and unset fields during decoding. This is useful for performing custom validation on the decoded data. ```go package main import ( "fmt" "log" "github.com/mitchellh/mapstructure" ) type UserInput struct { Name string `mapstructure:"name"` Email string `mapstructure:"email"` Age int `mapstructure:"age"` Country string `mapstructure:"country"` } func main() { input := map[string]interface{}{ "name": "Alice Johnson", "email": "alice@example.com", // age not provided "country": "USA", "phone": "+1-555-1234", // Unknown field } var user UserInput meta := &mapstructure.Metadata{} err := mapstructure.DecodeMetadata(input, &user, meta) if err != nil { log.Fatalf("Decode failed: %v", err) } fmt.Printf("Decoded User: %+v\n", user) fmt.Printf("Keys used: %v\n", meta.Keys) fmt.Printf("Unused input keys: %v\n", meta.Unused) fmt.Printf("Unset struct fields: %v\n", meta.Unset) // Custom validation if len(meta.Unset) > 0 { log.Printf("Warning: Some fields were not provided: %v", meta.Unset) } if len(meta.Unused) > 0 { log.Printf("Warning: Unknown configuration keys were ignored: %v", meta.Unused) } } ``` -------------------------------- ### Basic mapstructure Decode Usage Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/quick-reference.md Demonstrates the basic usage of mapstructure.Decode to convert a map into a struct. Ensure the target is a pointer to the struct. ```go import "github.com/mitchellh/mapstructure" type MyStruct struct { Field1 string `mapstructure:"field_1"` Field2 int `mapstructure:"field_2"` } func main() { input := map[string]interface{}{ "field_1": "value", "field_2": 42, } var result MyStruct err := mapstructure.Decode(input, &result) if err != nil { panic(err) } } ``` -------------------------------- ### Convert String to net.IP using StringToIPHookFunc Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-decode-hooks.md Use StringToIPHookFunc to convert string representations of IP addresses into net.IP types. This hook is useful when your configuration data contains IP addresses as strings and you need them as native IP types in your Go structs. It handles both IPv4 and IPv6 formats and returns an error for invalid input. ```go package main import ( "fmt" "net" "github.com/mitchellh/mapstructure" ) type Server struct { Host net.IP `mapstructure:"host"` Port int `mapstructure:"port"` } func main() { input := map[string]interface{}{ "host": "192.168.1.1", "port": 8080, } var srv Server decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &srv, DecodeHook: mapstructure.StringToIPHookFunc(), }) decoder.Decode(input) fmt.Println(srv.Host.String()) // Output: 192.168.1.1 } ``` -------------------------------- ### Convert String to net.IPNet using StringToIPNetHookFunc Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-decode-hooks.md Employ StringToIPNetHookFunc to parse CIDR notation strings into net.IPNet types. This hook is ideal for configurations where network subnets are provided as strings. It supports both IPv4 and IPv6 CIDR formats and validates the input string. ```go package main import ( "fmt" "net" "github.com/mitchellh/mapstructure" ) type NetworkConfig struct { Subnet net.IPNet `mapstructure:"subnet"` } func main() { input := map[string]interface{}{ "subnet": "10.0.0.0/8", } var cfg NetworkConfig decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &cfg, DecodeHook: mapstructure.StringToIPNetHookFunc(), }) decoder.Decode(input) fmt.Println(cfg.Subnet.String()) // Output: 10.0.0.0/8 } ``` -------------------------------- ### Custom Case-Sensitive Matching Function Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/struct-tags.md To enforce case-sensitive matching, provide a custom function to `DecoderConfig.MatchName` that checks for exact string equality. ```go // Case-sensitive matching config := &mapstructure.DecoderConfig{ Result: &target, MatchName: strings.EqualFold, // Already default // Or for case-sensitive: MatchName: func(mapKey, fieldName string) bool { return mapKey == fieldName }, } ``` -------------------------------- ### WeakDecode Data with Weak Type Conversions Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-functions.md Performs decoding with weak type conversions enabled, allowing for more flexible data type handling. This is useful when input data types may not exactly match target types. ```go package main import ( "fmt" "log" "github.com/mitchellh/mapstructure" ) type Config struct { Port int `mapstructure:"port" Enabled bool `mapstructure:"enabled" Timeout string `mapstructure:"timeout" } func main() { input := map[string]interface{}{ "port": "8080", // string → int "enabled": 1, // int → bool "timeout": 30, // int → string } var cfg Config if err := mapstructure.WeakDecode(input, &cfg); err != nil { log.Fatalf("WeakDecode failed: %v", err) } // cfg.Port == 8080 (string parsed as int) // cfg.Enabled == true (1 converted to bool) // cfg.Timeout == "30" (int converted to string) fmt.Printf("Config: %+v\n", cfg) } ``` -------------------------------- ### Enforce Strict Validation with DecoderConfig Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/README.md Configure the decoder to reject unknown keys (ErrorUnused) or require all struct fields to be present in the input (ErrorUnset). This ensures data integrity and completeness. ```go decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &config, ErrorUnused: true, // Reject unknown keys ErrorUnset: true, // Require all fields }) ``` -------------------------------- ### Weak Mode Array Conversions Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/internal-behavior.md Describes special array conversion rules when `WeaklyTypedInput` is enabled, such as converting an empty map to an empty array, a scalar value to a single-element array, or a string to a char array for `[N]uint8` targets. ```go With `WeaklyTypedInput`: - Empty map → empty array - Scalar value → array with single element - String → char array (if target is `[N]uint8`) ``` -------------------------------- ### DecoderConfig Source: https://github.com/mitchellh/mapstructure/blob/main/_autodocs/api-reference-decoder.md Complete configuration structure for decoder behavior, including options for hooks, error handling, and tag names. ```APIDOC ## DecoderConfig ### Description Complete configuration structure for decoder behavior. ### Type Definition ```go type DecoderConfig struct { DecodeHook DecodeHookFunc ErrorUnused bool ErrorUnset bool ZeroFields bool WeaklyTypedInput bool Squash bool Metadata *Metadata Result interface{} TagName string IgnoreUntaggedFields bool MatchName func(mapKey, fieldName string) bool } ``` ### Fields - **`DecodeHook`** (`DecodeHookFunc`) - Optional hook function called before decoding for data transformation. Supports three types: `DecodeHookFuncType`, `DecodeHookFuncKind`, or `DecodeHookFuncValue`. - **`ErrorUnused`** (`bool`) - If true, an error is returned if the input map contains keys not present in the target struct. - **`ErrorUnset`** (`bool`) - If true, an error is returned if the target struct has fields that were not set during decoding. - **`ZeroFields`** (`bool`) - If true, all fields are zero-initialized before decoding (e.g., maps are cleared). If false, maps are merged with existing values. - **`WeaklyTypedInput`** (`bool`) - If true, enables weak type conversions (e.g., string "1" to bool true, int 1 to string "1"). - **`Squash`** (`bool`) - If true, all embedded structs are squashed by default. Individual fields can override with tags. - **`Metadata`** (`*Metadata`) - If non-nil, populated with decode metadata: keys used, unused keys, unset fields. - **`Result`** (`interface{}`) - Required. Pointer to target struct or map to decode into. - **`TagName`** (`string`) - Struct tag name to look for when mapping input keys to struct fields. Default is `"mapstructure"`. - **`IgnoreUntaggedFields`** (`bool`) - If true, struct fields without explicit `TagName` tags are ignored (treated as if tagged with `-`). - **`MatchName`** (`func(mapKey, fieldName string) bool`) - Function used to match map keys to struct field names. Default is case-insensitive. ### Example ```go config := &mapstructure.DecoderConfig{ Result: &myStruct, TagName: "json", // Use "json" tags instead of "mapstructure" WeaklyTypedInput: true, // Allow weak type conversions ErrorUnused: true, // Fail on unused keys Metadata: &mapstructure.Metadata{}, // Track decode info } ``` ```