conv

package module
v0.6.1 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2023 License: MIT Imports: 9 Imported by: 4

README

conv - A Go package for type conversion

GoDoc Go codecov License GoVersion Go Report Card

Features:

  • Supports conversion between numbers, string, time.
  • Supports converting from slice to slice.
  • Supports converting between struct and map[string]interface{}.
  • Overflow checking when converting integers.
  • Match field names case-insensitively, or in the camel-case manner.
  • Case-insensitive JSON unmarshalling using a map as the middleware.
  • Deep-clone.
  • Support pointers.
  • No third party dependency. All features are implemented using the standard library.

Some features are still under development, the APIs may change in the future.

Quick start

Installation:

go get -u github.com/cmstar/go-conv@latest

Simple usage, the code below comes from the example_test.go:

// There is a group of shortcut functions for converting from/to simple types.
// All conversion functions returns the converted value and an error.
fmt.Println(conv.Int("123"))         // -> 123
fmt.Println(conv.String(3.14))       // -> "3.14"
fmt.Println(conv.Float64("invalid")) // -> get an error

// When converting integers, overflow-checking is applied.
fmt.Println(conv.Int8(1000)) // -> overflow

// A zero value of number is converted to false; a non-zero value is converted to true.
fmt.Println(conv.Bool(float32(0.0))) // -> false
fmt.Println(conv.Bool(float64(0.1))) // -> true
fmt.Println(conv.Bool(-1))           // -> true

// strconv.ParseBool() is used for string-to-bool conversion.
fmt.Println(conv.Bool("true"))  // -> true
fmt.Println(conv.Bool("false")) // -> false

// Numbers can be converted time.Time, they're treated as UNIX timestamp.
// For more information, see the example in go-doc.
t, err := conv.Time(3600) // An hour later after 1970-01-01T00:00:00Z.
// By default time.Time is converted to string with the RFC3339 format.
fmt.Println(conv.String(t.UTC())) // -> 1970-01-01T01:00:00Z

// ConvertType() is the core function in the package. In fact all conversion can be done via
// this function. For complex types, there is no shortcut, we can use ConvertType() directly.
// It receives the source value and the destination type.

// Convert from a slice to another. ConvertType() is applied to each element.
sliceOfInt, err := conv.ConvertType([]string{"1", "2", "3"}, reflect.TypeOf([]int{}))
fmt.Println(sliceOfInt, err) // -> []int{1, 2, 3}

// Convert from a map[string]interface{} to a struct. ConvertType() is applied to each field.
user := DemoUser{Name: "Bob", MailAddr: "bob@example.org", Age: 51}
out, err := conv.ConvertType(user, reflect.TypeOf(map[string]interface{}{}))
fmt.Println(out, err) // -> map[string]interface{}{"Age":51, "MailAddr":"bob@example.org", "Name":"Bob", "IsVip":false}

// From map to struct.
m := map[string]interface{}{"Name": "Alice", "Age": "27", "IsVip": 1}
out, err = conv.ConvertType(m, reflect.TypeOf(user))
fmt.Printf("%+v\n", out) // -> DemoUser{Name: "Alice", MailAddr: "", Age: 27, IsVip:true}

// Deep-clone a struct.
out, err = conv.ConvertType(user, reflect.TypeOf(user))
fmt.Printf("%+v\n", out) // -> DemoUser{Name: "Bob", MailAddr: "bob@example.org", Age: 51}

// Convert() is similar to ConvertType(), but receive a pointer instead of a type.
// It's more like some functions in the standard library such as json.Unmarshal().
clone := DemoUser{}
conv.Convert(user, &clone)
fmt.Printf("%+v\n", clone) // -> DemoUser{Name: "Bob", MailAddr: "bob@example.org", Age: 51}

Output:

// 123 <nil>
// 3.14 <nil>
// 0 strconv.ParseFloat: parsing "invalid": invalid syntax
// 0 value overflow when converting 1000 (int) to int8
// false <nil>
// true <nil>
// true <nil>
// true <nil>
// false <nil>
// 1970-01-01T01:00:00Z <nil>
// [1 2 3] <nil>
// map[Age:51 IsVip:false MailAddr:bob@example.org Name:Bob] <nil>
// {Name:Alice MailAddr: Age:27 IsVip:true}
// {Name:Bob MailAddr:bob@example.org Age:51 IsVip:false}
// {Name:Bob MailAddr:bob@example.org Age:51 IsVip:false}

Deep into the Conv instance:

// The Conv struct providers the underlying features for all shortcuts functions.
// You can use it directly. A zero value has the default behavior.
c := new(conv.Conv)

// It has a field named Conf which is used to customize the conversion.
// By default, we get an error when converting to a slice from a string that is a group of
// elements separated by some characters.
fmt.Println(c.ConvertType("1,2,3", reflect.TypeOf([]int{}))) // -> error

