mapstructure

package module
v0.0.0-...-5d659c4 Latest Latest
Warning

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

Go to latest
Published: Mar 22, 2026 License: MIT Imports: 8 Imported by: 2

README

mapstructure

A Go library for decoding map[string]any values into Go structs with type conversion and struct tag support.

Why?

When decoding data from JSON, YAML, or other formats, you often get map[string]any as an intermediate representation. This library converts those maps into strongly-typed Go structs with:

  • Automatic type conversion (string → int, float → bool, etc.)
  • Struct tag support for field name mapping
  • Nested struct and embedded field handling
  • Custom type converters

Installation

go get github.com/talav/talav/pkg/component/mapstructure

Quick Start

package main

import (
    "fmt"
    "github.com/talav/talav/pkg/component/mapstructure"
)

type Person struct {
    Name string `schema:"name"`
    Age  int    `schema:"age"`
}

func main() {
    data := map[string]any{
        "name": "Alice",
        "age":  "30", // string automatically converted to int
    }

    var person Person
    if err := mapstructure.Unmarshal(data, &person); err != nil {
        panic(err)
    }

    fmt.Printf("%+v\n", person) // {Name:Alice Age:30}
}

Struct Tags

By default, the schema tag is used for field mapping:

type Config struct {
    ServerHost string `schema:"server_host"`
    ServerPort int    `schema:"port"`
    Debug      bool   `schema:"debug"`
    Ignored    string `schema:"-"` // Skip this field
}
Tag Behavior
schema:"name" Use "name" as the map key
schema:"-" Skip field entirely
No tag Use Go field name

Custom Tag Name

Use a different tag (e.g., json):

cache := mapstructure.NewStructMetadataCache(
    mapstructure.NewTagCacheBuilder("json"),
)
converters := mapstructure.NewDefaultConverterRegistry(nil)
unmarshaler := mapstructure.NewUnmarshaler(cache, converters)

type User struct {
    Name string `json:"name"`
}

var user User
unmarshaler.Unmarshal(data, &user)

Type Conversion

Built-in converters handle common type conversions:

Target Type Accepted Input Types
string string, bool, int, uint, float, []byte
bool bool, int, uint, float, string ("true", "false", "1", "0")
int, int8...int64 int, uint, float, bool, string
uint, uint8...uint64 int, uint, float, bool, string
float32, float64 int, uint, float, bool, string
[]byte []byte, string, []any, io.Reader
io.ReadCloser io.ReadCloser, io.Reader, []byte, string

Custom Converters

Register converters for custom types:

import (
    "reflect"
    "time"
)

timeConverter := func(value any) (reflect.Value, error) {
    s, ok := value.(string)
    if !ok {
        return reflect.Value{}, fmt.Errorf("expected string")
    }
    t, err := time.Parse(time.RFC3339, s)
    if err != nil {
        return reflect.Value{}, err
    }
    return reflect.ValueOf(t), nil
}

converters := mapstructure.NewDefaultConverterRegistry(map[reflect.Type]mapstructure.Converter{
    reflect.TypeOf(time.Time{}): timeConverter,
})

cache := mapstructure.NewStructMetadataCache(nil)
unmarshaler := mapstructure.NewUnmarshaler(cache, converters)

Nested Structs

Nested structs are handled automatically:

type Address struct {
    City    string `schema:"city"`
    Country string `schema:"country"`
}

type Person struct {
    Name    string  `schema:"name"`
    Address Address `schema:"address"`
}

data := map[string]any{
    "name": "Alice",
    "address": map[string]any{
        "city":    "New York",
        "country": "USA",
    },
}

var person Person
mapstructure.Unmarshal(data, &person)

Embedded Structs

Embedded structs support both promoted and named field access:

type Timestamps struct {
    CreatedAt string `schema:"created_at"`
    UpdatedAt string `schema:"updated_at"`
}

type User struct {
    Timestamps        // Embedded - fields promoted to parent
    Name       string `schema:"name"`
}

// Both work:
data1 := map[string]any{
    "name":       "Alice",
    "created_at": "2024-01-01", // Promoted field
    "updated_at": "2024-01-02",
}

data2 := map[string]any{
    "name": "Alice",
    "Timestamps": map[string]any{ // Named access
        "created_at": "2024-01-01",
        "updated_at": "2024-01-02",
    },
}

Pointers and Slices

type Config struct {
    Tags    []string `schema:"tags"`
    Count   *int     `schema:"count"`
    Data    []byte   `schema:"data"`
}

data := map[string]any{
    "tags":  []any{"go", "api"},
    "count": 42,
    "data":  []any{72, 101, 108, 108, 111}, // Converts to []byte("Hello")
}

API Reference

Functions
  • Unmarshal(data map[string]any, result any) error - Simple API using defaults
Types
  • Unmarshaler - Configurable unmarshaler instance
  • ConverterRegistry - Type converter registry
  • StructMetadataCache - Cached struct field metadata
  • Converter - Function type: func(any) (reflect.Value, error)
Constructors
  • NewUnmarshaler(cache, converters) - Create custom unmarshaler
  • NewStructMetadataCache(builder) - Create metadata cache (nil = default "schema" tag)
  • NewTagCacheBuilder(tagName) - Create cache builder for specific tag
  • NewDefaultConverterRegistry(additional) - Create registry with standard converters
  • NewConverterRegistry(converters) - Create registry with only specified converters

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultCacheBuilder = NewTagCacheBuilder("schema")

DefaultCacheBuilder builds struct metadata using "schema" tags.

Functions

func Unmarshal

func Unmarshal(data map[string]any, result any) error

Unmarshal transforms map[string]any into a Go struct pointed to by result. result must be a pointer to the target type. This is a convenience function that uses a shared default unmarshaler.

Types

type CacheBuilderFunc

type CacheBuilderFunc func(typ reflect.Type) (*StructMetadata, error)

CacheBuilderFunc builds struct metadata for caching.

func NewTagCacheBuilder

func NewTagCacheBuilder(tagName string) CacheBuilderFunc

NewTagCacheBuilder creates a cache builder that parses struct tags. tagName specifies which tag to read (e.g., "schema", "json", "mapstructure").

type Converter

type Converter func(value any) (reflect.Value, error)

Converter converts a value to a reflect.Value of a specific type.

type ConverterRegistry

type ConverterRegistry struct {
	// contains filtered or unexported fields
}

ConverterRegistry manages type converters. Immutable after construction, safe for concurrent reads.

func NewConverterRegistry

func NewConverterRegistry(converters map[reflect.Type]Converter) *ConverterRegistry

NewConverterRegistry creates a registry with the given converters. If converters is nil, an empty registry is created.

func NewDefaultConverterRegistry

func NewDefaultConverterRegistry(additional map[reflect.Type]Converter) *ConverterRegistry

NewDefaultConverterRegistry creates a registry with standard type converters. Additional converters can be provided to extend or override defaults.

func (*ConverterRegistry) Find

func (r *ConverterRegistry) Find(typ reflect.Type) (Converter, bool)

Find finds a converter for the given type. Lock-free read, safe for concurrent use.

type FieldMetadata

type FieldMetadata struct {
	StructFieldName string       // Go field name
	MapKey          string       // Key to lookup in map
	Index           int          // Field index for reflection
	Type            reflect.Type // Field type
	Embedded        bool         // Anonymous/embedded struct
	Default         *string      // Raw default value from `default` tag, nil if no tag
}

FieldMetadata holds cached struct field information.

type StructMetadata

type StructMetadata struct {
	Fields []FieldMetadata
}

StructMetadata holds cached metadata for a struct type.

type StructMetadataCache

type StructMetadataCache struct {
	// contains filtered or unexported fields
}

StructMetadataCache provides caching for struct field metadata.

func NewStructMetadataCache

func NewStructMetadataCache(builder CacheBuilderFunc) *StructMetadataCache

NewStructMetadataCache creates a new struct metadata cache. If builder is nil, DefaultCacheBuilder is used.

type Unmarshaler

type Unmarshaler struct {
	// contains filtered or unexported fields
}

Unmarshaler handles unmarshaling of maps to Go structs.

func NewDefaultUnmarshaler

func NewDefaultUnmarshaler() *Unmarshaler

NewDefaultUnmarshaler creates a new unmarshaler with default settings. Uses DefaultCacheBuilder (field names) and no custom converters.

func NewUnmarshaler

func NewUnmarshaler(fieldCache *StructMetadataCache, converters *ConverterRegistry) *Unmarshaler

NewUnmarshaler creates a new unmarshaler with explicit dependencies. For custom configurations, construct the cache and converters separately:

// With custom tag
cache := NewStructMetadataCache(NewTagCacheBuilder("schema"))
converters := NewDefaultConverterRegistry(nil)
u := NewUnmarshaler(cache, converters)

// With custom converters
cache := NewStructMetadataCache(DefaultCacheBuilder)
converters := NewDefaultConverterRegistry(customConverters)
u := NewUnmarshaler(cache, converters)

func (*Unmarshaler) Unmarshal

func (u *Unmarshaler) Unmarshal(data map[string]any, result any) error

Unmarshal transforms map[string]any into a Go struct pointed to by result. result must be a pointer to the target type.

Jump to

Keyboard shortcuts

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