accessors.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. package objx
  2. import (
  3. "reflect"
  4. "regexp"
  5. "strconv"
  6. "strings"
  7. )
  8. const (
  9. // PathSeparator is the character used to separate the elements
  10. // of the keypath.
  11. //
  12. // For example, `location.address.city`
  13. PathSeparator string = "."
  14. // arrayAccessRegexString is the regex used to extract the array number
  15. // from the access path
  16. arrayAccessRegexString = `^(.+)\[([0-9]+)\]$`
  17. // mapAccessRegexString is the regex used to extract the map key
  18. // from the access path
  19. mapAccessRegexString = `^([^\[]*)\[([^\]]+)\](.*)$`
  20. )
  21. // arrayAccessRegex is the compiled arrayAccessRegexString
  22. var arrayAccessRegex = regexp.MustCompile(arrayAccessRegexString)
  23. // mapAccessRegex is the compiled mapAccessRegexString
  24. var mapAccessRegex = regexp.MustCompile(mapAccessRegexString)
  25. // Get gets the value using the specified selector and
  26. // returns it inside a new Obj object.
  27. //
  28. // If it cannot find the value, Get will return a nil
  29. // value inside an instance of Obj.
  30. //
  31. // Get can only operate directly on map[string]interface{} and []interface.
  32. //
  33. // # Example
  34. //
  35. // To access the title of the third chapter of the second book, do:
  36. //
  37. // o.Get("books[1].chapters[2].title")
  38. func (m Map) Get(selector string) *Value {
  39. rawObj := access(m, selector, nil, false)
  40. return &Value{data: rawObj}
  41. }
  42. // Set sets the value using the specified selector and
  43. // returns the object on which Set was called.
  44. //
  45. // Set can only operate directly on map[string]interface{} and []interface
  46. //
  47. // # Example
  48. //
  49. // To set the title of the third chapter of the second book, do:
  50. //
  51. // o.Set("books[1].chapters[2].title","Time to Go")
  52. func (m Map) Set(selector string, value interface{}) Map {
  53. access(m, selector, value, true)
  54. return m
  55. }
  56. // getIndex returns the index, which is hold in s by two branches.
  57. // It also returns s without the index part, e.g. name[1] will return (1, name).
  58. // If no index is found, -1 is returned
  59. func getIndex(s string) (int, string) {
  60. arrayMatches := arrayAccessRegex.FindStringSubmatch(s)
  61. if len(arrayMatches) > 0 {
  62. // Get the key into the map
  63. selector := arrayMatches[1]
  64. // Get the index into the array at the key
  65. // We know this can't fail because arrayMatches[2] is an int for sure
  66. index, _ := strconv.Atoi(arrayMatches[2])
  67. return index, selector
  68. }
  69. return -1, s
  70. }
  71. // getKey returns the key which is held in s by two brackets.
  72. // It also returns the next selector.
  73. func getKey(s string) (string, string) {
  74. selSegs := strings.SplitN(s, PathSeparator, 2)
  75. thisSel := selSegs[0]
  76. nextSel := ""
  77. if len(selSegs) > 1 {
  78. nextSel = selSegs[1]
  79. }
  80. mapMatches := mapAccessRegex.FindStringSubmatch(s)
  81. if len(mapMatches) > 0 {
  82. if _, err := strconv.Atoi(mapMatches[2]); err != nil {
  83. thisSel = mapMatches[1]
  84. nextSel = "[" + mapMatches[2] + "]" + mapMatches[3]
  85. if thisSel == "" {
  86. thisSel = mapMatches[2]
  87. nextSel = mapMatches[3]
  88. }
  89. if nextSel == "" {
  90. selSegs = []string{"", ""}
  91. } else if nextSel[0] == '.' {
  92. nextSel = nextSel[1:]
  93. }
  94. }
  95. }
  96. return thisSel, nextSel
  97. }
  98. // access accesses the object using the selector and performs the
  99. // appropriate action.
  100. func access(current interface{}, selector string, value interface{}, isSet bool) interface{} {
  101. thisSel, nextSel := getKey(selector)
  102. indexes := []int{}
  103. for strings.Contains(thisSel, "[") {
  104. prevSel := thisSel
  105. index := -1
  106. index, thisSel = getIndex(thisSel)
  107. indexes = append(indexes, index)
  108. if prevSel == thisSel {
  109. break
  110. }
  111. }
  112. if curMap, ok := current.(Map); ok {
  113. current = map[string]interface{}(curMap)
  114. }
  115. // get the object in question
  116. switch current.(type) {
  117. case map[string]interface{}:
  118. curMSI := current.(map[string]interface{})
  119. if nextSel == "" && isSet {
  120. curMSI[thisSel] = value
  121. return nil
  122. }
  123. _, ok := curMSI[thisSel].(map[string]interface{})
  124. if !ok {
  125. _, ok = curMSI[thisSel].(Map)
  126. }
  127. if (curMSI[thisSel] == nil || !ok) && len(indexes) == 0 && isSet {
  128. curMSI[thisSel] = map[string]interface{}{}
  129. }
  130. current = curMSI[thisSel]
  131. default:
  132. current = nil
  133. }
  134. // do we need to access the item of an array?
  135. if len(indexes) > 0 {
  136. num := len(indexes)
  137. for num > 0 {
  138. num--
  139. index := indexes[num]
  140. indexes = indexes[:num]
  141. if array, ok := interSlice(current); ok {
  142. if index < len(array) {
  143. current = array[index]
  144. } else {
  145. current = nil
  146. break
  147. }
  148. }
  149. }
  150. }
  151. if nextSel != "" {
  152. current = access(current, nextSel, value, isSet)
  153. }
  154. return current
  155. }
  156. func interSlice(slice interface{}) ([]interface{}, bool) {
  157. if array, ok := slice.([]interface{}); ok {
  158. return array, ok
  159. }
  160. s := reflect.ValueOf(slice)
  161. if s.Kind() != reflect.Slice {
  162. return nil, false
  163. }
  164. ret := make([]interface{}, s.Len())
  165. for i := 0; i < s.Len(); i++ {
  166. ret[i] = s.Index(i).Interface()
  167. }
  168. return ret, true
  169. }