1
1
package jsonpatch
2
2
3
3
import (
4
- "bytes"
5
4
"encoding/json"
6
5
"fmt"
7
6
"reflect"
@@ -24,21 +23,28 @@ func (j *Operation) Json() string {
24
23
}
25
24
26
25
func (j * Operation ) MarshalJSON () ([]byte , error ) {
27
- var b bytes.Buffer
28
- b .WriteString ("{" )
29
- b .WriteString (fmt .Sprintf (`"op":"%s"` , j .Operation ))
30
- b .WriteString (fmt .Sprintf (`,"path":"%s"` , j .Path ))
31
- // Consider omitting Value for non-nullable operations.
32
- if j .Value != nil || j .Operation == "replace" || j .Operation == "add" {
33
- v , err := json .Marshal (j .Value )
34
- if err != nil {
35
- return nil , err
36
- }
37
- b .WriteString (`,"value":` )
38
- b .Write (v )
39
- }
40
- b .WriteString ("}" )
41
- return b .Bytes (), nil
26
+ // Ensure for add and replace we emit `value: null`
27
+ if j .Value == nil && (j .Operation == "replace" || j .Operation == "add" ) {
28
+ return json .Marshal (struct {
29
+ Operation string `json:"op"`
30
+ Path string `json:"path"`
31
+ Value interface {} `json:"value"`
32
+ }{
33
+ Operation : j .Operation ,
34
+ Path : j .Path ,
35
+ })
36
+ }
37
+ // otherwise just marshal normally. We cannot literally do json.Marshal(j) as it would be recursively
38
+ // calling this function.
39
+ return json .Marshal (struct {
40
+ Operation string `json:"op"`
41
+ Path string `json:"path"`
42
+ Value interface {} `json:"value,omitempty"`
43
+ }{
44
+ Operation : j .Operation ,
45
+ Path : j .Path ,
46
+ Value : j .Value ,
47
+ })
42
48
}
43
49
44
50
type ByPath []Operation
@@ -149,9 +155,6 @@ func makePath(path string, newPart interface{}) string {
149
155
if path == "" {
150
156
return "/" + key
151
157
}
152
- if strings .HasSuffix (path , "/" ) {
153
- return path + key
154
- }
155
158
return path + "/" + key
156
159
}
157
160
@@ -211,22 +214,18 @@ func handleValues(av, bv interface{}, p string, patch []Operation) ([]Operation,
211
214
}
212
215
case []interface {}:
213
216
bt := bv .([]interface {})
214
- if isSimpleArray (at ) && isSimpleArray (bt ) {
215
- patch = append (patch , compareEditDistance (at , bt , p )... )
216
- } else {
217
- n := min (len (at ), len (bt ))
218
- for i := len (at ) - 1 ; i >= n ; i -- {
219
- patch = append (patch , NewOperation ("remove" , makePath (p , i ), nil ))
220
- }
221
- for i := n ; i < len (bt ); i ++ {
222
- patch = append (patch , NewOperation ("add" , makePath (p , i ), bt [i ]))
223
- }
224
- for i := 0 ; i < n ; i ++ {
225
- var err error
226
- patch , err = handleValues (at [i ], bt [i ], makePath (p , i ), patch )
227
- if err != nil {
228
- return nil , err
229
- }
217
+ n := min (len (at ), len (bt ))
218
+ for i := len (at ) - 1 ; i >= n ; i -- {
219
+ patch = append (patch , NewOperation ("remove" , makePath (p , i ), nil ))
220
+ }
221
+ for i := n ; i < len (bt ); i ++ {
222
+ patch = append (patch , NewOperation ("add" , makePath (p , i ), bt [i ]))
223
+ }
224
+ for i := 0 ; i < n ; i ++ {
225
+ var err error
226
+ patch , err = handleValues (at [i ], bt [i ], makePath (p , i ), patch )
227
+ if err != nil {
228
+ return nil , err
230
229
}
231
230
}
232
231
default :
@@ -235,100 +234,9 @@ func handleValues(av, bv interface{}, p string, patch []Operation) ([]Operation,
235
234
return patch , nil
236
235
}
237
236
238
- func isBasicType (a interface {}) bool {
239
- switch a .(type ) {
240
- case string , float64 , bool :
241
- default :
242
- return false
243
- }
244
- return true
245
- }
246
-
247
- func isSimpleArray (a []interface {}) bool {
248
- for i := range a {
249
- switch a [i ].(type ) {
250
- case string , float64 , bool :
251
- default :
252
- val := reflect .ValueOf (a [i ])
253
- if val .Kind () == reflect .Map {
254
- for _ , k := range val .MapKeys () {
255
- av := val .MapIndex (k )
256
- if av .Kind () == reflect .Ptr || av .Kind () == reflect .Interface {
257
- if av .IsNil () {
258
- continue
259
- }
260
- av = av .Elem ()
261
- }
262
- if av .Kind () != reflect .String && av .Kind () != reflect .Float64 && av .Kind () != reflect .Bool {
263
- return false
264
- }
265
- }
266
- return true
267
- }
268
- return false
269
- }
270
- }
271
- return true
272
- }
273
-
274
- // https://en.wikipedia.org/wiki/Wagner%E2%80%93Fischer_algorithm
275
- // Adapted from https://github.com/texttheater/golang-levenshtein
276
- func compareEditDistance (s , t []interface {}, p string ) []Operation {
277
- m := len (s )
278
- n := len (t )
279
-
280
- d := make ([][]int , m + 1 )
281
- for i := 0 ; i <= m ; i ++ {
282
- d [i ] = make ([]int , n + 1 )
283
- d [i ][0 ] = i
284
- }
285
- for j := 0 ; j <= n ; j ++ {
286
- d [0 ][j ] = j
287
- }
288
-
289
- for j := 1 ; j <= n ; j ++ {
290
- for i := 1 ; i <= m ; i ++ {
291
- if reflect .DeepEqual (s [i - 1 ], t [j - 1 ]) {
292
- d [i ][j ] = d [i - 1 ][j - 1 ] // no op required
293
- } else {
294
- del := d [i - 1 ][j ] + 1
295
- add := d [i ][j - 1 ] + 1
296
- rep := d [i - 1 ][j - 1 ] + 1
297
- d [i ][j ] = min (rep , min (add , del ))
298
- }
299
- }
300
- }
301
-
302
- return backtrace (s , t , p , m , n , d )
303
- }
304
-
305
237
func min (x int , y int ) int {
306
238
if y < x {
307
239
return y
308
240
}
309
241
return x
310
242
}
311
-
312
- func backtrace (s , t []interface {}, p string , i int , j int , matrix [][]int ) []Operation {
313
- if i > 0 && matrix [i - 1 ][j ]+ 1 == matrix [i ][j ] {
314
- op := NewOperation ("remove" , makePath (p , i - 1 ), nil )
315
- return append ([]Operation {op }, backtrace (s , t , p , i - 1 , j , matrix )... )
316
- }
317
- if j > 0 && matrix [i ][j - 1 ]+ 1 == matrix [i ][j ] {
318
- op := NewOperation ("add" , makePath (p , i ), t [j - 1 ])
319
- return append ([]Operation {op }, backtrace (s , t , p , i , j - 1 , matrix )... )
320
- }
321
- if i > 0 && j > 0 && matrix [i - 1 ][j - 1 ]+ 1 == matrix [i ][j ] {
322
- if isBasicType (s [0 ]) {
323
- op := NewOperation ("replace" , makePath (p , i - 1 ), t [j - 1 ])
324
- return append ([]Operation {op }, backtrace (s , t , p , i - 1 , j - 1 , matrix )... )
325
- }
326
-
327
- p2 , _ := handleValues (s [i - 1 ], t [j - 1 ], makePath (p , i - 1 ), []Operation {})
328
- return append (p2 , backtrace (s , t , p , i - 1 , j - 1 , matrix )... )
329
- }
330
- if i > 0 && j > 0 && matrix [i - 1 ][j - 1 ] == matrix [i ][j ] {
331
- return backtrace (s , t , p , i - 1 , j - 1 , matrix )
332
- }
333
- return []Operation {}
334
- }
0 commit comments