Starting from version v2.6.2 of the framework, the conversion component provides the Converter feature, allowing developers to customize conversion methods to specify conversion logic between specific types.

Conversion Method Definition

The conversion method is defined as follows:

  1. func(T1) (T2, error)

Where T1 needs to be a non-pointer object, and T2 needs to be a pointer type. If the types are incorrect, the registration of the conversion method will result in an error.

Type Conversion - Converter - 图1tip

The design requiring the input parameter (T1) to be a non-pointer object aims to ensure the safety of input parameters, minimizing potential issues outside the scope of the conversion method.

The function to register a conversion method is as follows:

  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)

Struct Type Conversion

A common custom data structure conversion involves conversion between structs. Let’s look at two examples.

  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. }

In this example code, two conversion scenarios are demonstrated: custom struct conversion and automatic conversion of structs as attributes. The conversion method used is the generic struct conversion method gconv.Scan. The internal implementation will automatically determine if a custom type conversion function exists and prioritize its use; otherwise, the default conversion logic will be used.

Upon execution, the terminal outputs:

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

In addition to using the gconv.Scan method, we can also use the gconv.ConvertWithRefer method to achieve type conversion, both of which have the same effect:

  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. }

Alias Type Conversion

We can also use the Converter feature to implement conversions for alias types. Alias types are not limited to structs and can also be aliases for basic types such as int, string, etc. Here are two examples.

  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. }

The type xxx = yyy in the code is due to the needs of the *gtime.Time type. Other types can choose whether to use the alias symbol = as needed. For example, basic types such as int, string do not need the alias symbol.

Upon execution, the terminal outputs:

  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}}

Similarly, in addition to using the gconv.Scan method, we can also use the gconv.ConvertWithRefer method to achieve type conversion, both of which have the same effect:

  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. }