// Conf.StringSplitter is a function that defines how to convert from a string to a slice.
c.Conf.StringSplitter = func(v string) []string { return strings.Split(v, ",") }
// After configure, the conversion should be OK.
fmt.Println(c.ConvertType("1,2,3", reflect.TypeOf([]int{}))) // -> []int{1, 2, 3}

// Conf.FieldMatcherCreator define how to match names from a struct when converting from
// a map or another struct.
// Here we demonstrate how to make snake-case names match the field names automatically,
// using the build-in FieldMatcherCreator named SimpleMatcherCreator.
c.Conf.FieldMatcherCreator = conv.SimpleMatcherCreator{
	Conf: conv.SimpleMatcherConfig{
		CamelSnakeCase: true,
	},
}
// When then CamelSnakeCase option is true, 'mailAddr' can match the field MailAddr, 'is_vip' can match IsVip.
m := map[string]interface{}{"name": "Bob", "age": "51", "mailAddr": "bob@example.org", "is_vip": "true"}
user := DemoUser{}
_ = c.Convert(m, &user)
fmt.Printf("%+v\n", user) // -> DemoUser{Name: "Bob", MailAddr: "bob@example.org", Age: 51, IsVip: true})

// The json package of the standard library does not support matching fields in case-insensitive manner.
// We have to use field tag to specify the name of JSON properties.
// With FieldMatcherCreator, we can unmarshal JSON in case-insensitive manner, using a map as a middleware.
// Thou the performance is not good, but it works :) .
middleware := make(map[string]interface{})
_ = json.Unmarshal([]byte(`{"name":"Alice", "mailAddr":"alice@example.org", "isVip": true, "age":27}`), &middleware)
_ = c.Convert(middleware, &user)
fmt.Printf("%+v\n", user) // -> DemoUser{Name: "Alice", MailAddr: "alice@example.org", Age: 27, IsVip: true})

Output:

// <nil> conv.ConvertType: conv.StringToSlice: cannot convert to []int, at index 0: conv.SimpleToSimple: strconv.ParseInt: parsing "1,2,3": invalid syntax
// [1 2 3] <nil>
// {Name:Bob MailAddr:bob@example.org Age:51 IsVip:true}
// {Name:Alice MailAddr:alice@example.org Age:27 IsVip:true}

Performance

Not good. The code use reflect heavily, be aware if you are care for the performance.

Known issues

  • The field tags are not processed when converting from struct to map or to other struct.

Documentation

Overview

Package conv provides a group of functions to convert between primitive types, maps, slices and structs.

Example
// There is a group of shortcut functions for converting from/to simple types.
// All conversion functions returns the converted value and an error.
fmt.Println(conv.Int("123"))         // -> 123
fmt.Println(conv.String(3.14))       // -> "3.14"
fmt.Println(conv.Float64("invalid")) // -> get an error

// When converting integers, overflow-checking is applied.
fmt.Println(conv.Int8(1000)) // -> overflow

// A zero value of number is converted to false; a non-zero value is converted to true.
fmt.Println(conv.Bool(float32(0.0))) // -> false
fmt.Println(conv.Bool(float64(0.1))) // -> true
fmt.Println(conv.Bool(-1))           // -> true

// strconv.ParseBool() is used for string-to-bool conversion.
fmt.Println(conv.Bool("true"))  // -> true
fmt.Println(conv.Bool("false")) // -> false

// Numbers can be converted time.Time, they're treated as UNIX timestamp.
// NOTE: The location of the time depends on the input type, see the example of time for more details.
t, err := conv.Time(3600) // An hour later after 1970-01-01T00:00:00Z.
// By default time.Time is converted to string with the RFC3339 format.
fmt.Println(conv.String(t.UTC())) // -> 1970-01-01T01:00:00Z

// ConvertType() is the core function in the package. In fact all conversion can be done via
// this function. For complex types, there is no shortcut, we can use ConvertType() directly.
// It receives the source value and the destination type.

// Convert from a slice to another. ConvertType() is applied to each element.
sliceOfInt, err := conv.ConvertType([]string{"1", "2", "3"}, reflect.TypeOf([]int{}))
fmt.Println(sliceOfInt, err) // -> []int{1, 2, 3}

// Convert from a map[string]interface{} to a struct. ConvertType() is applied to each field.
user := DemoUser{Name: "Bob", MailAddr: "bob@example.org", Age: 51}
out, err := conv.ConvertType(user, reflect.TypeOf(map[string]interface{}{}))
fmt.Println(out, err) // -> map[string]interface{}{"Age":51, "MailAddr":"bob@example.org", "Name":"Bob", "IsVip":false}

// From map to struct.
m := map[string]interface{}{"Name": "Alice", "Age": "27", "IsVip": 1}
out, err = conv.ConvertType(m, reflect.TypeOf(user))
fmt.Printf("%+v\n", out) // -> DemoUser{Name: "Alice", MailAddr: "", Age: 27, IsVip:true}

