Введение
Сегодня мы познакомим вас с еще одной библиотекой spf13 God.cast.cast
Небольшая практичная библиотека преобразования типов для преобразования одного типа в другой.
Первоначально разработанcast
используется вhugoсередина.
быстрый в использовании
Сначала установите:
$ 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
середина.
// cast/cast.go
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{}
), а затем используйте утверждения типа для выполнения различных преобразований в зависимости от конкретного типа. Возвращает ошибку, если он не может быть преобразован.
// cast/caste.go
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))
default:
return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i)
}
}
первый звонокindirect
Функция удаляет из аргументов возможные указатели. Если сам тип не является указателем, возвращайтесь напрямую. В противном случае возвращает значение, на которое указывает указатель.
Цикл, пока не будет возвращено значение, не являющееся указателем:
// cast/caste.go
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
}
Преобразование времени и продолжительности
Код преобразования для типа времени выглядит следующим образом:
func ToTimeE(i interface{}) (tim time.Time, err error) {
i = indirect(i)
switch v := i.(type) {
case time.Time:
return v, nil
case string:
return StringToDate(v)
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
в основном то же самое.
прежде всего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
; - В других случаях он не может быть преобразован.
ToStringSliceE
:
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]
}
Перевести вmap[string]Type
Типы
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[interface{}]string
, превращая каждый ключ вstring
Сохраните новую карту и, наконец, верните новую карту; - если
map[interface{}]interface{}
, преобразует каждый ключ и значение вstring
Сохраните новую карту и, наконец, верните новую карту; -
если
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]
}
Суммировать
cast
Библиотека умеет конвертировать почти все распространенные типы, что очень удобно в использовании. Объем кода также невелик, и рекомендуется читать исходный код, когда у вас есть время.
Полный пример кода находится по адресуGitHubначальство.
ps: Я думал, что Праздник Весны всего несколько дней, мне было лень, но я не ожидал. . .
Ссылаться на
- castРепозиторий GitHub
я
Добро пожаловать, чтобы обратить внимание на мою общедоступную учетную запись WeChat [GoUpUp], учитесь вместе и добивайтесь прогресса вместе ~
Эта статья опубликована в блогеOpenWriteвыпускать!