• Stars
    star
    1,043
  • Rank 44,174 (Top 0.9 %)
  • Language
    Go
  • License
    BSD 3-Clause "New...
  • Created almost 11 years ago
  • Updated 5 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Go library to apply RFC6902 patches and create and apply RFC7386 patches

JSON-Patch

jsonpatch is a library which provides functionality for both applying RFC6902 JSON patches against documents, as well as for calculating & applying RFC7396 JSON merge patches.

GoDoc Build Status Report Card

Get It!

Latest and greatest:

go get -u github.com/evanphx/json-patch/v5

Stable Versions:

  • Version 5: go get -u gopkg.in/evanphx/json-patch.v5
  • Version 4: go get -u gopkg.in/evanphx/json-patch.v4

(previous versions below v3 are unavailable)

Use It!

Configuration

  • There is a global configuration variable jsonpatch.SupportNegativeIndices. This defaults to true and enables the non-standard practice of allowing negative indices to mean indices starting at the end of an array. This functionality can be disabled by setting jsonpatch.SupportNegativeIndices = false.

  • There is a global configuration variable jsonpatch.AccumulatedCopySizeLimit, which limits the total size increase in bytes caused by "copy" operations in a patch. It defaults to 0, which means there is no limit.

These global variables control the behavior of jsonpatch.Apply.

An alternative to jsonpatch.Apply is jsonpatch.ApplyWithOptions whose behavior is controlled by an options parameter of type *jsonpatch.ApplyOptions.

Structure jsonpatch.ApplyOptions includes the configuration options above and adds two new options: AllowMissingPathOnRemove and EnsurePathExistsOnAdd.

When AllowMissingPathOnRemove is set to true, jsonpatch.ApplyWithOptions will ignore remove operations whose path points to a non-existent location in the JSON document. AllowMissingPathOnRemove defaults to false which will lead to jsonpatch.ApplyWithOptions returning an error when hitting a missing path on remove.

When EnsurePathExistsOnAdd is set to true, jsonpatch.ApplyWithOptions will make sure that add operations produce all the path elements that are missing from the target object.

Use jsonpatch.NewApplyOptions to create an instance of jsonpatch.ApplyOptions whose values are populated from the global configuration variables.

Create and apply a merge patch

Given both an original JSON document and a modified JSON document, you can create a Merge Patch document.

It can describe the changes needed to convert from the original to the modified JSON document.

Once you have a merge patch, you can apply it to other JSON documents using the jsonpatch.MergePatch(document, patch) function.

package main

import (
	"fmt"

	jsonpatch "github.com/evanphx/json-patch"
)

func main() {
	// Let's create a merge patch from these two documents...
	original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
	target := []byte(`{"name": "Jane", "age": 24}`)

	patch, err := jsonpatch.CreateMergePatch(original, target)
	if err != nil {
		panic(err)
	}

	// Now lets apply the patch against a different JSON document...

	alternative := []byte(`{"name": "Tina", "age": 28, "height": 3.75}`)
	modifiedAlternative, err := jsonpatch.MergePatch(alternative, patch)

	fmt.Printf("patch document:   %s\n", patch)
	fmt.Printf("updated alternative doc: %s\n", modifiedAlternative)
}

When ran, you get the following output:

$ go run main.go
patch document:   {"height":null,"name":"Jane"}
updated alternative doc: {"age":28,"name":"Jane"}

Create and apply a JSON Patch

You can create patch objects using DecodePatch([]byte), which can then be applied against JSON documents.

The following is an example of creating a patch from two operations, and applying it against a JSON document.

package main

import (
	"fmt"

	jsonpatch "github.com/evanphx/json-patch"
)

func main() {
	original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
	patchJSON := []byte(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)

	patch, err := jsonpatch.DecodePatch(patchJSON)
	if err != nil {
		panic(err)
	}

	modified, err := patch.Apply(original)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Original document: %s\n", original)
	fmt.Printf("Modified document: %s\n", modified)
}

When ran, you get the following output:

$ go run main.go
Original document: {"name": "John", "age": 24, "height": 3.21}
Modified document: {"age":24,"name":"Jane"}

Comparing JSON documents

Due to potential whitespace and ordering differences, one cannot simply compare JSON strings or byte-arrays directly.

As such, you can instead use jsonpatch.Equal(document1, document2) to determine if two JSON documents are structurally equal. This ignores whitespace differences, and key-value ordering.

package main