// Deep-clone a struct.
out, err = conv.ConvertType(user, reflect.TypeOf(user))
fmt.Printf("%+v\n", out) // -> DemoUser{Name: "Bob", MailAddr: "bob@example.org", Age: 51}

// Convert() is similar to ConvertType(), but receive a pointer instead of a type.
// It's more like some functions in the standard library such as json.Unmarshal().
clone := DemoUser{}
conv.Convert(user, &clone)
fmt.Printf("%+v\n", clone) // -> DemoUser{Name: "Bob", MailAddr: "bob@example.org", Age: 51}
Output:

123 <nil>
3.14 <nil>
0 strconv.ParseFloat: parsing "invalid": invalid syntax
0 value overflow when converting 1000 (int) to int8
false <nil>
true <nil>
true <nil>
true <nil>
false <nil>
1970-01-01T01:00:00Z <nil>
[1 2 3] <nil>
map[Age:51 IsVip:false MailAddr:bob@example.org Name:Bob] <nil>
{Name:Alice MailAddr: Age:27 IsVip:true}
{Name:Bob MailAddr:bob@example.org Age:51 IsVip:false}
{Name:Bob MailAddr:bob@example.org Age:51 IsVip:false}
Example (CustomConverters)
// We can use conv.ConvertFunc to convert values to some type of your own.

type Name struct{ FirstName, LastName string }

// Converts a string such as "A B" to a struct Name{A, B}.
nameConverter := func(value interface{}, typ reflect.Type) (result interface{}, err error) {
	if typ != reflect.TypeOf(Name{}) {
		return nil, nil
	}

	s, ok := value.(string)
	if !ok {
		return nil, nil
	}

	parts := strings.Split(s, " ")
	if len(parts) != 2 {
		return nil, errors.New("bad name")
	}

	return Name{parts[0], parts[1]}, nil
}

c := &conv.Conv{
	Conf: conv.Config{
		CustomConverters: []conv.ConvertFunc{nameConverter},
	},
}

var name Name
c.Convert("John Doe", &name)
fmt.Println("FirstName:", name.FirstName)
fmt.Println("LastName:", name.LastName)

// The middle name is not supported so we'll get an error here.
fmt.Println("error:")
_, err := c.ConvertType("John Middle Doe", reflect.TypeOf(Name{}))
fmt.Println(err)
Output:

FirstName: John
LastName: Doe
error:
conv.ConvertType: converter[0]: bad name
Example (TheConvInstance)
// The Conv struct providers the underlying features for all shortcuts functions.
// You can use it directly. A zero value has the default behavior.
c := new(conv.Conv)

// It has a field named Conf which is used to customize the conversion.
// By default, we get an error when converting to a slice from a string that is a group of
// elements separated by some characters.
fmt.Println(c.ConvertType("1,2,3", reflect.TypeOf([]int{}))) // -> error

// Conf.StringSplitter is a function that defines how to convert from a string to a slice.
c.Conf.StringSplitter = func(v string) []string { return strings.Split(v, ",") }
// After configure, the conversion should be OK.
fmt.Println(c.ConvertType("1,2,3", reflect.TypeOf([]int{}))) // -> []int{1, 2, 3}

// Conf.FieldMatcherCreator define how to match names from a struct when converting from
// a map or another struct.
// Here we demonstrate how to make snake-case names match the field names automatically,
// using the build-in FieldMatcherCreator named SimpleMatcherCreator.
c.Conf.FieldMatcherCreator = &conv.SimpleMatcherCreator{
	Conf: conv.SimpleMatcherConfig{
		CamelSnakeCase: true,
	},
}
// When then CamelSnakeCase option is true, 'mailAddr' can match the field MailAddr, 'is_vip' can match IsVip.
m := map[string]interface{}{"name": "Bob", "age": "51", "mailAddr": "bob@example.org", "is_vip": "true"}
user := DemoUser{}
_ = c.Convert(m, &user)
fmt.Printf("%+v\n", user) // -> DemoUser{Name: "Bob", MailAddr: "bob@example.org", Age: 51, IsVip: true})

// The json package of the standard library does not support matching fields in case-insensitive manner.
// We have to use field tag to specify the name of JSON properties.
// With FieldMatcherCreator, we can unmarshal JSON in case-insensitive manner, using a map as a middleware.
// Thou the performance is not good, but it works :) .
middleware := make(map[string]interface{})
_ = json.Unmarshal([]byte(`{"name":"Alice", "mailAddr":"alice@example.org", "isVip": true, "age":27}`), &middleware)
_ = c.Convert(middleware, &user)
fmt.Printf("%+v\n", user) // -> DemoUser{Name: "Alice", MailAddr: "alice@example.org", Age: 27, IsVip: true})
Output:

