# README
conv - A Go package for type conversion
Features:
- Supports conversion between numbers, string, time.
- Supports converting from
slice
toslice
. - Supports converting between
struct
andmap[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: "[email protected]", Age: 51}
out, err := conv.ConvertType(user, reflect.TypeOf(map[string]interface{}{}))
fmt.Println(out, err) // -> map[string]interface{}{"Age":51, "MailAddr":"[email protected]", "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: "[email protected]", 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: "[email protected]", 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:[email protected] Name:Bob] <nil>
// {Name:Alice MailAddr: Age:27 IsVip:true}
// {Name:Bob MailAddr:[email protected] Age:51 IsVip:false}
// {Name:Bob MailAddr:[email protected] 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": "[email protected]", "is_vip": "true"}
user := DemoUser{}
_ = c.Convert(m, &user)
fmt.Printf("%+v\n", user) // -> DemoUser{Name: "Bob", MailAddr: "[email protected]", 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":"[email protected]", "isVip": true, "age":27}`), &middleware)
_ = c.Convert(middleware, &user)
fmt.Printf("%+v\n", user) // -> DemoUser{Name: "Alice", MailAddr: "[email protected]", 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:[email protected] Age:51 IsVip:true}
// {Name:Alice MailAddr:[email protected] 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
tomap
or to otherstruct
.
# Functions
Bool converts the given value to the corresponding value of bool.
Complex128 converts the given value to the corresponding value of complex128.
Complex64 converts the given value to the corresponding value of complex64.
Convert is equivalent to new(Conv).Convert() .
ConvertType is equivalent to new(Conv).ConvertType() .
DefaultStringToTime parses the time using the time.RFC3339Nano format.
DefaultTimeToString formats time using the time.RFC3339 format.
Float32 converts the given value to the corresponding value of float32.
Float64 converts the given value to the corresponding value of float64.
Int converts the given value to the corresponding value of int.
Int16 converts the given value to the corresponding value of int16.
Int32 converts the given value to the corresponding value of int32.
Int64 converts the given value to the corresponding value of int64.
Int8 converts the given value to the corresponding value of int8.
IsPrimitiveKind returns true if the given Kind is any of bool, int*, uint*, float*, complex* or string.
IsPrimitiveType returns true if the given type is any of bool, int*, uint*, float*, complex* or string.
IsSimpleType returns true if the given type IsPrimitiveType() or is convertible to time.Time .
MapToStruct is equivalent to new(Conv).MapToStruct() .
MustBool is like Bool() but panics instead of returns an error.
MustConvert is equivalent to new(Conv).MustConvert() .
MustConvertType is equivalent to new(Conv).MustConvertType() .
MustFloat32 is like Float32() but panics instead of returns an error.
MustFloat64 is like Float64() but panics instead of returns an error.
MustInt is like Int() but panics instead of returns an error.
MustInt16 is like Int16() but panics instead of returns an error.
MustInt32 is like Int32() but panics instead of returns an error.
MustInt64 is like Int64() but panics instead of returns an error.
MustInt8 is like Int8() but panics instead of returns an error.
MustMapToStruct is like MapToStruct() but panics instead of returns an error.
MustString is like String() but panics instead of returns an error.
MustStructToMap is like StructToMap() but panics instead of returns an error.
MustUint is like Uint() but panics instead of returns an error.
MustUint16 is like Uint16() but panics instead of returns an error.
MustUint32 is like Uint32() but panics instead of returns an error.
MustUint64 is like Uint64() but panics instead of returns an error.
MustUint8 is like Uint8() but panics instead of returns an error.
NewFieldWalker creates a new instance of FieldWalker.
String converts the given value to the corresponding value of string.
StructToMap is equivalent to new(Conv).StructToMap() .
Time converts the given value to the corresponding value of time.Time.
Uint converts the given value to the corresponding value of uint.
Uint16 converts the given value to the corresponding value of uint16.
Uint32 converts the given value to the corresponding value of uint32.
Uint64 converts the given value to the corresponding value of uint64.
Uint8 converts the given value to the corresponding value of uint8.
# Structs
Config is used to customize the conversion behavior of Conv .
Conv provides a group of functions to convert between simple types, maps, slices and structs.
FieldInfo describes a field in a struct.
FieldWalker is used to traverse all field of a struct.
SimpleMatcherConfig configures SimpleMatcherCreator.
SimpleMatcherCreator returns an instance of FieldMatcherCreator.
# Interfaces
FieldMatcher is used to match names when converting from map to struct or from struct to struct.
FieldMatcherCreator is used to create FieldMatcher instances when converting from map to struct or from struct to struct.
# Type aliases
ConvertFunc is used to customize the conversion.