123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- package jsonpatch
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "reflect"
- )
- func merge(cur, patch *lazyNode, mergeMerge bool) *lazyNode {
- curDoc, err := cur.intoDoc()
- if err != nil {
- pruneNulls(patch)
- return patch
- }
- patchDoc, err := patch.intoDoc()
- if err != nil {
- return patch
- }
- mergeDocs(curDoc, patchDoc, mergeMerge)
- return cur
- }
- func mergeDocs(doc, patch *partialDoc, mergeMerge bool) {
- for k, v := range *patch {
- if v == nil {
- if mergeMerge {
- (*doc)[k] = nil
- } else {
- delete(*doc, k)
- }
- } else {
- cur, ok := (*doc)[k]
- if !ok || cur == nil {
- pruneNulls(v)
- (*doc)[k] = v
- } else {
- (*doc)[k] = merge(cur, v, mergeMerge)
- }
- }
- }
- }
- func pruneNulls(n *lazyNode) {
- sub, err := n.intoDoc()
- if err == nil {
- pruneDocNulls(sub)
- } else {
- ary, err := n.intoAry()
- if err == nil {
- pruneAryNulls(ary)
- }
- }
- }
- func pruneDocNulls(doc *partialDoc) *partialDoc {
- for k, v := range *doc {
- if v == nil {
- delete(*doc, k)
- } else {
- pruneNulls(v)
- }
- }
- return doc
- }
- func pruneAryNulls(ary *partialArray) *partialArray {
- newAry := []*lazyNode{}
- for _, v := range *ary {
- if v != nil {
- pruneNulls(v)
- newAry = append(newAry, v)
- }
- }
- *ary = newAry
- return ary
- }
- var errBadJSONDoc = fmt.Errorf("Invalid JSON Document")
- var errBadJSONPatch = fmt.Errorf("Invalid JSON Patch")
- var errBadMergeTypes = fmt.Errorf("Mismatched JSON Documents")
- // MergeMergePatches merges two merge patches together, such that
- // applying this resulting merged merge patch to a document yields the same
- // as merging each merge patch to the document in succession.
- func MergeMergePatches(patch1Data, patch2Data []byte) ([]byte, error) {
- return doMergePatch(patch1Data, patch2Data, true)
- }
- // MergePatch merges the patchData into the docData.
- func MergePatch(docData, patchData []byte) ([]byte, error) {
- return doMergePatch(docData, patchData, false)
- }
- func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) {
- doc := &partialDoc{}
- docErr := json.Unmarshal(docData, doc)
- patch := &partialDoc{}
- patchErr := json.Unmarshal(patchData, patch)
- if _, ok := docErr.(*json.SyntaxError); ok {
- return nil, errBadJSONDoc
- }
- if _, ok := patchErr.(*json.SyntaxError); ok {
- return nil, errBadJSONPatch
- }
- if docErr == nil && *doc == nil {
- return nil, errBadJSONDoc
- }
- if patchErr == nil && *patch == nil {
- return nil, errBadJSONPatch
- }
- if docErr != nil || patchErr != nil {
- // Not an error, just not a doc, so we turn straight into the patch
- if patchErr == nil {
- if mergeMerge {
- doc = patch
- } else {
- doc = pruneDocNulls(patch)
- }
- } else {
- patchAry := &partialArray{}
- patchErr = json.Unmarshal(patchData, patchAry)
- if patchErr != nil {
- return nil, errBadJSONPatch
- }
- pruneAryNulls(patchAry)
- out, patchErr := json.Marshal(patchAry)
- if patchErr != nil {
- return nil, errBadJSONPatch
- }
- return out, nil
- }
- } else {
- mergeDocs(doc, patch, mergeMerge)
- }
- return json.Marshal(doc)
- }
- // resemblesJSONArray indicates whether the byte-slice "appears" to be
- // a JSON array or not.
- // False-positives are possible, as this function does not check the internal
- // structure of the array. It only checks that the outer syntax is present and
- // correct.
- func resemblesJSONArray(input []byte) bool {
- input = bytes.TrimSpace(input)
- hasPrefix := bytes.HasPrefix(input, []byte("["))
- hasSuffix := bytes.HasSuffix(input, []byte("]"))
- return hasPrefix && hasSuffix
- }
- // CreateMergePatch will return a merge patch document capable of converting
- // the original document(s) to the modified document(s).
- // The parameters can be bytes of either two JSON Documents, or two arrays of
- // JSON documents.
- // The merge patch returned follows the specification defined at http://tools.ietf.org/html/draft-ietf-appsawg-json-merge-patch-07
- func CreateMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) {
- originalResemblesArray := resemblesJSONArray(originalJSON)
- modifiedResemblesArray := resemblesJSONArray(modifiedJSON)
- // Do both byte-slices seem like JSON arrays?
- if originalResemblesArray && modifiedResemblesArray {
- return createArrayMergePatch(originalJSON, modifiedJSON)
- }
- // Are both byte-slices are not arrays? Then they are likely JSON objects...
- if !originalResemblesArray && !modifiedResemblesArray {
- return createObjectMergePatch(originalJSON, modifiedJSON)
- }
- // None of the above? Then return an error because of mismatched types.
- return nil, errBadMergeTypes
- }
- // createObjectMergePatch will return a merge-patch document capable of
- // converting the original document to the modified document.
- func createObjectMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) {
- originalDoc := map[string]interface{}{}
- modifiedDoc := map[string]interface{}{}
- err := json.Unmarshal(originalJSON, &originalDoc)
- if err != nil {
- return nil, errBadJSONDoc
- }
- err = json.Unmarshal(modifiedJSON, &modifiedDoc)
- if err != nil {
- return nil, errBadJSONDoc
- }
- dest, err := getDiff(originalDoc, modifiedDoc)
- if err != nil {
- return nil, err
- }
- return json.Marshal(dest)
- }
- // createArrayMergePatch will return an array of merge-patch documents capable
- // of converting the original document to the modified document for each
- // pair of JSON documents provided in the arrays.
- // Arrays of mismatched sizes will result in an error.
- func createArrayMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) {
- originalDocs := []json.RawMessage{}
- modifiedDocs := []json.RawMessage{}
- err := json.Unmarshal(originalJSON, &originalDocs)
- if err != nil {
- return nil, errBadJSONDoc
- }
- err = json.Unmarshal(modifiedJSON, &modifiedDocs)
- if err != nil {
- return nil, errBadJSONDoc
- }
- total := len(originalDocs)
- if len(modifiedDocs) != total {
- return nil, errBadJSONDoc
- }
- result := []json.RawMessage{}
- for i := 0; i < len(originalDocs); i++ {
- original := originalDocs[i]
- modified := modifiedDocs[i]
- patch, err := createObjectMergePatch(original, modified)
- if err != nil {
- return nil, err
- }
- result = append(result, json.RawMessage(patch))
- }
- return json.Marshal(result)
- }
- // Returns true if the array matches (must be json types).
- // As is idiomatic for go, an empty array is not the same as a nil array.
- func matchesArray(a, b []interface{}) bool {
- if len(a) != len(b) {
- return false
- }
- if (a == nil && b != nil) || (a != nil && b == nil) {
- return false
- }
- for i := range a {
- if !matchesValue(a[i], b[i]) {
- return false
- }
- }
- return true
- }
- // Returns true if the values matches (must be json types)
- // The types of the values must match, otherwise it will always return false
- // If two map[string]interface{} are given, all elements must match.
- func matchesValue(av, bv interface{}) bool {
- if reflect.TypeOf(av) != reflect.TypeOf(bv) {
- return false
- }
- switch at := av.(type) {
- case string:
- bt := bv.(string)
- if bt == at {
- return true
- }
- case float64:
- bt := bv.(float64)
- if bt == at {
- return true
- }
- case bool:
- bt := bv.(bool)
- if bt == at {
- return true
- }
- case nil:
- // Both nil, fine.
- return true
- case map[string]interface{}:
- bt := bv.(map[string]interface{})
- for key := range at {
- if !matchesValue(at[key], bt[key]) {
- return false
- }
- }
- for key := range bt {
- if !matchesValue(at[key], bt[key]) {
- return false
- }
- }
- return true
- case []interface{}:
- bt := bv.([]interface{})
- return matchesArray(at, bt)
- }
- return false
- }
- // getDiff returns the (recursive) difference between a and b as a map[string]interface{}.
- func getDiff(a, b map[string]interface{}) (map[string]interface{}, error) {
- into := map[string]interface{}{}
- for key, bv := range b {
- av, ok := a[key]
- // value was added
- if !ok {
- into[key] = bv
- continue
- }
- // If types have changed, replace completely
- if reflect.TypeOf(av) != reflect.TypeOf(bv) {
- into[key] = bv
- continue
- }
- // Types are the same, compare values
- switch at := av.(type) {
- case map[string]interface{}:
- bt := bv.(map[string]interface{})
- dst := make(map[string]interface{}, len(bt))
- dst, err := getDiff(at, bt)
- if err != nil {
- return nil, err
- }
- if len(dst) > 0 {
- into[key] = dst
- }
- case string, float64, bool:
- if !matchesValue(av, bv) {
- into[key] = bv
- }
- case []interface{}:
- bt := bv.([]interface{})
- if !matchesArray(at, bt) {
- into[key] = bv
- }
- case nil:
- switch bv.(type) {
- case nil:
- // Both nil, fine.
- default:
- into[key] = bv
- }
- default:
- panic(fmt.Sprintf("Unknown type:%T in key %s", av, key))
- }
- }
- // Now add all deleted values as nil
- for key := range a {
- _, found := b[key]
- if !found {
- into[key] = nil
- }
- }
- return into, nil
- }
|