<nil> conv.ConvertType: conv.StringToSlice: cannot convert to []int, at index 0: conv.SimpleToSimple: strconv.ParseInt: parsing "1,2,3": invalid syntax
[1 2 3] <nil>
{Name:Bob MailAddr:bob@example.org Age:51 IsVip:true}
{Name:Alice MailAddr:alice@example.org Age:27 IsVip:true}
Example (TimeConversion)
// When converting to a time.Time, the location depends on the input value.

// When converting a number to a Time, returns a Time with the location time.Local, behaves like time.Unix().
t, _ := conv.Time(0) // Shortcut of conv.ConvertType(0, reflect.TypeOf(time.Time{})) .

fmt.Println("convert a number to a Time, the output location is:", t.Location()) // -> Local

// Returns a Time with offset 02:00, like time.Parse().
t, _ = conv.Time("2022-04-20T11:30:40+02:00")
_, offset := t.Zone()
fmt.Println(offset) // -> 7200 which is the total seconds of 02:00.

// Returns  a Time with time.UTC, like time.Parse().
t, _ = conv.Time("2022-04-20T11:30:40Z")
fmt.Println(t.Location()) // -> UTC

// When converting a Time to a Time, keeps the location: the raw value is cloned.
t, _ = conv.Time(time.Now())
fmt.Println("convert a Local time to a Local time, the output location is:", t.Location()) // -> Local
t, _ = conv.Time(time.Now().UTC())
fmt.Println("convert an UTC time to an UTC time, the output location is:", t.Location()) // -> UTC

// When converting a Time to a number, outputs an UNIX timestamp.
t, _ = time.Parse(time.RFC3339, "2022-04-20T03:30:40Z")
fmt.Println(conv.MustInt(t)) // -> 1650425440

// When converting a Time to a string, outputs using conv.DefaultTimeToString function, which format the time in RFC3339.
fmt.Println(conv.MustString(t)) // -> 2022-04-20T03:30:40Z
Output:

convert a number to a Time, the output location is: Local
7200
UTC
convert a Local time to a Local time, the output location is: Local
convert an UTC time to an UTC time, the output location is: UTC
1650425440
2022-04-20T03:30:40Z

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Bool

func Bool(v interface{}) (bool, error)

Bool converts the given value to the corresponding value of bool. The value must be simple, for which IsSimpleType() returns true. It is equivalent to new(Conv).SimpleToBool(v) .

func Complex128

func Complex128(v interface{}) (complex128, error)

Complex128 converts the given value to the corresponding value of complex128. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(complex128(0+0i))) .

func Complex64

func Complex64(v interface{}) (complex64, error)

Complex64 converts the given value to the corresponding value of complex64. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(complex64(0+0i))) .

func Convert

func Convert(src interface{}, dstPtr interface{}) error

Convert is equivalent to new(Conv).Convert() .

func ConvertType

func ConvertType(src interface{}, dstTyp reflect.Type) (interface{}, error)

ConvertType is equivalent to new(Conv).ConvertType() .

func DefaultStringToTime

func DefaultStringToTime(v string) (time.Time, error)

DefaultStringToTime parses the time using the time.RFC3339Nano format.

func DefaultTimeToString

func DefaultTimeToString(t time.Time) (string, error)

DefaultTimeToString formats time using the time.RFC3339 format.

func Float32

func Float32(v interface{}) (float32, error)

Float32 converts the given value to the corresponding value of float32. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(float32(0))) .

func Float64

func Float64(v interface{}) (float64, error)

Float64 converts the given value to the corresponding value of float64. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(float64(0))) .

func Int

func Int(v interface{}) (int, error)

Int converts the given value to the corresponding value of int. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(int(0))) .

func Int16

func Int16(v interface{}) (int16, error)

Int16 converts the given value to the corresponding value of int16. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(int16(0))) .

func Int32

func Int32(v interface{}) (int32, error)

Int32 converts the given value to the corresponding value of int32. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(int32(0))) .

func Int64

func Int64(v interface{}) (int64, error)

Int64 converts the given value to the corresponding value of int64. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(int64(0))) .

func Int8

func Int8(v interface{}) (int8, error)

Int8 converts the given value to the corresponding value of int8. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(int8(0))) .

func IsPrimitiveKind

func IsPrimitiveKind(k reflect.Kind) bool

IsPrimitiveKind returns true if the given Kind is any of bool, int*, uint*, float*, complex* or string.

func IsPrimitiveType

func IsPrimitiveType(t reflect.Type) bool

IsPrimitiveType returns true if the given type is any of bool, int*, uint*, float*, complex* or string.

func IsSimpleType

func IsSimpleType(t reflect.Type) bool

IsSimpleType returns true if the given type IsPrimitiveType() or is convertible to time.Time .

func MapToStruct

func MapToStruct(m map[string]interface{}, dstTyp reflect.Type) (interface{}, error)

MapToStruct is equivalent to new(Conv).MapToStruct() .

func MustBool added in v0.2.0

func MustBool(v interface{}) bool

