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"]

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)

Development

bundle install
bundle exec rspec      # Run tests
bundle exec rubocop    # Check code style

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