...

Source file src/gomodules.xyz/jsonpatch/v2/jsonpatch.go

Documentation: gomodules.xyz/jsonpatch/v2

     1  package jsonpatch
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"reflect"
     8  	"strings"
     9  )
    10  
    11  var errBadJSONDoc = fmt.Errorf("invalid JSON Document")
    12  
    13  type JsonPatchOperation = Operation
    14  
    15  type Operation struct {
    16  	Operation string      `json:"op"`
    17  	Path      string      `json:"path"`
    18  	Value     interface{} `json:"value,omitempty"`
    19  }
    20  
    21  func (j *Operation) Json() string {
    22  	b, _ := json.Marshal(j)
    23  	return string(b)
    24  }
    25  
    26  func (j *Operation) MarshalJSON() ([]byte, error) {
    27  	// Ensure for add and replace we emit `value: null`
    28  	if j.Value == nil && (j.Operation == "replace" || j.Operation == "add") {
    29  		return json.Marshal(struct {
    30  			Operation string      `json:"op"`
    31  			Path      string      `json:"path"`
    32  			Value     interface{} `json:"value"`
    33  		}{
    34  			Operation: j.Operation,
    35  			Path:      j.Path,
    36  		})
    37  	}
    38  	// otherwise just marshal normally. We cannot literally do json.Marshal(j) as it would be recursively
    39  	// calling this function.
    40  	return json.Marshal(struct {
    41  		Operation string      `json:"op"`
    42  		Path      string      `json:"path"`
    43  		Value     interface{} `json:"value,omitempty"`
    44  	}{
    45  		Operation: j.Operation,
    46  		Path:      j.Path,
    47  		Value:     j.Value,
    48  	})
    49  }
    50  
    51  type ByPath []Operation
    52  
    53  func (a ByPath) Len() int           { return len(a) }
    54  func (a ByPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
    55  func (a ByPath) Less(i, j int) bool { return a[i].Path < a[j].Path }
    56  
    57  func NewOperation(op, path string, value interface{}) Operation {
    58  	return Operation{Operation: op, Path: path, Value: value}
    59  }
    60  
    61  // CreatePatch creates a patch as specified in http://jsonpatch.com/
    62  //
    63  // 'a' is original, 'b' is the modified document. Both are to be given as json encoded content.
    64  // The function will return an array of JsonPatchOperations
    65  //
    66  // An error will be returned if any of the two documents are invalid.
    67  func CreatePatch(a, b []byte) ([]Operation, error) {
    68  	if bytes.Equal(a, b) {
    69  		return []Operation{}, nil
    70  	}
    71  	var aI interface{}
    72  	var bI interface{}
    73  	err := json.Unmarshal(a, &aI)
    74  	if err != nil {
    75  		return nil, errBadJSONDoc
    76  	}
    77  	err = json.Unmarshal(b, &bI)
    78  	if err != nil {
    79  		return nil, errBadJSONDoc
    80  	}
    81  	return handleValues(aI, bI, "", []Operation{})
    82  }
    83  
    84  // Returns true if the values matches (must be json types)
    85  // The types of the values must match, otherwise it will always return false
    86  // If two map[string]interface{} are given, all elements must match.
    87  func matchesValue(av, bv interface{}) bool {
    88  	if reflect.TypeOf(av) != reflect.TypeOf(bv) {
    89  		return false
    90  	}
    91  	switch at := av.(type) {
    92  	case string:
    93  		bt, ok := bv.(string)
    94  		if ok && bt == at {
    95  			return true
    96  		}
    97  	case float64:
    98  		bt, ok := bv.(float64)
    99  		if ok && bt == at {
   100  			return true
   101  		}
   102  	case bool:
   103  		bt, ok := bv.(bool)
   104  		if ok && bt == at {
   105  			return true
   106  		}
   107  	case map[string]interface{}:
   108  		bt, ok := bv.(map[string]interface{})
   109  		if !ok {
   110  			return false
   111  		}
   112  		for key := range at {
   113  			if !matchesValue(at[key], bt[key]) {
   114  				return false
   115  			}
   116  		}
   117  		for key := range bt {
   118  			if !matchesValue(at[key], bt[key]) {
   119  				return false
   120  			}
   121  		}
   122  		return true
   123  	case []interface{}:
   124  		bt, ok := bv.([]interface{})
   125  		if !ok {
   126  			return false
   127  		}
   128  		if len(bt) != len(at) {
   129  			return false
   130  		}
   131  		for key := range at {
   132  			if !matchesValue(at[key], bt[key]) {
   133  				return false
   134  			}
   135  		}
   136  		for key := range bt {
   137  			if !matchesValue(at[key], bt[key]) {
   138  				return false
   139  			}
   140  		}
   141  		return true
   142  	}
   143  	return false
   144  }
   145  
   146  // From http://tools.ietf.org/html/rfc6901#section-4 :
   147  //
   148  // Evaluation of each reference token begins by decoding any escaped
   149  // character sequence.  This is performed by first transforming any
   150  // occurrence of the sequence '~1' to '/', and then transforming any
   151  // occurrence of the sequence '~0' to '~'.
   152  //   TODO decode support:
   153  //   var rfc6901Decoder = strings.NewReplacer("~1", "/", "~0", "~")
   154  
   155  var rfc6901Encoder = strings.NewReplacer("~", "~0", "/", "~1")
   156  
   157  func makePath(path string, newPart interface{}) string {
   158  	key := rfc6901Encoder.Replace(fmt.Sprintf("%v", newPart))
   159  	if path == "" {
   160  		return "/" + key
   161  	}
   162  	return path + "/" + key
   163  }
   164  
   165  // diff returns the (recursive) difference between a and b as an array of JsonPatchOperations.
   166  func diff(a, b map[string]interface{}, path string, patch []Operation) ([]Operation, error) {
   167  	for key, bv := range b {
   168  		p := makePath(path, key)
   169  		av, ok := a[key]
   170  		// value was added
   171  		if !ok {
   172  			patch = append(patch, NewOperation("add", p, bv))
   173  			continue
   174  		}
   175  		// Types are the same, compare values
   176  		var err error
   177  		patch, err = handleValues(av, bv, p, patch)
   178  		if err != nil {
   179  			return nil, err
   180  		}
   181  	}
   182  	// Now add all deleted values as nil
   183  	for key := range a {
   184  		_, found := b[key]
   185  		if !found {
   186  			p := makePath(path, key)
   187  
   188  			patch = append(patch, NewOperation("remove", p, nil))
   189  		}
   190  	}
   191  	return patch, nil
   192  }
   193  
   194  func handleValues(av, bv interface{}, p string, patch []Operation) ([]Operation, error) {
   195  	{
   196  		at := reflect.TypeOf(av)
   197  		bt := reflect.TypeOf(bv)
   198  		if at == nil && bt == nil {
   199  			// do nothing
   200  			return patch, nil
   201  		} else if at != bt {
   202  			// If types have changed, replace completely (preserves null in destination)
   203  			return append(patch, NewOperation("replace", p, bv)), nil
   204  		}
   205  	}
   206  
   207  	var err error
   208  	switch at := av.(type) {
   209  	case map[string]interface{}:
   210  		bt := bv.(map[string]interface{})
   211  		patch, err = diff(at, bt, p, patch)
   212  		if err != nil {
   213  			return nil, err
   214  		}
   215  	case string, float64, bool:
   216  		if !matchesValue(av, bv) {
   217  			patch = append(patch, NewOperation("replace", p, bv))
   218  		}
   219  	case []interface{}:
   220  		bt := bv.([]interface{})
   221  		n := min(len(at), len(bt))
   222  		for i := len(at) - 1; i >= n; i-- {
   223  			patch = append(patch, NewOperation("remove", makePath(p, i), nil))
   224  		}
   225  		for i := n; i < len(bt); i++ {
   226  			patch = append(patch, NewOperation("add", makePath(p, i), bt[i]))
   227  		}
   228  		for i := 0; i < n; i++ {
   229  			var err error
   230  			patch, err = handleValues(at[i], bt[i], makePath(p, i), patch)
   231  			if err != nil {
   232  				return nil, err
   233  			}
   234  		}
   235  	default:
   236  		panic(fmt.Sprintf("Unknown type:%T ", av))
   237  	}
   238  	return patch, nil
   239  }
   240  
   241  func min(x int, y int) int {
   242  	if y < x {
   243  		return y
   244  	}
   245  	return x
   246  }
   247  

View as plain text