MustBool is like Bool() but panics instead of returns an error.

func MustConvert added in v0.2.0

func MustConvert(src interface{}, dstPtr interface{})

MustConvert is equivalent to new(Conv).MustConvert() .

func MustConvertType added in v0.2.0

func MustConvertType(src interface{}, dstTyp reflect.Type) interface{}

MustConvertType is equivalent to new(Conv).MustConvertType() .

func MustFloat32 added in v0.2.0

func MustFloat32(v interface{}) float32

MustFloat32 is like Float32() but panics instead of returns an error.

func MustFloat64 added in v0.2.0

func MustFloat64(v interface{}) float64

MustFloat64 is like Float64() but panics instead of returns an error.

func MustInt added in v0.2.0

func MustInt(v interface{}) int

MustInt is like Int() but panics instead of returns an error.

func MustInt16 added in v0.2.0

func MustInt16(v interface{}) int16

MustInt16 is like Int16() but panics instead of returns an error.

func MustInt32 added in v0.2.0

func MustInt32(v interface{}) int32

MustInt32 is like Int32() but panics instead of returns an error.

func MustInt64 added in v0.2.0

func MustInt64(v interface{}) int64

MustInt64 is like Int64() but panics instead of returns an error.

func MustInt8 added in v0.2.0

func MustInt8(v interface{}) int8

MustInt8 is like Int8() but panics instead of returns an error.

func MustMapToStruct added in v0.2.0

func MustMapToStruct(m map[string]interface{}, dstTyp reflect.Type) interface{}

MustMapToStruct is like MapToStruct() but panics instead of returns an error.

func MustString added in v0.2.0

func MustString(v interface{}) string

MustString is like String() but panics instead of returns an error.

func MustStructToMap added in v0.2.0

func MustStructToMap(v interface{}) map[string]interface{}

MustStructToMap is like StructToMap() but panics instead of returns an error.

func MustUint added in v0.2.0

func MustUint(v interface{}) uint

MustUint is like Uint() but panics instead of returns an error.

func MustUint16 added in v0.2.0

func MustUint16(v interface{}) uint16

MustUint16 is like Uint16() but panics instead of returns an error.

func MustUint32 added in v0.2.0

func MustUint32(v interface{}) uint32

MustUint32 is like Uint32() but panics instead of returns an error.

func MustUint64 added in v0.2.0

func MustUint64(v interface{}) uint64

MustUint64 is like Uint64() but panics instead of returns an error.

func MustUint8 added in v0.2.0

func MustUint8(v interface{}) uint8

MustUint8 is like Uint8() but panics instead of returns an error.

func String

func String(v interface{}) (string, error)

String converts the given value to the corresponding value of string. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).SimpleToString(v) .

func StructToMap

func StructToMap(v interface{}) (map[string]interface{}, error)

StructToMap is equivalent to new(Conv).StructToMap() .

func Time

func Time(v interface{}) (time.Time, error)

Time converts the given value to the corresponding value of time.Time. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).SimpleToSimple(v, reflect.TypeOf(time.Time{})) .

func Uint

func Uint(v interface{}) (uint, error)

Uint converts the given value to the corresponding value of uint. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(uint(0))) .

func Uint16

func Uint16(v interface{}) (uint16, error)

Uint16 converts the given value to the corresponding value of uint16. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(uint(0))) .

func Uint32

func Uint32(v interface{}) (uint32, error)

Uint32 converts the given value to the corresponding value of uint32. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(uint(0))) .

func Uint64

func Uint64(v interface{}) (uint64, error)

Uint64 converts the given value to the corresponding value of uint64. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(uint(0))) .

func Uint8

func Uint8(v interface{}) (uint8, error)

Uint8 converts the given value to the corresponding value of uint8. The value must be a simple type, for which IsSimpleType() returns true. It is equivalent to new(Conv).Convert(v, reflect.TypeOf(uint(0))) .

Types

type Config

type Config struct {
	// StringSplitter is the function used to split the string into elements of the slice when converting a string to a slice.
	// It is called internally by Convert(), ConvertType() or other functions.
	// Set this field if customization of the conversion is needed.
	// If this field is nil, the value will not be split.
	StringSplitter func(v string) []string

	// FieldMatcherCreator is used to get FieldMatcher instances when converting from map to struct or
	// from struct to struct.
	//
	// When converting a map to a struct, a FieldMatcherCreator.GetMatcher() returns a FieldMatcher instance for the
	// target struct, then FieldMatcher.MatchField() is applied to each key of the map.
	// The matched field will be set by the converted value of the key, the value is converted with Conv.ConvertType().
	//
	// When converting a struct to another, FieldMatcher.MatchField() is applied to each field name from the source struct.
	//
	// If FieldMatcherCreator is nil, SimpleMatcherCreator() will be used. There are some predefined implementations,
	// such as CaseInsensitiveFieldMatcherCreator(), CamelSnakeCaseFieldMatcherCreator().
	FieldMatcherCreator FieldMatcherCreator

	// CustomConverters provides a group of functions for converting the given value to some specific type.
	// The target type will never be nil.
	//
	// These functions are used to customize the conversion.
	// It is only used by Convert() or ConvertType(), not works in other functions.
	//
	// When a conversion starts, it will firstly go through each function in this slice:
	//   - The conversion stops immediately when some function returns a non-nil result or an error.
	//     Convert() or ConvertType() will use the result or returns the error directly.
	//   - The conversion runs next function in the slice if the previous one return nil with no error.
	//   - If no function in the slice returns OK, the conversion will continue with the predefined implementations,
	//     such as MapToMap(), StructToMap(), etc.
	//
	// NOTE: If your ConvertFunc use Conv internally, be carefully if there will be infinity loops. Is it suggested to
	// use a Conv instance with no ConvertFunc for the internal conversions.
	CustomConverters []ConvertFunc

	// TimeToString formats the given time.
	// It is called internally by Convert(), ConvertType() or other functions.
	// Set this field if it is needed to customize the procedure.
	// If this field is nil, the function DefaultTimeToString() will be used.
	TimeToString func(t time.Time) (string, error)

	// StringToTime parses the given string and returns the time it represents.
	// It is called internally by Convert, ConvertType or other functions.
	// Set this field if it is needed to customize the procedure.
	// If this field is nil, the function DefaultStringToTime() will be used.
	StringToTime func(v string) (time.Time, error)
}

Config is used to customize the conversion behavior of Conv .

type Conv

type Conv struct {
	// Conf is used to customize the conversion behavior.
	Conf Config
}

Conv provides a group of functions to convert between simple types, maps, slices and structs. A pointer of a zero value is ready to use, it has the default behavior:

new(Conv).ConvertType(...)

The field Config is used to customize the conversion behavior. e.g., this Conv instance uses a built-in FieldMatcherCreator and a custom TimeToString function.

c:= &Conv{
    Config: Config {
        FieldMatcherCreator: CaseInsensitiveFieldMatcherCreator(),
        TimeToString: func(t time.Time) (string, error) { return t.Format(time.RFC1123), nil },
    },
}

All functions are thread-safe and can be used concurrently.

func (*Conv) Convert

func (c *Conv) Convert(src interface{}, dstPtr interface{}) error

Convert is like Conv.ConvertType() , but receives a pointer instead of a type. It stores the result in the value pointed to by dst.

If the source value is nil, the function returns without an error, the underlying value of the pointer will not be set. If dst is not a pointer, the function panics an error.

func (*Conv) ConvertType

func (c *Conv) ConvertType(src interface{}, dstTyp reflect.Type) (interface{}, error)

ConvertType is the core function of Conv . It converts the given value to the destination type.

Currently, these conversions are supported:

simple                 -> simple                  use Conv.SimpleToSimple()
string                 -> []simple                use Conv.StringToSlice()
map[string]interface{} -> struct                  use Conv.MapToStruct()
map[ANY]ANY            -> map[ANY]ANY             use Conv.MapToMap()
[]ANY                  -> []ANY                   use Conv.SliceToSlice()
struct                 -> map[string]interface{}  use Conv.StructToMap()
struct                 -> struct                  use Conv.StructToStruct()

'ANY' generally can be any other type listed above. 'simple' is some type which IsSimpleType() returns true.

If the destination type is the type of the empty interface, the function returns src directly without any error.

For pointers: If the source value is a pointer, the value pointed to will be extracted and converted. The destination type can be a type of pointer, the source value which is nil will be converted to a nil pointer.

This function can be used to deep-clone a struct, e.g.:

clone, err := ConvertType(src, reflect.TypeOf(src))

There is a special conversion that can convert a map[string]interface{} to some other type listed above, when the map has only one key and the key is an empty string, the conversion is performed over the value other than the map itself. This is a special contract for some particular situation, when some code is working on maps only.

func (*Conv) MapToMap

func (c *Conv) MapToMap(m interface{}, typ reflect.Type) (interface{}, error)

MapToMap converts a map to another map. If the source value is nil, the function returns a nil map of the destination type without any error.

All keys and values in the map are converted using Conv.ConvertType() .

func (*Conv) MapToStruct

func (c *Conv) MapToStruct(m map[string]interface{}, dstTyp reflect.Type) (interface{}, error)

MapToStruct converts a map[string]interface{} to a struct.

Each exported field of the struct is indexed using Conv.Config.FieldMatcherCreator().

func (*Conv) MustConvert added in v0.2.0

func (c *Conv) MustConvert(src interface{}, dstPtr interface{})

MustConvert is like Convert() but panics instead of returns an error.

func (*Conv) MustConvertType added in v0.2.0

func (c *Conv) MustConvertType(src interface{}, dstTyp reflect.Type) interface{}

MustConvertType is like ConvertType() but panics instead of returns an error.

func (*Conv) SimpleToBool

func (c *Conv) SimpleToBool(simple interface{}) (bool, error)

SimpleToBool converts the value to bool. The value must be simple, for which IsSimpleType() returns true.

Rules:

  • nil: as false.
  • Numbers: zero as false, non-zero as true.
  • String: same as strconv.ParseBool().
  • time.Time: zero Unix timestamps as false, other values as true.
  • Other values are not supported, returns false and an error.

func (*Conv) SimpleToSimple

func (c *Conv) SimpleToSimple(src interface{}, dstTyp reflect.Type) (interface{}, error)

SimpleToSimple converts a simple type, for which IsSimpleType() returns true, to another simple type. The conversion use the following rules:

Booleans:

  • true/false is converted to number 0/1, or string '0'/'1'.
  • From a boolean to a string: use strconv.ParseBool().
  • From a number to a boolean: zero value as false; non-zero value as true.

Numbers:

  • From a complex number to a real number: the imaginary part must be zero, the real part will be converted.

To time.Time:

  • From a number: the number is treated as a Unix-timestamp as converted using time.Unix(), the time zone is time.Local.
  • From a string: use Conv.Conf.StringToTime function.
  • From another time.Time: the raw value is cloned, includes the timestamp and the location.

From time.Time:

  • To a number: output a Unix-timestamp.
  • To a string: use Conv.Conf.TimeToString function.

func (*Conv) SimpleToString

func (c *Conv) SimpleToString(v interface{}) (string, error)

SimpleToString converts the given value to a string. The value must be a simple type, for which IsSimpleType() returns true.

Conv.Config.StringToTime() is used to format times. Specially, booleans are converted to 0/1, not the default format true/false.

func (*Conv) SliceToSlice

func (c *Conv) SliceToSlice(src interface{}, dstSliceTyp reflect.Type) (interface{}, error)

SliceToSlice converts a slice to another slice.

Each element will be converted using Conv.ConvertType() . A nil slice will be converted to a nil slice of the destination type. If the source value is nil interface{}, returns nil and an error.

func (*Conv) StringToSlice

func (c *Conv) StringToSlice(v string, simpleSliceType reflect.Type) (interface{}, error)

StringToSlice converts a string to a slice. The elements of the slice must be simple type, for which IsSimpleType() returns true.

Conv.Config.StringSplitter() is used to split the string.

func (*Conv) StructToMap

func (c *Conv) StructToMap(v interface{}) (map[string]interface{}, error)

StructToMap is partially like json.Unmarshal(json.Marshal(v), &someMap) . It converts a struct to map[string]interface{} .

Each value of exported field will be processed recursively with an internal function f() , which:

Simple types, for which IsSimpleType() returns true:

  • A type whose kind is primitive, will be converted to a primitive value.
  • For other types, the value will be cloned into the map directly.

Slices:

  • A nil slice is converted to a nil slice; an empty slice is converted to an empty slice with cap=0.
  • A non-empty slice is converted to another slice, each element is process with f() , all elements must be the same type.

Maps:

  • A nil map are converted to nil of map[string]interface{} .
  • A non-nil map is converted to map[string]interface{} , keys are processed with Conv.ConvertType() , values with f() .

Structs are converted to map[string]interface{} using Conv.StructToMap() recursively.

Pointers:

  • Nils are ignored.
  • Non-nil values pointed to are converted with f() .

Other types not listed above are not supported and will result in an error.

func (*Conv) StructToStruct

func (c *Conv) StructToStruct(src interface{}, dstTyp reflect.Type) (interface{}, error)

StructToStruct converts a struct to another. If the given value is nil, returns nil and an error.

When converting, each field of the destination struct is indexed using Conv.Config.FieldMatcherCreator. The field values are converted using Conv.ConvertType() .

This function can be used to deep-clone a struct.

type ConvertFunc added in v0.6.0

type ConvertFunc func(value interface{}, typ reflect.Type) (result interface{}, err error)

ConvertFunc is used to customize the conversion.

type FieldInfo added in v0.4.1

type FieldInfo struct {
	reflect.StructField

	// If the field is a field of am embedded and untagged field which type is struct,
	// the path is a dot-split string like A.B.C; otherwise it's equal to F.Name.
	Path string

	// The tag value of the field.
	TagValue string
}

FieldInfo describes a field in a struct.

type FieldMatcher

type FieldMatcher interface {
	// MatchField returns the first matched field for the given name;
	// if no name can match, returns a zero value and false.
	// The field returned must be an exported field.
	MatchField(name string) (field reflect.StructField, ok bool)
}

FieldMatcher is used to match names when converting from map to struct or from struct to struct.

type FieldMatcherCreator

type FieldMatcherCreator interface {
	// GetMatcher returns a FieldMatcher for the given type of struct.
	// If the type is not Struct, it panics.
	GetMatcher(typ reflect.Type) FieldMatcher
}

