Low commit activity in last 3 years
Apply and generate JSON patches using RFC 7396 Merge Patch and RFC 6902 JSON Patch. Supports add, remove, replace, move, copy, and test operations.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

philiprehberger-json_merge

Tests Gem Version Last updated

JSON Merge Patch (RFC 7396) and JSON Patch (RFC 6902) for Ruby

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-json_merge"

Or install directly:

gem install philiprehberger-json_merge

Usage

require "philiprehberger/json_merge"

RFC 7396 Merge Patch

target = { "a" => 1, "b" => 2 }
patch  = { "b" => 3, "c" => 4 }

Philiprehberger::JsonMerge.merge_patch(target, patch)
# => {"a"=>1, "b"=>3, "c"=>4}

Remove keys with nil:

Philiprehberger::JsonMerge.merge_patch({ "a" => 1, "b" => 2 }, { "b" => nil })
# => {"a"=>1}

RFC 6902 JSON Patch

doc = { "name" => "Alice", "age" => 30 }
ops = [
  { "op" => "replace", "path" => "/name", "value" => "Bob" },
  { "op" => "add", "path" => "/active", "value" => true },
  { "op" => "remove", "path" => "/age" }
]

Philiprehberger::JsonMerge.apply(doc, ops)
# => {"name"=>"Bob", "active"=>true}

Generate Patches

RFC 6902 diff:

source = { "a" => 1, "b" => 2 }
target = { "a" => 1, "b" => 3, "c" => 4 }

Philiprehberger::JsonMerge.diff(source, target)
# => [{"op"=>"replace", "path"=>"/b", "value"=>3}, {"op"=>"add", "path"=>"/c", "value"=>4}]

RFC 7396 merge diff:

Philiprehberger::JsonMerge.merge_diff(source, target)
# => {"b"=>3, "c"=>4}

Validate

Dry-run patch operations without modifying the document:

result = Philiprehberger::JsonMerge.validate(target, operations)
result[:valid]   # => true/false
result[:errors]  # => ["Operation 0 (remove /missing): ..."]

Invert

Generate reverse operations to undo a patch:

inverse = Philiprehberger::JsonMerge.invert(target, operations)
restored = Philiprehberger::JsonMerge.apply(patched, inverse)
restored == target  # => true

Compact

Remove redundant operations:

optimized = Philiprehberger::JsonMerge.compact(operations)

Paths affected by a patch

Get the sorted, deduplicated list of document paths that a patch sequence would touch. For move and copy ops the from location is included alongside path:

ops = [
  { "op" => "replace", "path" => "/name", "value" => "Bob" },
  { "op" => "move",    "from" => "/age", "path" => "/years" },
  { "op" => "add",     "path" => "/name", "value" => "Bob" }
]

Philiprehberger::JsonMerge.paths(ops)
# => ["/age", "/name", "/years"]

JSON Pointer Read & Write

doc = { "user" => { "profile" => { "email" => "a@b.com" } } }

Philiprehberger::JsonMerge.read(doc, "/user/profile/email")            # => "a@b.com"
Philiprehberger::JsonMerge.read(doc, "/user/missing", default: :gone)  # => :gone

Philiprehberger::JsonMerge.write(doc, "/user/role", "admin")
# doc now: { "user" => { "profile" => {...}, "role" => "admin" } }

# Append to an array
items = { "list" => ["a"] }
Philiprehberger::JsonMerge.write(items, "/list/-", "b")
# items["list"] => ["a", "b"]

API

Method Description
JsonMerge.merge_patch(target, patch) Apply RFC 7396 merge patch
JsonMerge.apply(target, operations) Apply RFC 6902 JSON Patch
JsonMerge.diff(source, target) Generate RFC 6902 patch operations
JsonMerge.merge_diff(source, target) Generate RFC 7396 merge patch
JsonMerge.validate(target, ops) Dry-run operations, return { valid:, errors: }
JsonMerge.invert(target, ops) Generate reverse operations to undo a patch
JsonMerge.compact(ops) Remove redundant operations
JsonMerge.paths(ops) List sorted, deduplicated paths a patch touches (includes from for move/copy)
JsonMerge.read(doc, path, default:) Read a value via RFC 6901 JSON Pointer; returns default when missing
JsonMerge.write(doc, path, value) Write a value at an RFC 6901 JSON Pointer; creates intermediate hashes as needed
JsonMerge.delete(doc, path) Remove the value at an RFC 6901 JSON Pointer; returns the removed value or nil when missing

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT