struct2

package module
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Mar 12, 2024 License: MIT Imports: 7 Imported by: 8

README

struct2

License Coverage GitHub Workflow Status Go Report Card Go PKG

This repository helps to work with struct, convert map and get information about that.

This is a modified version of common struct to map libraries with cool features.

Usage

go get github.com/worldline-go/struct2
Map

Get decoder and run Map method, default looking the struct tag in struct.

Supported tags: -, omitempty, string, ptr2, omitnested, flatten, remain.

Convertion order is -, omitempty, string, ptr2, custom hook function, hooker interface, omitnested + flatten

type ColorGroup struct {
    ID     int      `db:"id"`
    Name   string   `db:"name"`
    Colors []string `db:"colors"`
    // custom type with implemented Hooker interface
    // covertion result to time.Time
    Date types.Time `db:"time"`
	// RGB unknown type but to untouch it add omitnested to keep that struct type
	RGB *rgb `db:"rgb,omitempty,omitnested"`
}

//...

// default tagName is `struct`
decoder := struct2.Decoder{
    TagName: "db",
}

// get map[string]interface{}
result := decoder.Map(group)

// or use one line
// result := (&struct2.Decoder{}).SetTagName("db").Map(group) // default tag name is "struct"

Custom decoder can be use in struct which have struct2.Hooker interface.
Or set a slice of custom struct2.HookFunc functions in decoder.

Check documentation examples.

Tags Information

omitnested: very helpful to don't want to touch data.

ptr2: convert pointer to the concrete value. If pointer is nil, new empty value is generated. ptr2 to effect custom hook functions and hooker interface also omitnested.

remain: Must be defined as map[string]interface{} in struct. Puts all unknown fields, destined for the struct into the remain field.

Decode

Decode is working almostly same as the mitchellh/mapstructure repo.

Default tag is struct in struct.

decoder := struct2.Decoder{
    WeaklyTypedInput: true,
    WeaklyIgnoreSeperator: true,
    TagName: "struct",
    BackupTagName: "json",
}

//...

// input and output could be any, output should be pointer
if err := d.Decode(input, output); err != nil {
    return err
}

Inspired Projects

When starting this project, I want to make it from scratch and I can learn more and make some new features but codes turning to fatih/structs repo and they are solved lots of problems so I copied parts in there and add some features as hook functions. After that I want to extend that one to make map to struct operations. In that time, I see I need to check all types due to mixing types together. So I copied parts in mitchellh/mapstructure. Thanks for all people to make them.

fatih/structs
mitchellh/mapstructure

Documentation

Overview

Example
package main

import (
	"fmt"
	"sort"
	"time"

	"github.com/worldline-go/struct2"
	"github.com/worldline-go/struct2/types"
)

func SortPrint(m map[string]interface{}) {
	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	for _, k := range keys {
		fmt.Printf("Type: %T, Value: %v\n", m[k], m[k])
	}
}

func main() {
	type ColorGroup struct {
		ID     int        `json:"id"`
		Name   string     `json:"name"`
		Colors []string   `json:"colors"`
		Date   types.Time `json:"time"`
	}

	d, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")

	group := ColorGroup{
		ID:     1,
		Name:   "Reds",
		Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
		Date:   types.Time{Time: d},
	}

	result := (&struct2.Decoder{}).SetTagName("json").Map(group) // default tag name is "struct"

	// fmt.Printf("%#v", result)
	SortPrint(result)
}
Output:

Type: []string, Value: [Crimson Red Ruby Maroon]
Type: int, Value: 1
Type: string, Value: Reds
Type: time.Time, Value: 2006-01-02 15:04:05 +0000 UTC
Example (CustomHook)
package main

import (
	"fmt"
	"reflect"

	"github.com/worldline-go/struct2"
)

func main() {
	type ColorGroup struct {
		Name  string `db:"name"`
		Count int    `db:"count"`
	}

	group := ColorGroup{
		Name: "DeepCore",
	}

	decoder := struct2.Decoder{
		TagName: "db",
		Hooks: []struct2.HookFunc{func(v reflect.Value) (interface{}, error) {
			if v.Kind() == reflect.String {
				return "str_" + v.Interface().(string), nil
			}

			return nil, struct2.ErrContinueHook
		}},
	}

	result := decoder.Map(group)

	fmt.Printf("%v", result["name"])
}
Output:

str_DeepCore

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrContinueHook = errors.New("continue to decode")

ErrContinueHook usable with HookFunc. This error type not checking by decode. Replacable by any error to continue to decode.

Functions

func Ptr2Concrete

func Ptr2Concrete(val interface{}) interface{}

Types

type Decoder

type Decoder struct {
	// Tagname to lookup struct's field tag, default is `struct`
	TagName string
	// Hooks function run before decode and enable to change of data.
	Hooks []HookFunc

	// HooksDecode to modify data before decode.
	HooksDecode []HookDecodeFunc

	// WeaklyTypedInput is true, the decoder will make the following
	// "weak" conversions:
	//
	//   - bools to string (true = "1", false = "0")
	//   - numbers to string (base 10)
	//   - bools to int/uint (true = 1, false = 0)
	//   - strings to int/uint (base implied by prefix)
	//   - int to bool (true if value != 0)
	//   - string to bool (accepts: 1, t, T, TRUE, true, True, 0, f, F,
	//     FALSE, false, False. Anything else is an error)
	//   - empty array = empty map and vice versa
	//   - negative numbers to overflowed uint values (base 10)
	//   - slice of maps to a merged map
	//   - single values are converted to slices if required. Each
	//     element is weakly decoded. For example: "4" can become []int{4}
	//     if the target type is an int slice.
	//
	WeaklyTypedInput bool

	// ZeroFields, if set to true, will zero fields before writing them.
	// For example, a map will be emptied before decoded values are put in
	// it. If this is false, a map will be merged.
	ZeroFields bool

	// Squash will squash embedded structs.  A squash tag may also be
	// added to an individual struct field using a tag.  For example:
	//
	//  type Parent struct {
	//      Child `struct:",squash"`
	//  }
	Squash bool
	// IgnoreUntaggedFields ignores all struct fields without explicit
	// TagName, comparable to `struct:"-"` as default behaviour.
	IgnoreUntaggedFields bool

	// BackupTagName usable if TagName not found.
	BackupTagName string

	// WeaklyDashUnderscore apply underscore/dash conversion to variables
	// on map to struct. variable_name == variable-name.
	WeaklyDashUnderscore bool

	// WeaklyIgnoreSeperator ignore seperator on map to struct. variable_name == variablename
	// values are -, _ and space.
	WeaklyIgnoreSeperator bool

	// ForcePtr2 assume `struct:",ptr2` on all pointer struct fields.
	ForcePtr2 bool

	// OmitNullPtr omits nil pointers, in the map.
	OmitNilPtr bool

	// OutputCamelCase will convert map keys to camel case.
	OuputCamelCase bool

	// NoRemainFields will fail if there are extra fields in the input, that are not in the output.
	NoRemainFields bool
}

Decoder is main struct of struct2, holds config and functions.

func (*Decoder) Decode added in v1.2.1

func (d *Decoder) Decode(input, output interface{}) error

Decode the interface to another.

func (*Decoder) GetFields

func (d *Decoder) GetFields(s interface{}) []string

func (*Decoder) Map

func (d *Decoder) Map(input interface{}) map[string]interface{}

Map converts given struct to the map[string]interface{}. Panic if input not a struct type.

func (*Decoder) MapOmitNested

func (d *Decoder) MapOmitNested(input interface{}) map[string]interface{}

MapOmitNested converts given struct to the map[string]interface{} without looking nested object. Panic if input not a struct type.

func (*Decoder) SetHooks added in v1.1.0

func (d *Decoder) SetHooks(hooks []HookFunc) *Decoder

func (*Decoder) SetHooksDecode added in v1.2.2

func (d *Decoder) SetHooksDecode(hooksDecode []HookDecodeFunc) *Decoder

func (*Decoder) SetTagName added in v1.1.0

func (d *Decoder) SetTagName(t string) *Decoder

type HookDecodeFunc added in v1.2.2

type HookDecodeFunc func(reflect.Type, reflect.Type, interface{}) (interface{}, error)

HookDecodeFunc get input, output and data and return modified data.

type HookFunc

type HookFunc func(reflect.Value) (interface{}, error)

HookFunc get reflect.Value to modify custom in decoder.

type Hooker

type Hooker interface {
	Struct2Hook() interface{}
}

Hooker interface for structs.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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