Go语言类型转换工具库cast的实现

作者:

文章目录
  • cast可以在 Go 中轻松安全地从一种类型转换为另一种类型,cast 提供了简单的函数来轻松地将数字转换为字符串,将接口转换为布尔值等。当需要显示类型转换时,cast 会智能地执行转换操作。
  • 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 }
  • 实际上,这些函数的实现基本类似。使用类型断言判断类型。如果就是要返回的类型,直接返回。否则根据类型进行相应的转换。 我们主要分析两个实现:ToIntSliceE和ToStringSliceE。ToBoolSliceE/ToDurationSliceE与ToIntSliceE基本相同。
  • 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的使用详解
  • 目录
    • 简介
    • 快速入门
      • 安装
      • 使用
    • 高级转换
      • 时间和时长转换
        • 时间类型的转换
        • 字符串转换为时间
        • 时长类型的转换
      • 转换为切片
        • 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
        }
        

        实际上,这些函数的实现基本类似。使用类型断言判断类型。如果就是要返回的类型,直接返回。否则根据类型进行相应的转换。

        我们主要分析两个实现:ToIntSliceEToStringSliceEToBoolSliceE/ToDurationSliceEToIntSliceE基本相同。

        源码如下:

        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的使用详解

        站内搜索