conversions.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. package objx
  2. import (
  3. "bytes"
  4. "encoding/base64"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "net/url"
  9. "strconv"
  10. )
  11. // SignatureSeparator is the character that is used to
  12. // separate the Base64 string from the security signature.
  13. const SignatureSeparator = "_"
  14. // URLValuesSliceKeySuffix is the character that is used to
  15. // specify a suffix for slices parsed by URLValues.
  16. // If the suffix is set to "[i]", then the index of the slice
  17. // is used in place of i
  18. // Ex: Suffix "[]" would have the form a[]=b&a[]=c
  19. // OR Suffix "[i]" would have the form a[0]=b&a[1]=c
  20. // OR Suffix "" would have the form a=b&a=c
  21. var urlValuesSliceKeySuffix = "[]"
  22. const (
  23. URLValuesSliceKeySuffixEmpty = ""
  24. URLValuesSliceKeySuffixArray = "[]"
  25. URLValuesSliceKeySuffixIndex = "[i]"
  26. )
  27. // SetURLValuesSliceKeySuffix sets the character that is used to
  28. // specify a suffix for slices parsed by URLValues.
  29. // If the suffix is set to "[i]", then the index of the slice
  30. // is used in place of i
  31. // Ex: Suffix "[]" would have the form a[]=b&a[]=c
  32. // OR Suffix "[i]" would have the form a[0]=b&a[1]=c
  33. // OR Suffix "" would have the form a=b&a=c
  34. func SetURLValuesSliceKeySuffix(s string) error {
  35. if s == URLValuesSliceKeySuffixEmpty || s == URLValuesSliceKeySuffixArray || s == URLValuesSliceKeySuffixIndex {
  36. urlValuesSliceKeySuffix = s
  37. return nil
  38. }
  39. return errors.New("objx: Invalid URLValuesSliceKeySuffix provided.")
  40. }
  41. // JSON converts the contained object to a JSON string
  42. // representation
  43. func (m Map) JSON() (string, error) {
  44. for k, v := range m {
  45. m[k] = cleanUp(v)
  46. }
  47. result, err := json.Marshal(m)
  48. if err != nil {
  49. err = errors.New("objx: JSON encode failed with: " + err.Error())
  50. }
  51. return string(result), err
  52. }
  53. func cleanUpInterfaceArray(in []interface{}) []interface{} {
  54. result := make([]interface{}, len(in))
  55. for i, v := range in {
  56. result[i] = cleanUp(v)
  57. }
  58. return result
  59. }
  60. func cleanUpInterfaceMap(in map[interface{}]interface{}) Map {
  61. result := Map{}
  62. for k, v := range in {
  63. result[fmt.Sprintf("%v", k)] = cleanUp(v)
  64. }
  65. return result
  66. }
  67. func cleanUpStringMap(in map[string]interface{}) Map {
  68. result := Map{}
  69. for k, v := range in {
  70. result[k] = cleanUp(v)
  71. }
  72. return result
  73. }
  74. func cleanUpMSIArray(in []map[string]interface{}) []Map {
  75. result := make([]Map, len(in))
  76. for i, v := range in {
  77. result[i] = cleanUpStringMap(v)
  78. }
  79. return result
  80. }
  81. func cleanUpMapArray(in []Map) []Map {
  82. result := make([]Map, len(in))
  83. for i, v := range in {
  84. result[i] = cleanUpStringMap(v)
  85. }
  86. return result
  87. }
  88. func cleanUp(v interface{}) interface{} {
  89. switch v := v.(type) {
  90. case []interface{}:
  91. return cleanUpInterfaceArray(v)
  92. case []map[string]interface{}:
  93. return cleanUpMSIArray(v)
  94. case map[interface{}]interface{}:
  95. return cleanUpInterfaceMap(v)
  96. case Map:
  97. return cleanUpStringMap(v)
  98. case []Map:
  99. return cleanUpMapArray(v)
  100. default:
  101. return v
  102. }
  103. }
  104. // MustJSON converts the contained object to a JSON string
  105. // representation and panics if there is an error
  106. func (m Map) MustJSON() string {
  107. result, err := m.JSON()
  108. if err != nil {
  109. panic(err.Error())
  110. }
  111. return result
  112. }
  113. // Base64 converts the contained object to a Base64 string
  114. // representation of the JSON string representation
  115. func (m Map) Base64() (string, error) {
  116. var buf bytes.Buffer
  117. jsonData, err := m.JSON()
  118. if err != nil {
  119. return "", err
  120. }
  121. encoder := base64.NewEncoder(base64.StdEncoding, &buf)
  122. _, _ = encoder.Write([]byte(jsonData))
  123. _ = encoder.Close()
  124. return buf.String(), nil
  125. }
  126. // MustBase64 converts the contained object to a Base64 string
  127. // representation of the JSON string representation and panics
  128. // if there is an error
  129. func (m Map) MustBase64() string {
  130. result, err := m.Base64()
  131. if err != nil {
  132. panic(err.Error())
  133. }
  134. return result
  135. }
  136. // SignedBase64 converts the contained object to a Base64 string
  137. // representation of the JSON string representation and signs it
  138. // using the provided key.
  139. func (m Map) SignedBase64(key string) (string, error) {
  140. base64, err := m.Base64()
  141. if err != nil {
  142. return "", err
  143. }
  144. sig := HashWithKey(base64, key)
  145. return base64 + SignatureSeparator + sig, nil
  146. }
  147. // MustSignedBase64 converts the contained object to a Base64 string
  148. // representation of the JSON string representation and signs it
  149. // using the provided key and panics if there is an error
  150. func (m Map) MustSignedBase64(key string) string {
  151. result, err := m.SignedBase64(key)
  152. if err != nil {
  153. panic(err.Error())
  154. }
  155. return result
  156. }
  157. /*
  158. URL Query
  159. ------------------------------------------------
  160. */
  161. // URLValues creates a url.Values object from an Obj. This
  162. // function requires that the wrapped object be a map[string]interface{}
  163. func (m Map) URLValues() url.Values {
  164. vals := make(url.Values)
  165. m.parseURLValues(m, vals, "")
  166. return vals
  167. }
  168. func (m Map) parseURLValues(queryMap Map, vals url.Values, key string) {
  169. useSliceIndex := false
  170. if urlValuesSliceKeySuffix == "[i]" {
  171. useSliceIndex = true
  172. }
  173. for k, v := range queryMap {
  174. val := &Value{data: v}
  175. switch {
  176. case val.IsObjxMap():
  177. if key == "" {
  178. m.parseURLValues(val.ObjxMap(), vals, k)
  179. } else {
  180. m.parseURLValues(val.ObjxMap(), vals, key+"["+k+"]")
  181. }
  182. case val.IsObjxMapSlice():
  183. sliceKey := k
  184. if key != "" {
  185. sliceKey = key + "[" + k + "]"
  186. }
  187. if useSliceIndex {
  188. for i, sv := range val.MustObjxMapSlice() {
  189. sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]"
  190. m.parseURLValues(sv, vals, sk)
  191. }
  192. } else {
  193. sliceKey = sliceKey + urlValuesSliceKeySuffix
  194. for _, sv := range val.MustObjxMapSlice() {
  195. m.parseURLValues(sv, vals, sliceKey)
  196. }
  197. }
  198. case val.IsMSISlice():
  199. sliceKey := k
  200. if key != "" {
  201. sliceKey = key + "[" + k + "]"
  202. }
  203. if useSliceIndex {
  204. for i, sv := range val.MustMSISlice() {
  205. sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]"
  206. m.parseURLValues(New(sv), vals, sk)
  207. }
  208. } else {
  209. sliceKey = sliceKey + urlValuesSliceKeySuffix
  210. for _, sv := range val.MustMSISlice() {
  211. m.parseURLValues(New(sv), vals, sliceKey)
  212. }
  213. }
  214. case val.IsStrSlice(), val.IsBoolSlice(),
  215. val.IsFloat32Slice(), val.IsFloat64Slice(),
  216. val.IsIntSlice(), val.IsInt8Slice(), val.IsInt16Slice(), val.IsInt32Slice(), val.IsInt64Slice(),
  217. val.IsUintSlice(), val.IsUint8Slice(), val.IsUint16Slice(), val.IsUint32Slice(), val.IsUint64Slice():
  218. sliceKey := k
  219. if key != "" {
  220. sliceKey = key + "[" + k + "]"
  221. }
  222. if useSliceIndex {
  223. for i, sv := range val.StringSlice() {
  224. sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]"
  225. vals.Set(sk, sv)
  226. }
  227. } else {
  228. sliceKey = sliceKey + urlValuesSliceKeySuffix
  229. vals[sliceKey] = val.StringSlice()
  230. }
  231. default:
  232. if key == "" {
  233. vals.Set(k, val.String())
  234. } else {
  235. vals.Set(key+"["+k+"]", val.String())
  236. }
  237. }
  238. }
  239. }
  240. // URLQuery gets an encoded URL query representing the given
  241. // Obj. This function requires that the wrapped object be a
  242. // map[string]interface{}
  243. func (m Map) URLQuery() (string, error) {
  244. return m.URLValues().Encode(), nil
  245. }