...
1 package objx
2
3 import (
4 "reflect"
5 "regexp"
6 "strconv"
7 "strings"
8 )
9
10 const (
11
12
13
14
15 PathSeparator string = "."
16
17
18
19 arrayAccessRegexString = `^(.+)\[([0-9]+)\]$`
20
21
22
23 mapAccessRegexString = `^([^\[]*)\[([^\]]+)\](.*)$`
24 )
25
26
27 var arrayAccessRegex = regexp.MustCompile(arrayAccessRegexString)
28
29
30 var mapAccessRegex = regexp.MustCompile(mapAccessRegexString)
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 func (m Map) Get(selector string) *Value {
46 rawObj := access(m, selector, nil, false)
47 return &Value{data: rawObj}
48 }
49
50
51
52
53
54
55
56
57
58
59
60 func (m Map) Set(selector string, value interface{}) Map {
61 access(m, selector, value, true)
62 return m
63 }
64
65
66
67
68 func getIndex(s string) (int, string) {
69 arrayMatches := arrayAccessRegex.FindStringSubmatch(s)
70 if len(arrayMatches) > 0 {
71
72 selector := arrayMatches[1]
73
74
75 index, _ := strconv.Atoi(arrayMatches[2])
76 return index, selector
77 }
78 return -1, s
79 }
80
81
82
83 func getKey(s string) (string, string) {
84 selSegs := strings.SplitN(s, PathSeparator, 2)
85 thisSel := selSegs[0]
86 nextSel := ""
87
88 if len(selSegs) > 1 {
89 nextSel = selSegs[1]
90 }
91
92 mapMatches := mapAccessRegex.FindStringSubmatch(s)
93 if len(mapMatches) > 0 {
94 if _, err := strconv.Atoi(mapMatches[2]); err != nil {
95 thisSel = mapMatches[1]
96 nextSel = "[" + mapMatches[2] + "]" + mapMatches[3]
97
98 if thisSel == "" {
99 thisSel = mapMatches[2]
100 nextSel = mapMatches[3]
101 }
102
103 if nextSel == "" {
104 selSegs = []string{"", ""}
105 } else if nextSel[0] == '.' {
106 nextSel = nextSel[1:]
107 }
108 }
109 }
110
111 return thisSel, nextSel
112 }
113
114
115
116 func access(current interface{}, selector string, value interface{}, isSet bool) interface{} {
117 thisSel, nextSel := getKey(selector)
118
119 indexes := []int{}
120 for strings.Contains(thisSel, "[") {
121 prevSel := thisSel
122 index := -1
123 index, thisSel = getIndex(thisSel)
124 indexes = append(indexes, index)
125 if prevSel == thisSel {
126 break
127 }
128 }
129
130 if curMap, ok := current.(Map); ok {
131 current = map[string]interface{}(curMap)
132 }
133
134 switch current.(type) {
135 case map[string]interface{}:
136 curMSI := current.(map[string]interface{})
137 if nextSel == "" && isSet {
138 curMSI[thisSel] = value
139 return nil
140 }
141
142 _, ok := curMSI[thisSel].(map[string]interface{})
143 if !ok {
144 _, ok = curMSI[thisSel].(Map)
145 }
146
147 if (curMSI[thisSel] == nil || !ok) && len(indexes) == 0 && isSet {
148 curMSI[thisSel] = map[string]interface{}{}
149 }
150
151 current = curMSI[thisSel]
152 default:
153 current = nil
154 }
155
156
157 if len(indexes) > 0 {
158 num := len(indexes)
159 for num > 0 {
160 num--
161 index := indexes[num]
162 indexes = indexes[:num]
163 if array, ok := interSlice(current); ok {
164 if index < len(array) {
165 current = array[index]
166 } else {
167 current = nil
168 break
169 }
170 }
171 }
172 }
173
174 if nextSel != "" {
175 current = access(current, nextSel, value, isSet)
176 }
177 return current
178 }
179
180 func interSlice(slice interface{}) ([]interface{}, bool) {
181 if array, ok := slice.([]interface{}); ok {
182 return array, ok
183 }
184
185 s := reflect.ValueOf(slice)
186 if s.Kind() != reflect.Slice {
187 return nil, false
188 }
189
190 ret := make([]interface{}, s.Len())
191
192 for i := 0; i < s.Len(); i++ {
193 ret[i] = s.Index(i).Interface()
194 }
195
196 return ret, true
197 }
198
View as plain text