import (
	"fmt"

	jsonpatch "github.com/evanphx/json-patch"
)

func main() {
	original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
	similar := []byte(`
		{
			"age": 24,
			"height": 3.21,
			"name": "John"
		}
	`)
	different := []byte(`{"name": "Jane", "age": 20, "height": 3.37}`)

	if jsonpatch.Equal(original, similar) {
		fmt.Println(`"original" is structurally equal to "similar"`)
	}

	if !jsonpatch.Equal(original, different) {
		fmt.Println(`"original" is _not_ structurally equal to "different"`)
	}
}

When ran, you get the following output:

$ go run main.go
"original" is structurally equal to "similar"
"original" is _not_ structurally equal to "different"

Combine merge patches

Given two JSON merge patch documents, it is possible to combine them into a single merge patch which can describe both set of changes.

The resulting merge patch can be used such that applying it results in a document structurally similar as merging each merge patch to the document in succession.

package main

import (
	"fmt"

	jsonpatch "github.com/evanphx/json-patch"
)

func main() {
	original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)

	nameAndHeight := []byte(`{"height":null,"name":"Jane"}`)
	ageAndEyes := []byte(`{"age":4.23,"eyes":"blue"}`)

	// Let's combine these merge patch documents...
	combinedPatch, err := jsonpatch.MergeMergePatches(nameAndHeight, ageAndEyes)
	if err != nil {
		panic(err)
	}

	// Apply each patch individual against the original document
	withoutCombinedPatch, err := jsonpatch.MergePatch(original, nameAndHeight)
	if err != nil {
		panic(err)
	}

	withoutCombinedPatch, err = jsonpatch.MergePatch(withoutCombinedPatch, ageAndEyes)
	if err != nil {
		panic(err)
	}

	// Apply the combined patch against the original document

	withCombinedPatch, err := jsonpatch.MergePatch(original, combinedPatch)
	if err != nil {
		panic(err)
	}

	// Do both result in the same thing? They should!
	if jsonpatch.Equal(withCombinedPatch, withoutCombinedPatch) {
		fmt.Println("Both JSON documents are structurally the same!")
	}

	fmt.Printf("combined merge patch: %s", combinedPatch)
}

When ran, you get the following output:

$ go run main.go
Both JSON documents are structurally the same!
combined merge patch: {"age":4.23,"eyes":"blue","height":null,"name":"Jane"}

CLI for comparing JSON documents

You can install the commandline program json-patch.

This program can take multiple JSON patch documents as arguments, and fed a JSON document from stdin. It will apply the patch(es) against the document and output the modified doc.

patch.1.json

[
    {"op": "replace", "path": "/name", "value": "Jane"},
    {"op": "remove", "path": "/height"}
]

patch.2.json

[
    {"op": "add", "path": "/address", "value": "123 Main St"},
    {"op": "replace", "path": "/age", "value": "21"}
]

document.json

{
    "name": "John",
    "age": 24,
    "height": 3.21
}

You can then run:

$ go install github.com/evanphx/json-patch/cmd/json-patch
$ cat document.json | json-patch -p patch.1.json -p patch.2.json
{"address":"123 Main St","age":"21","name":"Jane"}

Help It!

Contributions are welcomed! Leave an issue or create a PR.

Before creating a pull request, we'd ask that you make sure tests are passing and that you have added new tests when applicable.

Contributors can run tests using:

go test -cover ./...

Builds for pull requests are tested automatically using GitHub Actions.

More Repositories

1

benchmark-ips

Provides iteration per second benchmarking for Ruby
Ruby
1,718
star
2

kpeg

A simple PEG library for ruby
Ruby
170
star
3

gx

A set of git tools
Ruby
129
star
4

alexa

Golang interface to the Amazon Alexa Voice service
Go
84
star
5

newrelic-redis

NewRelic instrumentation for redis
Ruby
77
star
6

wildcat

A golang zero-allocation HTTP parser (and eventually http server)
Go
68
star
7

eventd-rfc

A RFC of a syslog replacement
Protocol Buffer
54
star
8

stark

Optimized thrift bindings for ruby
Ruby
52
star
9

benchmark_suite

A set of enhancements to benchmark.rb
Ruby
44
star
10

gemjour

Serve and install gems over Bonjour
Ruby
32
star
11

lost

A ruby wrapper for CoreLocation
Objective-C
29
star
12

ulysses

A thin OS for application goodness
C
28
star
13

distance_between

