gconv_structs.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. // Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. package gconv
  7. import (
  8. "github.com/gogf/gf/errors/gerror"
  9. "github.com/gogf/gf/internal/json"
  10. "reflect"
  11. )
  12. // Structs converts any slice to given struct slice.
  13. func Structs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
  14. return doStructs(params, pointer, mapping...)
  15. }
  16. // StructsDeep converts any slice to given struct slice recursively.
  17. // Deprecated, use Structs instead.
  18. func StructsDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
  19. return doStructs(params, pointer, mapping...)
  20. }
  21. // doStructs converts any slice to given struct slice.
  22. //
  23. // It automatically checks and converts json string to []map if <params> is string/[]byte.
  24. //
  25. // The parameter <pointer> should be type of pointer to slice of struct.
  26. // Note that if <pointer> is a pointer to another pointer of type of slice of struct,
  27. // it will create the struct/pointer internally.
  28. func doStructs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
  29. if params == nil {
  30. // If <params> is nil, no conversion.
  31. return nil
  32. }
  33. if pointer == nil {
  34. return gerror.New("object pointer cannot be nil")
  35. }
  36. if doStructsByDirectReflectSet(params, pointer) {
  37. return nil
  38. }
  39. defer func() {
  40. // Catch the panic, especially the reflect operation panics.
  41. if exception := recover(); exception != nil {
  42. if e, ok := exception.(errorStack); ok {
  43. err = e
  44. } else {
  45. err = gerror.NewSkipf(1, "%v", exception)
  46. }
  47. }
  48. }()
  49. // If given <params> is JSON, it then uses json.Unmarshal doing the converting.
  50. switch r := params.(type) {
  51. case []byte:
  52. if json.Valid(r) {
  53. if rv, ok := pointer.(reflect.Value); ok {
  54. if rv.Kind() == reflect.Ptr {
  55. return json.Unmarshal(r, rv.Interface())
  56. }
  57. } else {
  58. return json.Unmarshal(r, pointer)
  59. }
  60. }
  61. case string:
  62. if paramsBytes := []byte(r); json.Valid(paramsBytes) {
  63. if rv, ok := pointer.(reflect.Value); ok {
  64. if rv.Kind() == reflect.Ptr {
  65. return json.Unmarshal(paramsBytes, rv.Interface())
  66. }
  67. } else {
  68. return json.Unmarshal(paramsBytes, pointer)
  69. }
  70. }
  71. }
  72. // Pointer type check.
  73. pointerRv, ok := pointer.(reflect.Value)
  74. if !ok {
  75. pointerRv = reflect.ValueOf(pointer)
  76. if kind := pointerRv.Kind(); kind != reflect.Ptr {
  77. return gerror.Newf("pointer should be type of pointer, but got: %v", kind)
  78. }
  79. }
  80. // Converting <params> to map slice.
  81. paramsMaps := Maps(params)
  82. // If <params> is an empty slice, no conversion.
  83. if len(paramsMaps) == 0 {
  84. return nil
  85. }
  86. var (
  87. array = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsMaps), len(paramsMaps))
  88. itemType = array.Index(0).Type()
  89. )
  90. for i := 0; i < len(paramsMaps); i++ {
  91. if itemType.Kind() == reflect.Ptr {
  92. // Slice element is type pointer.
  93. e := reflect.New(itemType.Elem()).Elem()
  94. if err = Struct(paramsMaps[i], e, mapping...); err != nil {
  95. return err
  96. }
  97. array.Index(i).Set(e.Addr())
  98. } else {
  99. // Slice element is not type of pointer.
  100. e := reflect.New(itemType).Elem()
  101. if err = Struct(paramsMaps[i], e, mapping...); err != nil {
  102. return err
  103. }
  104. array.Index(i).Set(e)
  105. }
  106. }
  107. pointerRv.Elem().Set(array)
  108. return nil
  109. }
  110. // doStructsByDirectReflectSet do the converting directly using reflect Set.
  111. // It returns true if success, or else false.
  112. func doStructsByDirectReflectSet(params interface{}, pointer interface{}) (ok bool) {
  113. v1 := reflect.ValueOf(pointer)
  114. v2 := reflect.ValueOf(params)
  115. if v1.Kind() == reflect.Ptr {
  116. if elem := v1.Elem(); elem.IsValid() && elem.Type() == v2.Type() {
  117. elem.Set(v2)
  118. ok = true
  119. }
  120. }
  121. return ok
  122. }