map.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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. //
  44. // Returns nil if any key argument is non-string or if there are an odd number of arguments.
  45. //
  46. // Example
  47. //
  48. // To easily create Maps:
  49. //
  50. // m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true))
  51. //
  52. // // creates an Map equivalent to
  53. // m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}}
  54. func MSI(keyAndValuePairs ...interface{}) Map {
  55. newMap := Map{}
  56. keyAndValuePairsLen := len(keyAndValuePairs)
  57. if keyAndValuePairsLen%2 != 0 {
  58. return nil
  59. }
  60. for i := 0; i < keyAndValuePairsLen; i = i + 2 {
  61. key := keyAndValuePairs[i]
  62. value := keyAndValuePairs[i+1]
  63. // make sure the key is a string
  64. keyString, keyStringOK := key.(string)
  65. if !keyStringOK {
  66. return nil
  67. }
  68. newMap[keyString] = value
  69. }
  70. return newMap
  71. }
  72. // ****** Conversion Constructors
  73. // MustFromJSON creates a new Map containing the data specified in the
  74. // jsonString.
  75. //
  76. // Panics if the JSON is invalid.
  77. func MustFromJSON(jsonString string) Map {
  78. o, err := FromJSON(jsonString)
  79. if err != nil {
  80. panic("objx: MustFromJSON failed with error: " + err.Error())
  81. }
  82. return o
  83. }
  84. // FromJSON creates a new Map containing the data specified in the
  85. // jsonString.
  86. //
  87. // Returns an error if the JSON is invalid.
  88. func FromJSON(jsonString string) (Map, error) {
  89. var data interface{}
  90. err := json.Unmarshal([]byte(jsonString), &data)
  91. if err != nil {
  92. return Nil, err
  93. }
  94. return New(data), nil
  95. }
  96. // FromBase64 creates a new Obj containing the data specified
  97. // in the Base64 string.
  98. //
  99. // The string is an encoded JSON string returned by Base64
  100. func FromBase64(base64String string) (Map, error) {
  101. decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String))
  102. decoded, err := ioutil.ReadAll(decoder)
  103. if err != nil {
  104. return nil, err
  105. }
  106. return FromJSON(string(decoded))
  107. }
  108. // MustFromBase64 creates a new Obj containing the data specified
  109. // in the Base64 string and panics if there is an error.
  110. //
  111. // The string is an encoded JSON string returned by Base64
  112. func MustFromBase64(base64String string) Map {
  113. result, err := FromBase64(base64String)
  114. if err != nil {
  115. panic("objx: MustFromBase64 failed with error: " + err.Error())
  116. }
  117. return result
  118. }
  119. // FromSignedBase64 creates a new Obj containing the data specified
  120. // in the Base64 string.
  121. //
  122. // The string is an encoded JSON string returned by SignedBase64
  123. func FromSignedBase64(base64String, key string) (Map, error) {
  124. parts := strings.Split(base64String, SignatureSeparator)
  125. if len(parts) != 2 {
  126. return nil, errors.New("objx: Signed base64 string is malformed")
  127. }
  128. sig := HashWithKey(parts[0], key)
  129. if parts[1] != sig {
  130. return nil, errors.New("objx: Signature for base64 data does not match")
  131. }
  132. return FromBase64(parts[0])
  133. }
  134. // MustFromSignedBase64 creates a new Obj containing the data specified
  135. // in the Base64 string and panics if there is an error.
  136. //
  137. // The string is an encoded JSON string returned by Base64
  138. func MustFromSignedBase64(base64String, key string) Map {
  139. result, err := FromSignedBase64(base64String, key)
  140. if err != nil {
  141. panic("objx: MustFromSignedBase64 failed with error: " + err.Error())
  142. }
  143. return result
  144. }
  145. // FromURLQuery generates a new Obj by parsing the specified
  146. // query.
  147. //
  148. // For queries with multiple values, the first value is selected.
  149. func FromURLQuery(query string) (Map, error) {
  150. vals, err := url.ParseQuery(query)
  151. if err != nil {
  152. return nil, err
  153. }
  154. m := Map{}
  155. for k, vals := range vals {
  156. m[k] = vals[0]
  157. }
  158. return m, nil
  159. }
  160. // MustFromURLQuery generates a new Obj by parsing the specified
  161. // query.
  162. //
  163. // For queries with multiple values, the first value is selected.
  164. //
  165. // Panics if it encounters an error
  166. func MustFromURLQuery(query string) Map {
  167. o, err := FromURLQuery(query)
  168. if err != nil {
  169. panic("objx: MustFromURLQuery failed with error: " + err.Error())
  170. }
  171. return o
  172. }