PATCH Generation From a JSON Diff

Editorial illustration in origami-folded style depicting a JSON diff transforming into a JSON Patch with test, remove, add, replace ops

Once you have a diff between two versions of a resource, generating a PATCH to transform one into the other is a small step. Done well, the resulting PATCH is minimal, safe, and applyable by any FHIR server that supports JSON Patch. Done poorly, it produces a PATCH that either fails to apply cleanly or transforms the resource into an unexpected shape. The site's Resource-vs-resource comparator emits JSON Patch as one of its output formats. For the wider FHIR framing, the rest of the FHIR series has more.

JSON Patch (RFC 6902) In A Nutshell

  • op — the operation (add, remove, replace, move, copy, test)
  • path — JSON Pointer to the target
  • value — the payload for add/replace
  • from — source path for move/copy

Six operations. Each targets a JSON Pointer. Combined into an array, they describe a series of transformations.

Diff Output To Patch Output

Every diff entry maps to a patch operation:

  • "field added" → add op
  • "field removed" → remove op
  • "field changed" → replace op
  • "field moved" (rare) → move op

The mapping is nearly one-to-one. Most diff tools can emit patch directly.

Test Operations For Safety

test is the underused RFC 6902 operation. It fails the patch if the target does not match an expected value. Adding a test before every replace protects against concurrent modification:

`` { "op": "test", "path": "/name/0/family", "value": "Smith" } { "op": "replace", "path": "/name/0/family", "value": "Jones" } ``

If someone else changed the family between your read and your write, the test fails and the whole patch rolls back.

For the concurrency framing, when a diff should be a merge conflict is the entry.

Path Format Matters

FHIR PATCH accepts JSON Patch, XML Patch, and FHIRPath Patch. JSON Patch uses JSON Pointer paths (/name/0/family). FHIRPath Patch uses FHIRPath expressions (Patient.name[0].family).

The two are not interchangeable. Pick the format your target server supports. For the split, structural diff vs semantic diff for FHIR JSON touches on the difference.

Minimal Patches

A minimal patch is one with the fewest operations. It is not necessarily the most efficient — sometimes replacing a whole array is fewer bytes than modifying individual elements.

The comparator emits minimal by default. If your server has a size limit on patches, verify the output fits.

When To Use Move

move is convenient when a field's value moved to a different path — reorganizing a resource's shape. But move is not universally supported and can be replaced by remove + add.

Default to the safer form (remove + add) unless move is documented as supported.

Handling Arrays

Arrays are where JSON Patch semantics get subtle:

  • Adding to the end: path = /array/-
  • Adding at position: path = /array/N
  • Replacing an element: path = /array/N with replace
  • Removing an element: path = /array/N with remove

Path indexes reference the current array state after preceding operations. Miscounting indexes produces patches that transform the wrong positions.

For the Bundle case where arrays are especially fiddly, diffing Bundles: aligning entries before comparing is the entry.

PATCH And Optimistic Concurrency

Every FHIR PATCH should include If-Match with the ETag from a prior read. Combined with test operations inside the patch, that gives two layers of concurrency protection.

For the deeper mechanic, JSON Patch is the wire form; If-Match is the HTTP form.

Reversibility

A well-formed patch is reversible: apply the inverse patch to undo. That property is useful for audit trails and rollback logic.

The comparator can emit the inverse patch alongside the forward patch on request.

The Short Version

JSON Patch is the standard. Emit test ops for safety. Prefer remove+add over move. Handle array indexes carefully. Combine with If-Match for concurrency. The comparator produces patches; verify they apply cleanly against a real server.

Origami-folded diagram of a JSON diff transforming into a JSON Patch document with test, remove, add, and replace ops annotated by path, drawn as flat paper-fold shapes with soft-blue accents on cream paper

Sources