从框架 v2.6.2 版本开始,转换组件提供了 Converter 特性,允许开发者可以自定义 Converter转换方法指定特定类型之间的转换逻辑。

转换方法定义

转换方法定义如下:

  1. func(T1) (T2, error)

其中 T1 需要为非指针对象, T2 需要为指针类型,如果类型不正确转换方法注册将会报错。

类型转换-Converter特性 - 图1提示

输入参数( T1)必须为非指针对象的设计,目的是为了保证输入参数的安全,尽可能避免在转换方法内部修改输入参数引起作用域外问题。

注册转换方法的函数如下:

  1. // RegisterConverter to register custom converter.
  2. // It must be registered before you use this custom converting feature.
  3. // It is suggested to do it in boot procedure of the process.
  4. //
  5. // Note:
  6. // 1. The parameter `fn` must be defined as pattern `func(T1) (T2, error)`.
  7. // It will convert type `T1` to type `T2`.
  8. // 2. The `T1` should not be type of pointer, but the `T2` should be type of pointer.
  9. func RegisterConverter(fn interface{}) (err error)

结构体类型转换

常见的自定义数据结构是结构体之间的类型转换。我们来看两个例子。

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/v2/util/gconv"
  5. )
  6. type Src struct {
  7. A int
  8. }
  9. type Dst struct {
  10. B int
  11. }
  12. type SrcWrap struct {
  13. Value Src
  14. }
  15. type DstWrap struct {
  16. Value Dst
  17. }
  18. func SrcToDstConverter(src Src) (dst *Dst, err error) {
  19. return &Dst{B: src.A}, nil
  20. }
  21. // SrcToDstConverter is custom converting function for custom type.
  22. func main() {
  23. // register custom converter function.
  24. err := gconv.RegisterConverter(SrcToDstConverter)
  25. if err != nil {
  26. panic(err)
  27. }
  28. // custom struct converting.
  29. var (
  30. src = Src{A: 1}
  31. dst *Dst
  32. )
  33. err = gconv.Scan(src, &dst)
  34. if err != nil {
  35. panic(err)
  36. }
  37. fmt.Println("src:", src)
  38. fmt.Println("dst:", dst)
  39. // custom struct attributes converting.
  40. var (
  41. srcWrap = SrcWrap{Src{A: 1}}
  42. dstWrap *DstWrap
  43. )
  44. err = gconv.Scan(srcWrap, &dstWrap)
  45. if err != nil {
  46. panic(err)
  47. }
  48. fmt.Println("srcWrap:", srcWrap)
  49. fmt.Println("dstWrap:", dstWrap)
  50. }

在该示例代码中,演示了两种类型转换场景:自定义结构体转换,以及结构体作为属性的自动转换。转换方法使用的是通用的结构体转换方法 gconv.Scan,该方法在内部实现中会自动判断如果存在自定义类型转换函数,会优先使用自定义类型转换函数,否则会走默认的转换逻辑。

执行后,终端输出:

  1. src: {1}
  2. dst: &{1}
  3. srcWrap: {{1}}
  4. dstWrap: &{{1}}

除了使用 gconv.Scan 方法外,我们也可以使 gconv.ConvertWithRefer 方法实现类型转换,两者的效果都是一样的:

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/v2/util/gconv"
  5. )
  6. type Src struct {
  7. A int
  8. }
  9. type Dst struct {
  10. B int
  11. }
  12. type SrcWrap struct {
  13. Value Src
  14. }
  15. type DstWrap struct {
  16. Value Dst
  17. }
  18. // SrcToDstConverter is custom converting function for custom type.
  19. func SrcToDstConverter(src Src) (dst *Dst, err error) {
  20. return &Dst{B: src.A}, nil
  21. }
  22. func main() {
  23. // register custom converter function.
  24. err := gconv.RegisterConverter(SrcToDstConverter)
  25. if err != nil {
  26. panic(err)
  27. }
  28. // custom struct converting.
  29. var src = Src{A: 1}
  30. dst := gconv.ConvertWithRefer(src, Dst{})
  31. fmt.Println("src:", src)
  32. fmt.Println("dst:", dst)
  33. // custom struct attributes converting.
  34. var srcWrap = SrcWrap{Src{A: 1}}
  35. dstWrap := gconv.ConvertWithRefer(srcWrap, &DstWrap{})
  36. fmt.Println("srcWrap:", srcWrap)
  37. fmt.Println("dstWrap:", dstWrap)
  38. }

