winget install --id=josephburnett.jd -e
jd is a commandline utility and Go library for diffing and patching JSON and YAML values. It supports a native jd format (similar to unified format) as well as JSON Merge Patch (RFC 7386) and a subset of JSON Patch (RFC 6902). Try it out at http://play.jd-tool.io/.
JD is a command-line utility and Go library designed to diff and patch JSON and YAML values. It supports a native jd format (similar to unified diffs) as well as JSON Merge Patch (RFC 7386) and a subset of JSON Patch (RFC 6902). JD offers human-readable output, minimal array diffs using the LCS algorithm, context around modifications, and compatibility with multiple patch formats. It also includes a WebAssembly-based UI for intuitive interaction without network calls.
Key Features:
Audience & Benefit:
Ideal for developers working with JSON/YAML configurations who need precise control over data structures through structural patching. JD helps maintain consistency and accuracy in configuration management.
JD can be installed via winget, making it accessible across various platforms.
jd
is a commandline utility and Go library for diffing and patching
JSON and YAML values. It supports a native jd
format (similar to
unified format) as well as JSON Merge Patch (RFC
7386) and a subset of
JSON Patch (RFC
6902). Try it out at
http://play.jd-tool.io/.
Diff jd a.json b.json
:
{"foo":["bar","baz"]}
{"foo":["bar","bam","boom"]}
Output:
@ ["foo",1]
"bar"
- "baz"
+ "bam"
+ "boom"
]
GitHub Action:
- name: Diff A and B
id: diff
uses: josephburnett/jd@v2.1.2
with:
args: a.json b.json
- name: Print the diff
run: echo '${{ steps.diff.outputs.output }}'
- name: Check the exit code
run: if [ "${{ steps.diff.outputs.exit_code }}" != "1" ]; then exit 1; fi
To get the jd
commandline utility:
brew install jd
, orgo install github.com/josephburnett/jd/v2/jd@latest
, orjd(){ docker run --rm -i -v $PWD:$PWD -w $PWD josephburnett/jd "$@"; }
.To use the jd
web UI:
jd -port 8080
and visit http://localhost:8080.Note: to include the UI when building from source, use the Makefile.
Usage: jd [OPTION]... FILE1 [FILE2]
Diff and patch JSON files.
Prints the diff of FILE1 and FILE2 to STDOUT.
When FILE2 is omitted the second input is read from STDIN.
When patching (-p) FILE1 is a diff.
Options:
-color Print color diff.
-p Apply patch FILE1 to FILE2 or STDIN.
-o=FILE3 Write to FILE3 instead of STDOUT.
-set Treat arrays as sets.
-mset Treat arrays as multisets (bags).
-setkeys Keys to identify set objects
-yaml Read and write YAML instead of JSON.
-port=N Serve web UI on port N
-precision=N Maximum absolute difference for numbers to be equal.
Example: -precision=0.00001
-f=FORMAT Read and write diff in FORMAT "jd" (default), "patch" (RFC 6902) or
"merge" (RFC 7386)
-t=FORMATS Translate FILE1 between FORMATS. Supported formats are "jd",
"patch" (RFC 6902), "merge" (RFC 7386), "json" and "yaml".
FORMATS are provided as a pair separated by "2". E.g.
"yaml2json" or "jd2patch".
Examples:
jd a.json b.json
cat b.json | jd a.json
jd -o patch a.json b.json; jd patch a.json
jd -set a.json b.json
jd -f patch a.json b.json
jd -f merge a.json b.json
setkeys
This option determines what keys are used to decide if two
objects 'match'. Then the matched objects are compared, which will
return a diff if there are differences in the objects themselves,
their keys and/or values. You shouldn't expect this option to mask or
ignore non-specified keys, it is not intended as a way to 'ignore'
some differences between objects.
Note: import only release commits (v2.Y.Z
) because master
can be unstable.
Note: the v2
library replaces the v1 (lib
) library. V2 adds diff
context, minimal array diffs and hunk-level metadata. However the
format is not backward compatable. You should use v2
.
import (
"fmt"
jd "github.com/josephburnett/jd/v2"
)
func ExampleJsonNode_Diff() {
a, _ := jd.ReadJsonString(`{"foo":["bar"]}`)
b, _ := jd.ReadJsonString(`{"foo":["baz"]}`)
fmt.Print(a.Diff(b).Render())
// Output:
// @ ["foo",0]
// [
// - "bar"
// + "baz"
// ]
}
func ExampleJsonNode_Patch() {
a, _ := jd.ReadJsonString(`["foo"]`)
diff, _ := jd.ReadDiffString(`
@ [1]
"foo"
+ "bar"
]
`)
b, _ := a.Patch(diff)
fmt.Print(b.Json())
// Output:
// ["foo","bar"]
}
Note: this is the v1 grammar. Needs to be updated with v2.
@
header and the path to a node0
) accesses an array"foo"
) accesses an object{}
) accesses an array as a set or multiset-
and then the JSON value to be removed+
and then the JSON value to addedDiff ::= ( '@' '[' ( 'JSON String' | 'JSON Number' | 'Empty JSON Object' )* ']' '\n' ( ( '-' 'JSON Value' '\n' )+ | '+' 'JSON Value' '\n' ) ( '+' 'JSON Value' '\n' )* )*
@ ["a"]
- 1
+ 2
@ [2]
[
+ {"foo":"bar"}
]
@ ["Movies",67,"Title"]
- "Dr. Strangelove"
+ "Dr. Evil Love"
@ ["Movies",67,"Actors","Dr. Strangelove"]
- "Peter Sellers"
+ "Mike Myers"
@ ["Movies",102]
{"Title":"Terminator","Actors":{"Terminator":"Arnold"}}
+ {"Title":"Austin Powers","Actors":{"Austin Powers":"Mike Myers"}}
]
@ ["Movies",67,"Tags",{}]
- "Romance"
+ "Action"
+ "Comedy"
git difftool -yx jd @ -- foo.json
@ ["foo"]
- "bar"
+ "baz"
kubectl get deployment example -oyaml > a.yaml
kubectl edit deployment example
# change cpu resource from 100m to 200m
kubectl get deployment example -oyaml | jd -yaml a.yaml
output:
@ ["metadata","annotations","deployment.kubernetes.io/revision"]
- "2"
+ "3"
@ ["metadata","generation"]
- 2
+ 3
@ ["metadata","resourceVersion"]
- "4661"
+ "5179"
@ ["spec","template","spec","containers",0,"resources","requests","cpu"]
- "100m"
+ "200m"
@ ["status","conditions",1,"lastUpdateTime"]
- "2021-12-23T09:40:39Z"
+ "2021-12-23T09:41:49Z"
@ ["status","conditions",1,"message"]
- "ReplicaSet \"nginx-deployment-787d795676\" has successfully progressed."
+ "ReplicaSet \"nginx-deployment-795c7f5bb\" has successfully progressed."
@ ["status","observedGeneration"]
- 2
+ 3
apply these change to another deployment:
# edit file "patch" to contain only the hunk updating cpu request
kubectl patch deployment example2 --type json --patch "$(jd -t jd2patch ~/patch)"