map.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. package objx
  2. import (
  3. "encoding/base64"
  4. "encoding/json"
  5. "errors"
  6. "io/ioutil"
  7. "net/url"
  8. "strings"
  9. )
  10. // MSIConvertable is an interface that defines methods for converting your
  11. // custom types to a map[string]interface{} representation.
  12. type MSIConvertable interface {
  13. // MSI gets a map[string]interface{} (msi) representing the
  14. // object.
  15. MSI() map[string]interface{}
  16. }
  17. // Map provides extended functionality for working with
  18. // untyped data, in particular map[string]interface (msi).
  19. type Map map[string]interface{}
  20. // Value returns the internal value instance
  21. func (m Map) Value() *Value {
  22. return &Value{data: m}
  23. }
  24. // Nil represents a nil Map.
  25. var Nil = New(nil)
  26. // New creates a new Map containing the map[string]interface{} in the data argument.
  27. // If the data argument is not a map[string]interface, New attempts to call the
  28. // MSI() method on the MSIConvertable interface to create one.
  29. func New(data interface{}) Map {
  30. if _, ok := data.(map[string]interface{}); !ok {
  31. if converter, ok := data.(MSIConvertable); ok {
  32. data = converter.MSI()
  33. } else {
  34. return nil
  35. }
  36. }
  37. return Map(data.(map[string]interface{}))
  38. }
  39. // MSI creates a map[string]interface{} and puts it inside a new Map.
  40. //
  41. // The arguments follow a key, value pattern.
  42. //
  43. // Returns nil if any key argument is non-string or if there are an odd number of arguments.
  44. //
  45. // # Example
  46. //
  47. // To easily create Maps:
  48. //
  49. // m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true))
  50. //
  51. // // creates an Map equivalent to
  52. // m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}}
  53. func MSI(keyAndValuePairs ...interface{}) Map {
  54. newMap := Map{}
  55. keyAndValuePairsLen := len(keyAndValuePairs)
  56. if keyAndValuePairsLen%2 != 0 {
  57. return nil
  58. }
  59. for i := 0; i < keyAndValuePairsLen; i = i + 2 {
  60. key := keyAndValuePairs[i]
  61. value := keyAndValuePairs[i+1]
  62. // make sure the key is a string
  63. keyString, keyStringOK := key.(string)
  64. if !keyStringOK {
  65. return nil
  66. }
  67. newMap[keyString] = value
  68. }
  69. return newMap
  70. }
  71. // ****** Conversion Constructors
  72. // MustFromJSON creates a new Map containing the data specified in the
  73. // jsonString.
  74. //
  75. // Panics if the JSON is invalid.
  76. func MustFromJSON(jsonString string) Map {
  77. o, err := FromJSON(jsonString)
  78. if err != nil {
  79. panic("objx: MustFromJSON failed with error: " + err.Error())
  80. }
  81. return o
  82. }
  83. // MustFromJSONSlice creates a new slice of Map containing the data specified in the
  84. // jsonString. Works with jsons with a top level array
  85. //
  86. // Panics if the JSON is invalid.
  87. func MustFromJSONSlice(jsonString string) []Map {
  88. slice, err := FromJSONSlice(jsonString)
  89. if err != nil {
  90. panic("objx: MustFromJSONSlice failed with error: " + err.Error())
  91. }
  92. return slice
  93. }
  94. // FromJSON creates a new Map containing the data specified in the
  95. // jsonString.
  96. //
  97. // Returns an error if the JSON is invalid.
  98. func FromJSON(jsonString string) (Map, error) {
  99. var m Map
  100. err := json.Unmarshal([]byte(jsonString), &m)
  101. if err != nil {
  102. return Nil, err
  103. }
  104. return m, nil
  105. }
  106. // FromJSONSlice creates a new slice of Map containing the data specified in the
  107. // jsonString. Works with jsons with a top level array
  108. //
  109. // Returns an error if the JSON is invalid.
  110. func FromJSONSlice(jsonString string) ([]Map, error) {
  111. var slice []Map
  112. err := json.Unmarshal([]byte(jsonString), &slice)
  113. if err != nil {
  114. return nil, err
  115. }
  116. return slice, nil
  117. }
  118. // FromBase64 creates a new Obj containing the data specified
  119. // in the Base64 string.
  120. //
  121. // The string is an encoded JSON string returned by Base64
  122. func FromBase64(base64String string) (Map, error) {
  123. decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String))
  124. decoded, err := ioutil.ReadAll(decoder)
  125. if err != nil {
  126. return nil, err
  127. }
  128. return FromJSON(string(decoded))
  129. }
  130. // MustFromBase64 creates a new Obj containing the data specified
  131. // in the Base64 string and panics if there is an error.
  132. //
  133. // The string is an encoded JSON string returned by Base64
  134. func MustFromBase64(base64String string) Map {
  135. result, err := FromBase64(base64String)
  136. if err != nil {
  137. panic("objx: MustFromBase64 failed with error: " + err.Error())
  138. }
  139. return result
  140. }
  141. // FromSignedBase64 creates a new Obj containing the data specified
  142. // in the Base64 string.
  143. //
  144. // The string is an encoded JSON string returned by SignedBase64
  145. func FromSignedBase64(base64String, key string) (Map, error) {
  146. parts := strings.Split(base64String, SignatureSeparator)
  147. if len(parts) != 2 {
  148. return nil, errors.New("objx: Signed base64 string is malformed")
  149. }
  150. sig := HashWithKey(parts[0], key)
  151. if parts[1] != sig {
  152. return nil, errors.New("objx: Signature for base64 data does not match")
  153. }
  154. return FromBase64(parts[0])
  155. }
  156. // MustFromSignedBase64 creates a new Obj containing the data specified
  157. // in the Base64 string and panics if there is an error.
  158. //
  159. // The string is an encoded JSON string returned by Base64
  160. func MustFromSignedBase64(base64String, key string) Map {
  161. result, err := FromSignedBase64(base64String, key)
  162. if err != nil {
  163. panic("objx: MustFromSignedBase64 failed with error: " + err.Error())
  164. }
  165. return result
  166. }
  167. // FromURLQuery generates a new Obj by parsing the specified
  168. // query.
  169. //
  170. // For queries with multiple values, the first value is selected.
  171. func FromURLQuery(query string) (Map, error) {
  172. vals, err := url.ParseQuery(query)
  173. if err != nil {
  174. return nil, err
  175. }
  176. m := Map{}
  177. for k, vals := range vals {
  178. m[k] = vals[0]
  179. }
  180. return m, nil
  181. }
  182. // MustFromURLQuery generates a new Obj by parsing the specified
  183. // query.
  184. //
  185. // For queries with multiple values, the first value is selected.
  186. //
  187. // Panics if it encounters an error
  188. func MustFromURLQuery(query string) Map {
  189. o, err := FromURLQuery(query)
  190. if err != nil {
  191. panic("objx: MustFromURLQuery failed with error: " + err.Error())
  192. }
  193. return o
  194. }