A RubyMotion App that uses calculates the distance between 2 locations
Ruby
24
star
14

Gauge

A live status viewer for Rubinius
Ruby
22
star
15

prattle

A simple smalltalk frontend to Rubinius
Ruby
21
star
16

orthrus-ssh

A user authentication system built on SSH's key
Ruby
21
star
17

talon

A syntax engine for a series of languages
Ruby
19
star
18

hear

A PortAudio + GCP Speech2text golang library
Go
16
star
19

marius

A dynamic language experiment with a fun VM
C++
14
star
20

heap_dump

Code to read Rubinius HeapDump format
Ruby
13
star
21

ssh

Fork of go's ssh lib
Go
12
star
22

remoteenv

A POC for patching getenv to fetch values from Consul
C
11
star
23

wal

A WAL primitive for Golang
Go
10
star
24

yoke

A VirtualBox fork for the modern world
C
10
star
25

mesh

A Peer to Peer networking package for Go
Go
9
star
26

columbia

WebAssembly based Linux compatible Runtime
WebAssembly
9
star
27

irccat

irccat is like `cat`, but here, the STDOUT is an IRC channel.
Ruby
9
star
28

harq

A simple, high speed message queue with optional message durability
C++
9
star
29

webui

A platform independent wrapper for creating applications using webviews
C
9
star
30

benchmark.fyi

A place to share benchmarking results
Ruby
9
star
31

schubert

A simple systems/configuration management idea
Ruby
8
star
32

go-secretly

A package for storing and retrieving secrets from files, Vault, AWS Parameter Storage, (etc?)
Go
7
star
33

puma-heroku

Puma plugin for easy integration with Heroku
Ruby
7
star
34

marlowe

A language experiment
Ruby
7
star
35

schain

An alternative to envchain that is cross platform
Go
6
star
36

osx-notify

A tiny, ruby like, wrapper for OS X's notifications
Ruby
6
star
37

go-hclog-slog

An adapter from hclog to log/slog
Go
6
star
38

mesh-vpn

Go
5
star
39

redsun

Ruby
5
star
40

inspeqtor

A older checkout of @mperham's inspeqtor to use for Monit's DMCA comparison.
Go
4
star
41

zodiac-prime

A RAFT consensus implementation
Ruby
4
star
42

m13

A dynamic language experiment
Go
4
star
43

rubygems_fp

Rubygems Future Proof APIs
Ruby
4
star
44

lights

Control Phillips Hue lights in Go
Go
4
star
45

ulysses-libc

The libc to go along with the ulysses kernel
C
4
star
46

rivetdb

A simple key/value database backed by log merging
Go
4
star
47

evanphx.github.com

My page
3
star
48

hclogr

Adapter for hclog to the logr protocol
Go
3
star
49

stark-rack

A rack middleware for thrift services
Ruby
3
star
50

party.to

Ruby
3
star
51

dotfiles

Various configuration files
Vim Script
3
star
52

sync

Docker image to sync between 2 directories
Go
3
star
53

go-crypto-dh

Diffie-Hellman algorithm for Go
Go
3
star
54

on_fork

Manager of code to run when a Ruby process forks
Ruby
2
star
55

w3c-css

A CSS spec compliant parser
Ruby
2
star
56

securetunnel

Go
2
star
57

tfe-emp-dev

Terraform Enterprise setup for AWS
HCL
2
star
58

opaqueany

Go
2
star
59

callbox

A twilio app to control my callbox
2
star
60

stark-http

Thrift client protocol for accessing thrift APIs over HTTP
Ruby
2
star
61

evanphx.github.io

blog!
HTML
1
star
62

ficus

LLVM + Lisp!
C++
1
star
63

party.to-website

The party.to website
1
star
64

mesh-shell

Go
1
star
65

pegdown

A markdown parser
Ruby
1
star
66

xlr8r

A turbocharger for ruby 1.8
C
1
star
67

rubinius-website

Rubinius Website
JavaScript
1
star
68

kids-pics

A Toshiba Flashair app and go server
Lua
1
star
69

yfs

A transactional deduping, compressing, encrypting filesystem-like package
Go
1
star
70

pubuser

Go library for fetching basic user and ssh key information from services
Go
1
star
71

blog

The source for my blog
HTML
1
star
72

tree-sitter-hardlight

C
1
star
73

rubygems-lazymirror

A rack app to lazily mirror rubygems infrastructure
Ruby
1
star