1
16
17 package objectmeta
18
19 import (
20 "bytes"
21 "fmt"
22
23 "k8s.io/apimachinery/pkg/runtime"
24 )
25
26 type (
27 jsonPathNode struct {
28 index *int
29 field string
30 }
31 JSONPath []jsonPathNode
32 )
33
34 func (p JSONPath) String() string {
35 var buf bytes.Buffer
36 for _, n := range p {
37 if n.index == nil {
38 buf.WriteString("." + n.field)
39 } else {
40 buf.WriteString(fmt.Sprintf("[%d]", *n.index))
41 }
42 }
43 return buf.String()
44 }
45
46 func jsonPaths(base JSONPath, j map[string]interface{}) []JSONPath {
47 res := make([]JSONPath, 0, len(j))
48 for k, old := range j {
49 kPth := append(append([]jsonPathNode(nil), base...), jsonPathNode{field: k})
50 res = append(res, kPth)
51
52 switch old := old.(type) {
53 case map[string]interface{}:
54 res = append(res, jsonPaths(kPth, old)...)
55 case []interface{}:
56 res = append(res, jsonIterSlice(kPth, old)...)
57 }
58 }
59 return res
60 }
61
62 func jsonIterSlice(base JSONPath, j []interface{}) []JSONPath {
63 res := make([]JSONPath, 0, len(j))
64 for i, old := range j {
65 index := i
66 iPth := append(append([]jsonPathNode(nil), base...), jsonPathNode{index: &index})
67 res = append(res, iPth)
68
69 switch old := old.(type) {
70 case map[string]interface{}:
71 res = append(res, jsonPaths(iPth, old)...)
72 case []interface{}:
73 res = append(res, jsonIterSlice(iPth, old)...)
74 }
75 }
76 return res
77 }
78
79 func JSONPathValue(j map[string]interface{}, pth JSONPath, base int) (interface{}, error) {
80 if len(pth) == base {
81 return nil, fmt.Errorf("empty json path is invalid for object")
82 }
83 if pth[base].index != nil {
84 return nil, fmt.Errorf("index json path is invalid for object")
85 }
86 field, ok := j[pth[base].field]
87 if !ok || len(pth) == base+1 {
88 if len(pth) > base+1 {
89 return nil, fmt.Errorf("invalid non-terminal json path %q for non-existing field", pth)
90 }
91 return j[pth[base].field], nil
92 }
93 switch field := field.(type) {
94 case map[string]interface{}:
95 return JSONPathValue(field, pth, base+1)
96 case []interface{}:
97 return jsonPathValueSlice(field, pth, base+1)
98 default:
99 return nil, fmt.Errorf("invalid non-terminal json path %q for field", pth[:base+1])
100 }
101 }
102
103 func jsonPathValueSlice(j []interface{}, pth JSONPath, base int) (interface{}, error) {
104 if len(pth) == base {
105 return nil, fmt.Errorf("empty json path %q is invalid for object", pth)
106 }
107 if pth[base].index == nil {
108 return nil, fmt.Errorf("field json path %q is invalid for object", pth[:base+1])
109 }
110 if *pth[base].index >= len(j) {
111 return nil, fmt.Errorf("invalid index %q for array of size %d", pth[:base+1], len(j))
112 }
113 if len(pth) == base+1 {
114 return j[*pth[base].index], nil
115 }
116 switch item := j[*pth[base].index].(type) {
117 case map[string]interface{}:
118 return JSONPathValue(item, pth, base+1)
119 case []interface{}:
120 return jsonPathValueSlice(item, pth, base+1)
121 default:
122 return nil, fmt.Errorf("invalid non-terminal json path %q for index", pth[:base+1])
123 }
124 }
125
126 func SetJSONPath(j map[string]interface{}, pth JSONPath, base int, value interface{}) error {
127 if len(pth) == base {
128 return fmt.Errorf("empty json path is invalid for object")
129 }
130 if pth[base].index != nil {
131 return fmt.Errorf("index json path is invalid for object")
132 }
133 field, ok := j[pth[base].field]
134 if !ok || len(pth) == base+1 {
135 if len(pth) > base+1 {
136 return fmt.Errorf("invalid non-terminal json path %q for non-existing field", pth)
137 }
138 j[pth[base].field] = runtime.DeepCopyJSONValue(value)
139 return nil
140 }
141 switch field := field.(type) {
142 case map[string]interface{}:
143 return SetJSONPath(field, pth, base+1, value)
144 case []interface{}:
145 return setJSONPathSlice(field, pth, base+1, value)
146 default:
147 return fmt.Errorf("invalid non-terminal json path %q for field", pth[:base+1])
148 }
149 }
150
151 func setJSONPathSlice(j []interface{}, pth JSONPath, base int, value interface{}) error {
152 if len(pth) == base {
153 return fmt.Errorf("empty json path %q is invalid for object", pth)
154 }
155 if pth[base].index == nil {
156 return fmt.Errorf("field json path %q is invalid for object", pth[:base+1])
157 }
158 if *pth[base].index >= len(j) {
159 return fmt.Errorf("invalid index %q for array of size %d", pth[:base+1], len(j))
160 }
161 if len(pth) == base+1 {
162 j[*pth[base].index] = runtime.DeepCopyJSONValue(value)
163 return nil
164 }
165 switch item := j[*pth[base].index].(type) {
166 case map[string]interface{}:
167 return SetJSONPath(item, pth, base+1, value)
168 case []interface{}:
169 return setJSONPathSlice(item, pth, base+1, value)
170 default:
171 return fmt.Errorf("invalid non-terminal json path %q for index", pth[:base+1])
172 }
173 }
174
175 func DeleteJSONPath(j map[string]interface{}, pth JSONPath, base int) error {
176 if len(pth) == base {
177 return fmt.Errorf("empty json path is invalid for object")
178 }
179 if pth[base].index != nil {
180 return fmt.Errorf("index json path is invalid for object")
181 }
182 field, ok := j[pth[base].field]
183 if !ok || len(pth) == base+1 {
184 if len(pth) > base+1 {
185 return fmt.Errorf("invalid non-terminal json path %q for non-existing field", pth)
186 }
187 delete(j, pth[base].field)
188 return nil
189 }
190 switch field := field.(type) {
191 case map[string]interface{}:
192 return DeleteJSONPath(field, pth, base+1)
193 case []interface{}:
194 if len(pth) == base+2 {
195 if pth[base+1].index == nil {
196 return fmt.Errorf("field json path %q is invalid for object", pth)
197 }
198 j[pth[base].field] = append(field[:*pth[base+1].index], field[*pth[base+1].index+1:]...)
199 return nil
200 }
201 return deleteJSONPathSlice(field, pth, base+1)
202 default:
203 return fmt.Errorf("invalid non-terminal json path %q for field", pth[:base+1])
204 }
205 }
206
207 func deleteJSONPathSlice(j []interface{}, pth JSONPath, base int) error {
208 if len(pth) == base {
209 return fmt.Errorf("empty json path %q is invalid for object", pth)
210 }
211 if pth[base].index == nil {
212 return fmt.Errorf("field json path %q is invalid for object", pth[:base+1])
213 }
214 if *pth[base].index >= len(j) {
215 return fmt.Errorf("invalid index %q for array of size %d", pth[:base+1], len(j))
216 }
217 if len(pth) == base+1 {
218 return fmt.Errorf("cannot delete item at index %q in-place", pth[:base])
219 }
220 switch item := j[*pth[base].index].(type) {
221 case map[string]interface{}:
222 return DeleteJSONPath(item, pth, base+1)
223 case []interface{}:
224 if len(pth) == base+2 {
225 if pth[base+1].index == nil {
226 return fmt.Errorf("field json path %q is invalid for object", pth)
227 }
228 j[*pth[base].index] = append(item[:*pth[base+1].index], item[*pth[base+1].index+1:])
229 return nil
230 }
231 return deleteJSONPathSlice(item, pth, base+1)
232 default:
233 return fmt.Errorf("invalid non-terminal json path %q for index", pth[:base+1])
234 }
235 }
236
View as plain text