文章目录
- go get github.com/spf13/cast
- package main import ( “fmt” “github.com/spf13/cast” ) func main() { // ToString fmt.Println(cast.ToString(“leedarjun”)) // leedarjun fmt.Println(cast.ToString(8)) // 8 fmt.Println(cast.ToString(8.31)) // 8.31 fmt.Println(cast.ToString([]byte(“one time”))) // one time fmt.Println(cast.ToString(nil)) // “” var foo interface{} = “one more time” fmt.Println(cast.ToString(foo)) // one more time // ToInt fmt.Println(cast.ToInt(8)) // 8 fmt.Println(cast.ToInt(8.31)) // 8 fmt.Println(cast.ToInt(“8”)) // 8 fmt.Println(cast.ToInt(true)) // 1 fmt.Println(cast.ToInt(false)) // 0 var eight interface{} = 8 fmt.Println(cast.ToInt(eight)) // 8 fmt.Println(cast.ToInt(nil)) // 0 } 实际上,cast实现了多种常见类型之间的相互转换,返回最符合直觉的结果。例如: nil转为string的结果为"",而不是"nil"; true转为string的结果为"true",而true转为int的结果为1; interface{}转为其他类型,要看它里面存储的值类型。 这些类型包括所有的基本类型(整形、浮点型、布尔值和字符串)、空接口、nil,时间(time.Time)、时长(time.Duration)以及它们的切片类型,还有map[string]Type(其中Type为前面提到的类型): byte bool float32 float64 string int8 int16 int32 int64 int uint8 uint16 uint32 uint64 uint interface{} time.Time time.Duration nil
- 源码如下: // ToTimeE casts an interface to a time.Time type. func ToTimeE(i interface{}) (tim time.Time, err error) { return ToTimeInDefaultLocationE(i, time.UTC) } // ToTimeInDefaultLocationE casts an empty interface to time.Time, // interpreting inputs without a timezone to be in the given location, // or the local timezone if nil. func ToTimeInDefaultLocationE(i interface{}, location *time.Location) (tim time.Time, err error) { i = indirect(i) switch v := i.(type) { case time.Time: return v, nil case string: return StringToDateInDefaultLocation(v, location) case json.Number: s, err1 := ToInt64E(v) if err1 != nil { return time.Time{}, fmt.Errorf(“unable to cast %#v of type %T to Time”, i, i) } return time.Unix(s, 0), nil case int: return time.Unix(int64(v), 0), nil case int64: return time.Unix(v, 0), nil case int32: return time.Unix(int64(v), 0), nil case uint: return time.Unix(int64(v), 0), nil case uint64: return time.Unix(int64(v), 0), nil case uint32: return time.Unix(int64(v), 0), nil default: return time.Time{}, fmt.Errorf(“unable to cast %#v of type %T to Time”, i, i) } } 根据传入的类型执行不同的处理: 如果是time.Time,直接返回; 如果是整型,将参数作为时间戳(自 UTC 时间1970.01.01 00:00:00到现在的秒数)调用time.Unix生成时间。Unix接受两个参数,第一个参数指定秒,第二个参数指定纳秒; 如果是字符串,调用StringToDate函数依次尝试以下面这些时间格式调用time.Parse解析该字符串。如果某个格式解析成功,则返回获得的time.Time。否则解析失败,返回错误; 其他任何类型都无法转换为time.Time。
- 源码: // cast/caste.go func StringToDate(s string) (time.Time, error) { return parseDateWith(s, []string{ time.RFC3339, “2006-01-02T15:04:05”, // iso8601 without timezone time.RFC1123Z, time.RFC1123, time.RFC822Z, time.RFC822, time.RFC850, time.ANSIC, time.UnixDate, time.RubyDate, “2006-01-02 15:04:05.999999999 -0700 MST”, // Time.String() “2006-01-02”, “02 Jan 2006”, “2006-01-02T15:04:05-0700”, // RFC3339 without timezone hh:mm colon “2006-01-02 15:04:05 -07:00”, “2006-01-02 15:04:05 -0700”, “2006-01-02 15:04:05Z07:00”, // RFC3339 without T “2006-01-02 15:04:05Z0700”, // RFC3339 without T or timezone hh:mm colon “2006-01-02 15:04:05”, time.Kitchen, time.Stamp, time.StampMilli, time.StampMicro, time.StampNano, }) } func parseDateWith(s string, dates []string) (d time.Time, e error) { for _, dateType := range dates { if d, e = time.Parse(dateType, s); e == nil { return } } return d, fmt.Errorf(“unable to parse date: %s”, s) }
- 源码如下: // cast/caste.go func ToDurationE(i interface{}) (d time.Duration, err error) { i = indirect(i) switch s := i.(type) { case time.Duration: return s, nil case int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8: d = time.Duration(ToInt64(s)) return case float32, float64: d = time.Duration(ToFloat64(s)) return case string: if strings.ContainsAny(s, “nsuµmh”) { d, err = time.ParseDuration(s) } else { d, err = time.ParseDuration(s “ns”) } return default: err = fmt.Errorf(“unable to cast %#v of type %T to Duration”, i, i) return } } 根据传入的类型进行不同的处理: 如果是time.Duration类型,直接返回; 如果是整型或浮点型,将其数值强制转换为time.Duration类型,单位默认为ns; 如果是字符串,分为两种情况:如果字符串中有时间单位符号nsuµmh,直接调用time.ParseDuration解析;否则在字符串后拼接ns再调用time.ParseDuration解析; 其他类型解析失败。 示例: package main import ( “fmt” “time” “github.com/spf13/cast” ) func main() { now := time.Now() timestamp := 1579615973 timeStr := “2020-01-21 22:13:48” fmt.Println(cast.ToTime(now)) // 2020-01-22 06:31:50.5068465 0800 CST m= 0.000997701 fmt.Println(cast.ToTime(timestamp)) // 2020-01-21 22:12:53 0800 CST fmt.Println(cast.ToTime(timeStr)) // 2020-01-21 22:13:48 0000 UTC d, _ := time.ParseDuration(“1m30s”) ns := 30000 strWithUnit := “130s” strWithoutUnit := “130” fmt.Println(cast.ToDuration(d)) // 1m30s fmt.Println(cast.ToDuration(ns)) // 30µs fmt.Println(cast.ToDuration(strWithUnit)) // 2m10s fmt.Println(cast.ToDuration(strWithoutUnit)) // 130ns }
- 源码如下: func ToIntSliceE(i interface{}) ([]int, error) { if i == nil { return []int{}, fmt.Errorf(“unable to cast %#v of type %T to []int”, i, i) } switch v := i.(type) { case []int: return v, nil } kind := reflect.TypeOf(i).Kind() switch kind { case reflect.Slice, reflect.Array: s := reflect.ValueOf(i) a := make([]int, s.Len()) for j := 0; j < s.Len(); j { val, err := ToIntE(s.Index(j).Interface()) if err != nil { return []int{}, fmt.Errorf(“unable to cast %#v of type %T to []int”, i, i) } a[j] = val } return a, nil default: return []int{}, fmt.Errorf(“unable to cast %#v of type %T to []int”, i, i) } } 根据传入参数的类型: 如果是nil,直接返回错误; 如果是[]int,不用转换,直接返回; 如果传入类型为切片或数组,新建一个[]int,将切片或数组中的每个元素转为int放到该[]int中。最后返回这个[]int; 其他情况,不能转换。
- 源码如下: func ToStringSliceE(i interface{}) ([]string, error) { var a []string switch v := i.(type) { case []interface{}: for _, u := range v { a = append(a, ToString(u)) } return a, nil case []string: return v, nil case string: return strings.Fields(v), nil case interface{}: str, err := ToStringE(v) if err != nil { return a, fmt.Errorf(“unable to cast %#v of type %T to []string”, i, i) } return []string{str}, nil default: return a, fmt.Errorf(“unable to cast %#v of type %T to []string”, i, i) } } 根据传入的参数类型: 如果是[]interface{},将该参数中每个元素转为string,返回结果切片; 如果是[]string,不需要转换,直接返回; 如果是interface{},将参数转为string,返回只包含这个值的切片; 如果是string,调用strings.Fields函数按空白符将参数拆分,返回拆分后的字符串切片; 其他情况,不能转换。 示例: package main import ( “fmt” “github.com/spf13/cast” ) func main() { sliceOfInt := []int{1, 3, 7} arrayOfInt := [3]int{8, 12} // ToIntSlice fmt.Println(cast.ToIntSlice(sliceOfInt)) // [1 3 7] fmt.Println(cast.ToIntSlice(arrayOfInt)) // [8 12 0] sliceOfInterface := []interface{}{1, 2.0, “darjun”} sliceOfString := []string{“abc”, “dj”, “pipi”} stringFields := ” abc def hij ” any := interface{}(37) // ToStringSliceE fmt.Println(cast.ToStringSlice(sliceOfInterface)) // [1 2 darjun] fmt.Println(cast.ToStringSlice(sliceOfString)) // [abc dj pipi] fmt.Println(cast.ToStringSlice(stringFields)) // [abc def hij] fmt.Println(cast.ToStringSlice(any)) // [37] }
目录
- 简介
- 快速入门
- 安装
- 使用
- 高级转换
- 时间和时长转换
- 时间类型的转换
- 字符串转换为时间
- 时长类型的转换
- 转换为切片
- ToIntSliceE
- ToStringSliceE
- 转为map[string]Type类型
cast可以在 Go 中轻松安全地从一种类型转换为另一种类型,cast 提供了简单的函数来轻松地将数字转换为字符串,将接口转换为布尔值等。当需要显示类型转换时,cast 会智能地执行转换操作。
go get github.com/spf13/cast
package main
import (
"fmt"
"github.com/spf13/cast"
)
func main() {
// ToString
fmt.Println(cast.ToString("leedarjun")) // leedarjun
fmt.Println(cast.ToString(8)) // 8
fmt.Println(cast.ToString(8.31)) // 8.31
fmt.Println(cast.ToString([]byte("one time"))) // one time
fmt.Println(cast.ToString(nil)) // ""
var foo interface{} = "one more time"
fmt.Println(cast.ToString(foo)) // one more time
// ToInt
fmt.Println(cast.ToInt(8)) // 8
fmt.Println(cast.ToInt(8.31)) // 8
fmt.Println(cast.ToInt("8")) // 8
fmt.Println(cast.ToInt(true)) // 1
fmt.Println(cast.ToInt(false)) // 0
var eight interface{} = 8
fmt.Println(cast.ToInt(eight)) // 8
fmt.Println(cast.ToInt(nil)) // 0
}
实际上,cast实现了多种常见类型之间的相互转换,返回最符合直觉的结果。例如:
nil转为string的结果为"",而不是"nil";true转为string的结果为"true",而true转为int的结果为1;interface{}转为其他类型,要看它里面存储的值类型。
这些类型包括所有的基本类型(整形、浮点型、布尔值和字符串)、空接口、nil,时间(time.Time)、时长(time.Duration)以及它们的切片类型,还有map[string]Type(其中Type为前面提到的类型):
byte bool float32 float64 string
int8 int16 int32 int64 int
uint8 uint16 uint32 uint64 uint
interface{} time.Time time.Duration nil
cast提供了两组函数:
ToType(其中Type可以为任何支持的类型),将参数转换为Type类型。如果无法转换,返回Type类型的零值或nil;ToTypeE以 E 结尾,返回转换后的值和一个error。这组函数可以区分参数中实际存储了零值,还是转换失败了。
实现上大部分代码都类似,ToType在内部调用ToTypeE函数,返回结果并忽略错误。ToType函数的实现在文件cast.go中,而ToTypeE函数的实现在文件caste.go中。
部分源码如下:
// ToBoolE casts an interface to a bool type.
func ToBool(i interface{}) bool {
v, _ := ToBoolE(i)
return v
}
// ToDuration casts an interface to a time.Duration type.
func ToDuration(i interface{}) time.Duration {
v, _ := ToDurationE(i)
return v
}
ToTypeE函数都接受任意类型的参数(interface{}),然后使用类型断言根据具体的类型来执行不同的转换。如果无法转换,返回错误。
// ToBoolE casts an interface to a bool type.
func ToBoolE(i interface{}) (bool, error) {
i = indirect(i)
switch b := i.(type) {
case bool:
return b, nil
case nil:
return false, nil
case int:
if i.(int) != 0 {
return true, nil
}
return false, nil
case string:
return strconv.ParseBool(i.(string))
case json.Number:
v, err := ToInt64E(b)
if err == nil {
return v != 0, nil
}
return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i)
default:
return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i)
}
}
首先调用indirect函数将参数中可能的指针去掉。如果类型本身不是指针,那么直接返回。否则返回指针指向的值。循环直到返回一个非指针的值:
// From html/template/content.go
// Copyright 2011 The Go Authors. All rights reserved.
// indirect returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil).
func indirect(a interface{}) interface{} {
if a == nil {
return nil
}
if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
// Avoid creating a reflect.Value if it's not a pointer.
return a
}
v := reflect.ValueOf(a)
for v.Kind() == reflect.Ptr && !v.IsNil() {
v = v.Elem()
}
return v.Interface()
}
所以,下面代码输出都是 8:
package main
import (
"fmt"
"github.com/spf13/cast"
)
func main() {
p := new(int)
*p = 8
fmt.Println(cast.ToInt(p)) // 8
pp := &p
fmt.Println(cast.ToInt(pp)) // 8
}
源码如下:
// ToTimeE casts an interface to a time.Time type.
func ToTimeE(i interface{}) (tim time.Time, err error) {
return ToTimeInDefaultLocationE(i, time.UTC)
}
// ToTimeInDefaultLocationE casts an empty interface to time.Time,
// interpreting inputs without a timezone to be in the given location,
// or the local timezone if nil.
func ToTimeInDefaultLocationE(i interface{}, location *time.Location) (tim time.Time, err error) {
i = indirect(i)
switch v := i.(type) {
case time.Time:
return v, nil
case string:
return StringToDateInDefaultLocation(v, location)
case json.Number:
s, err1 := ToInt64E(v)
if err1 != nil {
return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i)
}
return time.Unix(s, 0), nil
case int:
return time.Unix(int64(v), 0), nil
case int64:
return time.Unix(v, 0), nil
case int32:
return time.Unix(int64(v), 0), nil
case uint:
return time.Unix(int64(v), 0), nil
case uint64:
return time.Unix(int64(v), 0), nil
case uint32:
return time.Unix(int64(v), 0), nil
default:
return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i)
}
}
根据传入的类型执行不同的处理:
- 如果是
time.Time,直接返回; - 如果是整型,将参数作为时间戳(自
UTC时间1970.01.01 00:00:00到现在的秒数)调用time.Unix生成时间。Unix接受两个参数,第一个参数指定秒,第二个参数指定纳秒; - 如果是字符串,调用
StringToDate函数依次尝试以下面这些时间格式调用time.Parse解析该字符串。如果某个格式解析成功,则返回获得的time.Time。否则解析失败,返回错误; - 其他任何类型都无法转换为
time.Time。
源码:
// cast/caste.go
func StringToDate(s string) (time.Time, error) {
return parseDateWith(s, []string{
time.RFC3339,
"2006-01-02T15:04:05", // iso8601 without timezone
time.RFC1123Z,
time.RFC1123,
time.RFC822Z,
time.RFC822,
time.RFC850,
time.ANSIC,
time.UnixDate,
time.RubyDate,
"2006-01-02 15:04:05.999999999 -0700 MST", // Time.String()
"2006-01-02",
"02 Jan 2006",
"2006-01-02T15:04:05-0700", // RFC3339 without timezone hh:mm colon
"2006-01-02 15:04:05 -07:00",
"2006-01-02 15:04:05 -0700",
"2006-01-02 15:04:05Z07:00", // RFC3339 without T
"2006-01-02 15:04:05Z0700", // RFC3339 without T or timezone hh:mm colon
"2006-01-02 15:04:05",
time.Kitchen,
time.Stamp,
time.StampMilli,
time.StampMicro,
time.StampNano,
})
}
func parseDateWith(s string, dates []string) (d time.Time, e error) {
for _, dateType := range dates {
if d, e = time.Parse(dateType, s); e == nil {
return
}
}
return d, fmt.Errorf("unable to parse date: %s", s)
}
源码如下:
// cast/caste.go
func ToDurationE(i interface{}) (d time.Duration, err error) {
i = indirect(i)
switch s := i.(type) {
case time.Duration:
return s, nil
case int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8:
d = time.Duration(ToInt64(s))
return
case float32, float64:
d = time.Duration(ToFloat64(s))
return
case string:
if strings.ContainsAny(s, "nsuµmh") {
d, err = time.ParseDuration(s)
} else {
d, err = time.ParseDuration(s "ns")
}
return
default:
err = fmt.Errorf("unable to cast %#v of type %T to Duration", i, i)
return
}
}
根据传入的类型进行不同的处理:
- 如果是
time.Duration类型,直接返回; - 如果是整型或浮点型,将其数值强制转换为
time.Duration类型,单位默认为ns; - 如果是字符串,分为两种情况:如果字符串中有时间单位符号
nsuµmh,直接调用time.ParseDuration解析;否则在字符串后拼接ns再调用time.ParseDuration解析; - 其他类型解析失败。
示例:
package main
import (
"fmt"
"time"
"github.com/spf13/cast"
)
func main() {
now := time.Now()
timestamp := 1579615973
timeStr := "2020-01-21 22:13:48"
fmt.Println(cast.ToTime(now)) // 2020-01-22 06:31:50.5068465 0800 CST m= 0.000997701
fmt.Println(cast.ToTime(timestamp)) // 2020-01-21 22:12:53 0800 CST
fmt.Println(cast.ToTime(timeStr)) // 2020-01-21 22:13:48 0000 UTC
d, _ := time.ParseDuration("1m30s")
ns := 30000
strWithUnit := "130s"
strWithoutUnit := "130"
fmt.Println(cast.ToDuration(d)) // 1m30s
fmt.Println(cast.ToDuration(ns)) // 30µs
fmt.Println(cast.ToDuration(strWithUnit)) // 2m10s
fmt.Println(cast.ToDuration(strWithoutUnit)) // 130ns
}
实际上,这些函数的实现基本类似。使用类型断言判断类型。如果就是要返回的类型,直接返回。否则根据类型进行相应的转换。
我们主要分析两个实现:ToIntSliceE和ToStringSliceE。ToBoolSliceE/ToDurationSliceE与ToIntSliceE基本相同。
源码如下:
func ToIntSliceE(i interface{}) ([]int, error) {
if i == nil {
return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i)
}
switch v := i.(type) {
case []int:
return v, nil
}
kind := reflect.TypeOf(i).Kind()
switch kind {
case reflect.Slice, reflect.Array:
s := reflect.ValueOf(i)
a := make([]int, s.Len())
for j := 0; j < s.Len(); j {
val, err := ToIntE(s.Index(j).Interface())
if err != nil {
return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i)
}
a[j] = val
}
return a, nil
default:
return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i)
}
}
根据传入参数的类型:
- 如果是
nil,直接返回错误; - 如果是
[]int,不用转换,直接返回; - 如果传入类型为切片或数组,新建一个
[]int,将切片或数组中的每个元素转为int放到该[]int中。最后返回这个[]int; - 其他情况,不能转换。
源码如下:
func ToStringSliceE(i interface{}) ([]string, error) {
var a []string
switch v := i.(type) {
case []interface{}:
for _, u := range v {
a = append(a, ToString(u))
}
return a, nil
case []string:
return v, nil
case string:
return strings.Fields(v), nil
case interface{}:
str, err := ToStringE(v)
if err != nil {
return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i)
}
return []string{str}, nil
default:
return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i)
}
}
根据传入的参数类型:
- 如果是
[]interface{},将该参数中每个元素转为string,返回结果切片; - 如果是
[]string,不需要转换,直接返回; - 如果是
interface{},将参数转为string,返回只包含这个值的切片; - 如果是
string,调用strings.Fields函数按空白符将参数拆分,返回拆分后的字符串切片; - 其他情况,不能转换。
示例:
package main
import (
"fmt"
"github.com/spf13/cast"
)
func main() {
sliceOfInt := []int{1, 3, 7}
arrayOfInt := [3]int{8, 12}
// ToIntSlice
fmt.Println(cast.ToIntSlice(sliceOfInt)) // [1 3 7]
fmt.Println(cast.ToIntSlice(arrayOfInt)) // [8 12 0]
sliceOfInterface := []interface{}{1, 2.0, "darjun"}
sliceOfString := []string{"abc", "dj", "pipi"}
stringFields := " abc def hij "
any := interface{}(37)
// ToStringSliceE
fmt.Println(cast.ToStringSlice(sliceOfInterface)) // [1 2 darjun]
fmt.Println(cast.ToStringSlice(sliceOfString)) // [abc dj pipi]
fmt.Println(cast.ToStringSlice(stringFields)) // [abc def hij]
fmt.Println(cast.ToStringSlice(any)) // [37]
}
cast库能将传入的参数转为map[string]Type类型,Type为上面支持的类型。
其实只需要分析一个ToStringMapStringE函数就可以了,其他的实现基本一样。ToStringMapStringE返回map[string]string类型的值。
源码如下:
func ToStringMapStringE(i interface{}) (map[string]string, error) {
var m = map[string]string{}
switch v := i.(type) {
case map[string]string:
return v, nil
case map[string]interface{}:
for k, val := range v {
m[ToString(k)] = ToString(val)
}
return m, nil
case map[interface{}]string:
for k, val := range v {
m[ToString(k)] = ToString(val)
}
return m, nil
case map[interface{}]interface{}:
for k, val := range v {
m[ToString(k)] = ToString(val)
}
return m, nil
case string:
err := jsonStringToObject(v, &m)
return m, err
default:
return m, fmt.Errorf("unable to cast %#v of type %T to map[string]string", i, i)
}
}
根据传入的参数类型:
- 如果是
map[string]string,不用转换,直接返回; - 如果是
map[string]interface{},将每个值转为string存入新的 map,最后返回新的 map; - 如果是
map[interface{}]string,将每个键转为string存入新的 map,最后返回新的 map; - 如果是
map[interface{}]interface{},将每个键和值都转为string存入新的 map,最后返回新的 map; - 如果是
string类型,cast将它看成一个 JSON 串,解析这个 JSON 到map[string]string,然后返回结果; - 其他情况,返回错误。
示例:
package main
import (
"fmt"
"github.com/spf13/cast"
)
func main() {
m1 := map[string]string {
"name": "darjun",
"job": "developer",
}
m2 := map[string]interface{} {
"name": "jingwen",
"age": 18,
}
m3 := map[interface{}]string {
"name": "pipi",
"job": "designer",
}
m4 := map[interface{}]interface{} {
"name": "did",
"age": 29,
}
jsonStr := `{"name":"bibi", "job":"manager"}`
fmt.Println(cast.ToStringMapString(m1)) // map[job:developer name:darjun]
fmt.Println(cast.ToStringMapString(m2)) // map[age:18 name:jingwen]
fmt.Println(cast.ToStringMapString(m3)) // map[job:designer name:pipi]
fmt.Println(cast.ToStringMapString(m4)) // map[job:designer name:pipi]
fmt.Println(cast.ToStringMapString(jsonStr)) // map[job:manager name:bibi]
}
到此这篇关于Go语言类型转换工具库cast的实现的文章就介绍到这了,更多相关Go语言类型转换工具库cast内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!
您可能感兴趣的文章:
- Golang中类型转换利器cast库的用法详解
- 一文带你了解Golang中类型转换库cast的使用
- Golang 语言极简类型转换库cast的使用详解
- golang类型转换组件Cast的使用详解