1package jsonpatch 2 3import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "reflect" 8) 9 10func merge(cur, patch *lazyNode, mergeMerge bool) *lazyNode { 11 curDoc, err := cur.intoDoc() 12 13 if err != nil { 14 pruneNulls(patch) 15 return patch 16 } 17 18 patchDoc, err := patch.intoDoc() 19 20 if err != nil { 21 return patch 22 } 23 24 mergeDocs(curDoc, patchDoc, mergeMerge) 25 26 return cur 27} 28 29func mergeDocs(doc, patch *partialDoc, mergeMerge bool) { 30 for k, v := range *patch { 31 if v == nil { 32 if mergeMerge { 33 (*doc)[k] = nil 34 } else { 35 delete(*doc, k) 36 } 37 } else { 38 cur, ok := (*doc)[k] 39 40 if !ok || cur == nil { 41 if !mergeMerge { 42 pruneNulls(v) 43 } 44 45 (*doc)[k] = v 46 } else { 47 (*doc)[k] = merge(cur, v, mergeMerge) 48 } 49 } 50 } 51} 52 53func pruneNulls(n *lazyNode) { 54 sub, err := n.intoDoc() 55 56 if err == nil { 57 pruneDocNulls(sub) 58 } else { 59 ary, err := n.intoAry() 60 61 if err == nil { 62 pruneAryNulls(ary) 63 } 64 } 65} 66 67func pruneDocNulls(doc *partialDoc) *partialDoc { 68 for k, v := range *doc { 69 if v == nil { 70 delete(*doc, k) 71 } else { 72 pruneNulls(v) 73 } 74 } 75 76 return doc 77} 78 79func pruneAryNulls(ary *partialArray) *partialArray { 80 newAry := []*lazyNode{} 81 82 for _, v := range *ary { 83 if v != nil { 84 pruneNulls(v) 85 } 86 newAry = append(newAry, v) 87 } 88 89 *ary = newAry 90 91 return ary 92} 93 94var ErrBadJSONDoc = fmt.Errorf("Invalid JSON Document") 95var ErrBadJSONPatch = fmt.Errorf("Invalid JSON Patch") 96var errBadMergeTypes = fmt.Errorf("Mismatched JSON Documents") 97 98// MergeMergePatches merges two merge patches together, such that 99// applying this resulting merged merge patch to a document yields the same 100// as merging each merge patch to the document in succession. 101func MergeMergePatches(patch1Data, patch2Data []byte) ([]byte, error) { 102 return doMergePatch(patch1Data, patch2Data, true) 103} 104 105// MergePatch merges the patchData into the docData. 106func MergePatch(docData, patchData []byte) ([]byte, error) { 107 return doMergePatch(docData, patchData, false) 108} 109 110func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) { 111 doc := &partialDoc{} 112 113 docErr := json.Unmarshal(docData, doc) 114 115 patch := &partialDoc{} 116 117 patchErr := json.Unmarshal(patchData, patch) 118 119 if _, ok := docErr.(*json.SyntaxError); ok { 120 return nil, ErrBadJSONDoc 121 } 122 123 if _, ok := patchErr.(*json.SyntaxError); ok { 124 return nil, ErrBadJSONPatch 125 } 126 127 if docErr == nil && *doc == nil { 128 return nil, ErrBadJSONDoc 129 } 130 131 if patchErr == nil && *patch == nil { 132 return nil, ErrBadJSONPatch 133 } 134 135 if docErr != nil || patchErr != nil { 136 // Not an error, just not a doc, so we turn straight into the patch 137 if patchErr == nil { 138 if mergeMerge { 139 doc = patch 140 } else { 141 doc = pruneDocNulls(patch) 142 } 143 } else { 144 patchAry := &partialArray{} 145 patchErr = json.Unmarshal(patchData, patchAry) 146 147 if patchErr != nil { 148 return nil, ErrBadJSONPatch 149 } 150 151 pruneAryNulls(patchAry) 152 153 out, patchErr := json.Marshal(patchAry) 154 155 if patchErr != nil { 156 return nil, ErrBadJSONPatch 157 } 158 159 return out, nil 160 } 161 } else { 162 mergeDocs(doc, patch, mergeMerge) 163 } 164 165 return json.Marshal(doc) 166} 167 168// resemblesJSONArray indicates whether the byte-slice "appears" to be 169// a JSON array or not. 170// False-positives are possible, as this function does not check the internal 171// structure of the array. It only checks that the outer syntax is present and 172// correct. 173func resemblesJSONArray(input []byte) bool { 174 input = bytes.TrimSpace(input) 175 176 hasPrefix := bytes.HasPrefix(input, []byte("[")) 177 hasSuffix := bytes.HasSuffix(input, []byte("]")) 178 179 return hasPrefix && hasSuffix 180} 181 182// CreateMergePatch will return a merge patch document capable of converting 183// the original document(s) to the modified document(s). 184// The parameters can be bytes of either two JSON Documents, or two arrays of 185// JSON documents. 186// The merge patch returned follows the specification defined at http://tools.ietf.org/html/draft-ietf-appsawg-json-merge-patch-07 187func CreateMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { 188 originalResemblesArray := resemblesJSONArray(originalJSON) 189 modifiedResemblesArray := resemblesJSONArray(modifiedJSON) 190 191 // Do both byte-slices seem like JSON arrays? 192 if originalResemblesArray && modifiedResemblesArray { 193 return createArrayMergePatch(originalJSON, modifiedJSON) 194 } 195 196 // Are both byte-slices are not arrays? Then they are likely JSON objects... 197 if !originalResemblesArray && !modifiedResemblesArray { 198 return createObjectMergePatch(originalJSON, modifiedJSON) 199 } 200 201 // None of the above? Then return an error because of mismatched types. 202 return nil, errBadMergeTypes 203} 204 205// createObjectMergePatch will return a merge-patch document capable of 206// converting the original document to the modified document. 207func createObjectMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { 208 originalDoc := map[string]interface{}{} 209 modifiedDoc := map[string]interface{}{} 210 211 err := json.Unmarshal(originalJSON, &originalDoc) 212 if err != nil { 213 return nil, ErrBadJSONDoc 214 } 215 216 err = json.Unmarshal(modifiedJSON, &modifiedDoc) 217 if err != nil { 218 return nil, ErrBadJSONDoc 219 } 220 221 dest, err := getDiff(originalDoc, modifiedDoc) 222 if err != nil { 223 return nil, err 224 } 225 226 return json.Marshal(dest) 227} 228 229// createArrayMergePatch will return an array of merge-patch documents capable 230// of converting the original document to the modified document for each 231// pair of JSON documents provided in the arrays. 232// Arrays of mismatched sizes will result in an error. 233func createArrayMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { 234 originalDocs := []json.RawMessage{} 235 modifiedDocs := []json.RawMessage{} 236 237 err := json.Unmarshal(originalJSON, &originalDocs) 238 if err != nil { 239 return nil, ErrBadJSONDoc 240 } 241 242 err = json.Unmarshal(modifiedJSON, &modifiedDocs) 243 if err != nil { 244 return nil, ErrBadJSONDoc 245 } 246 247 total := len(originalDocs) 248 if len(modifiedDocs) != total { 249 return nil, ErrBadJSONDoc 250 } 251 252 result := []json.RawMessage{} 253 for i := 0; i < len(originalDocs); i++ { 254 original := originalDocs[i] 255 modified := modifiedDocs[i] 256 257 patch, err := createObjectMergePatch(original, modified) 258 if err != nil { 259 return nil, err 260 } 261 262 result = append(result, json.RawMessage(patch)) 263 } 264 265 return json.Marshal(result) 266} 267 268// Returns true if the array matches (must be json types). 269// As is idiomatic for go, an empty array is not the same as a nil array. 270func matchesArray(a, b []interface{}) bool { 271 if len(a) != len(b) { 272 return false 273 } 274 if (a == nil && b != nil) || (a != nil && b == nil) { 275 return false 276 } 277 for i := range a { 278 if !matchesValue(a[i], b[i]) { 279 return false 280 } 281 } 282 return true 283} 284 285// Returns true if the values matches (must be json types) 286// The types of the values must match, otherwise it will always return false 287// If two map[string]interface{} are given, all elements must match. 288func matchesValue(av, bv interface{}) bool { 289 if reflect.TypeOf(av) != reflect.TypeOf(bv) { 290 return false 291 } 292 switch at := av.(type) { 293 case string: 294 bt := bv.(string) 295 if bt == at { 296 return true 297 } 298 case float64: 299 bt := bv.(float64) 300 if bt == at { 301 return true 302 } 303 case bool: 304 bt := bv.(bool) 305 if bt == at { 306 return true 307 } 308 case nil: 309 // Both nil, fine. 310 return true 311 case map[string]interface{}: 312 bt := bv.(map[string]interface{}) 313 if len(bt) != len(at) { 314 return false 315 } 316 for key := range bt { 317 av, aOK := at[key] 318 bv, bOK := bt[key] 319 if aOK != bOK { 320 return false 321 } 322 if !matchesValue(av, bv) { 323 return false 324 } 325 } 326 return true 327 case []interface{}: 328 bt := bv.([]interface{}) 329 return matchesArray(at, bt) 330 } 331 return false 332} 333 334// getDiff returns the (recursive) difference between a and b as a map[string]interface{}. 335func getDiff(a, b map[string]interface{}) (map[string]interface{}, error) { 336 into := map[string]interface{}{} 337 for key, bv := range b { 338 av, ok := a[key] 339 // value was added 340 if !ok { 341 into[key] = bv 342 continue 343 } 344 // If types have changed, replace completely 345 if reflect.TypeOf(av) != reflect.TypeOf(bv) { 346 into[key] = bv 347 continue 348 } 349 // Types are the same, compare values 350 switch at := av.(type) { 351 case map[string]interface{}: 352 bt := bv.(map[string]interface{}) 353 dst := make(map[string]interface{}, len(bt)) 354 dst, err := getDiff(at, bt) 355 if err != nil { 356 return nil, err 357 } 358 if len(dst) > 0 { 359 into[key] = dst 360 } 361 case string, float64, bool: 362 if !matchesValue(av, bv) { 363 into[key] = bv 364 } 365 case []interface{}: 366 bt := bv.([]interface{}) 367 if !matchesArray(at, bt) { 368 into[key] = bv 369 } 370 case nil: 371 switch bv.(type) { 372 case nil: 373 // Both nil, fine. 374 default: 375 into[key] = bv 376 } 377 default: 378 panic(fmt.Sprintf("Unknown type:%T in key %s", av, key)) 379 } 380 } 381 // Now add all deleted values as nil 382 for key := range a { 383 _, found := b[key] 384 if !found { 385 into[key] = nil 386 } 387 } 388 return into, nil 389} 390