别名类型转换

我们也可以使用 Converter特性实现 别名类型 的转换。别名类型不限于结构体,也可以是 int, string 等基础类型的别名。我们来看两个例子。

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/v2/os/gtime"
  5. "github.com/gogf/gf/v2/util/gconv"
  6. )
  7. type MyTime = *gtime.Time
  8. type Src struct {
  9. A MyTime
  10. }
  11. type Dst struct {
  12. B string
  13. }
  14. type SrcWrap struct {
  15. Value Src
  16. }
  17. type DstWrap struct {
  18. Value Dst
  19. }
  20. // SrcToDstConverter is custom converting function for custom type.
  21. func SrcToDstConverter(src Src) (dst *Dst, err error) {
  22. return &Dst{B: src.A.Format("Y-m-d")}, nil
  23. }
  24. // SrcToDstConverter is custom converting function for custom type.
  25. func main() {
  26. // register custom converter function.
  27. err := gconv.RegisterConverter(SrcToDstConverter)
  28. if err != nil {
  29. panic(err)
  30. }
  31. // custom struct converting.
  32. var (
  33. src = Src{A: gtime.Now()}
  34. dst *Dst
  35. )
  36. err = gconv.Scan(src, &dst)
  37. if err != nil {
  38. panic(err)
  39. }
  40. fmt.Println("src:", src)
  41. fmt.Println("dst:", dst)
  42. // custom struct attributes converting.
  43. var (
  44. srcWrap = SrcWrap{Src{A: gtime.Now()}}
  45. dstWrap *DstWrap
  46. )
  47. err = gconv.Scan(srcWrap, &dstWrap)
  48. if err != nil {
  49. panic(err)
  50. }
  51. fmt.Println("srcWrap:", srcWrap)
  52. fmt.Println("dstWrap:", dstWrap)
  53. }

代码中的 type xxx = yyy是由于 *gtime.Time 类型的需要,其他类型可根据需要是否使用别名符号 =,例如基础类型 int, string 等是不需要别名符号的。

执行后,终端输出:

  1. src: {2024-01-22 21:45:28}
  2. dst: &{2024-01-22}
  3. srcWrap: {{2024-01-22 21:45:28}}
  4. dstWrap: &{{2024-01-22}}

同样的,除了使用 gconv.Scan 方法外,我们也可以使用 gconv.ConvertWithRefer 方法实现类型转换,两者的效果都是一样的:

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/v2/os/gtime"
  5. "github.com/gogf/gf/v2/util/gconv"
  6. )
  7. type MyTime = *gtime.Time
  8. type Src struct {
  9. A MyTime
  10. }
  11. type Dst struct {
  12. B string
  13. }
  14. type SrcWrap struct {
  15. Value Src
  16. }
  17. type DstWrap struct {
  18. Value Dst
  19. }
  20. // SrcToDstConverter is custom converting function for custom type.
  21. func SrcToDstConverter(src Src) (dst *Dst, err error) {
  22. return &Dst{B: src.A.Format("Y-m-d")}, nil
  23. }
  24. // SrcToDstConverter is custom converting function for custom type.
  25. func main() {
  26. // register custom converter function.
  27. err := gconv.RegisterConverter(SrcToDstConverter)
  28. if err != nil {
  29. panic(err)
  30. }
  31. // custom struct converting.
  32. var src = Src{A: gtime.Now()}
  33. dst := gconv.ConvertWithRefer(src, &Dst{})
  34. fmt.Println("src:", src)
  35. fmt.Println("dst:", dst)
  36. // custom struct attributes converting.
  37. var srcWrap = SrcWrap{Src{A: gtime.Now()}}
  38. dstWrap := gconv.ConvertWithRefer(srcWrap, &DstWrap{})
  39. fmt.Println("srcWrap:", srcWrap)
  40. fmt.Println("dstWrap:", dstWrap)
  41. }