FieldMatcherCreator is used to create FieldMatcher instances when converting from map to struct or from struct to struct.

type FieldWalker added in v0.4.1

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

FieldWalker is used to traverse all field of a struct.

The traverse will go into each level of embedded and untagged structs. Unexported fields are ignored. It reads fields in this order:

  • Tagged fields.
  • Non-embedded struct or non-struct fields.
  • Fields of embedded struct, recursively.

e.g. without tagName

type Ec struct {
    D int
}
type Eb struct {
    B int // hided by T.B
    Ec    // hided by T.Ec.D
    C int
}
type T struct {
    A  int
    Eb
    B  string // hides Eb.B
    Ec
}

The traverse order is:

PATH     INDEX
A        {0}
B        {2}
Eb.C     {1, 2}
Ec.D     {3, 0}

e.g. with tagName="json"

type A struct {
  A int
  X int  // hided by T.B
}
type B struct {
  B1 int // absent
  B2 int // absent
}
type T struct {
  A
  B `json:"X"` // hides B.X, the traverse will not go into the field
}

The traverse order is:

PATH     INDEX    TAG
B        {1}      X
A.A      {0, 0}

func NewFieldWalker added in v0.4.1

func NewFieldWalker(typ reflect.Type, tagName string) *FieldWalker

NewFieldWalker creates a new instance of FieldWalker. When tagName is specified, the values of the tag will be filled into FieldInfo.TagValue during the traversal.

func (*FieldWalker) WalkFields added in v0.4.1

func (walker *FieldWalker) WalkFields(callback func(FieldInfo) bool)

WalkFields walks through fields of the given type of struct (or pointer) with a breadth-first traverse. Each field will be send to the callback function. If the function returns false, the traverse stops.

func (*FieldWalker) WalkValues added in v0.4.1

func (walker *FieldWalker) WalkValues(value reflect.Value, callback func(FieldInfo, reflect.Value) bool)

WalkValues is like WalkFields(), but walks through all field values.

If a struct is embedded as a pointer, and the value is nil, the field is ignored. If the given value is nil, the traverse stops with no callback.

type SimpleMatcherConfig

type SimpleMatcherConfig struct {
	// Tag specifies the tag name for the fields. When a name is given by the tag, the matcher
	// matches the field using the given name; otherwise the raw field name is used.
	// The tag works like some commonly used tags such as 'json', 'xml'.
	// Tag can be empty.
	//
	// e.g. When 'conv' is given as the tag name, the matcher returns the OldName field when
	// indexing 'NewName'.
	//   type Target struct {
	//       OldName int `conv:"NewName"` // 'NewName' can match this field.
	//       RawName                      // No tag specified, use 'RawName' for field matching.
	//   }
	//
	Tag string

	// CaseInsensitive specifies whether the matcher matches field names in a case-insensitive manner.
	// If this field is true, CamelSnakeCase is ignored.
	//
	// If the field is true, 'ab', 'Ab', 'aB', 'AB' are equal.
	//
	CaseInsensitive bool

	// OmitUnderscore specifies whether to omit underscores in field names.
	// If this field is true, CamelSnakeCase is ignored.
	//
	// If the field is true, 'ab', 'a_b', '_ab', 'a__b_' are equal.
	//
	OmitUnderscore bool

	// CamelSnakeCase whether to support camel-case and snake-case name comparing.
	// If CaseInsensitive or OmitUnderscore is true, this field is ignored.
	//
	// When it is set to true, the matcher can match names in camel-case or snake-case form, such as
	// 'lowerCaseCamel' or 'UpperCaseCamel' or 'snake_case' or 'Snake_Case' (sometimes called train-case).
	//
	// The first rune of each word is compared case-insensitively; others are compared in the case-sensitive
	// manner. For example:
	//   - These names are equal: aaBB, AaBb, aa_bb, Aa_Bb, aa_BB
	//   - These names are not equal: aa_bb, Aabb, aabB, _aaBb, AaBb_, Aa__Bb
	//
	// Mostly this option can be used to match field names that come from different platform, e.g.,
	// 'lowerCaseCamel' from Javascript, 'UpperCaseCamel' from Go, 'snake_case' from Mysql database.
	//
	CamelSnakeCase bool
}

SimpleMatcherConfig configures SimpleMatcherCreator.

type SimpleMatcherCreator

type SimpleMatcherCreator struct {
	Conf SimpleMatcherConfig
	// contains filtered or unexported fields
}

SimpleMatcherCreator returns an instance of FieldMatcherCreator. It is used as the default value when Conv.Config.FieldMatcher is nil.

func (*SimpleMatcherCreator) GetMatcher

func (c *SimpleMatcherCreator) GetMatcher(typ reflect.Type) FieldMatcher

GetMatcher implements FieldMatcherCreator.GetMatcher().

Jump to

Keyboard shortcuts

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