Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: maven
directory: /
schedule:
interval: weekly
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Java CI

on:
pull_request:
branches: [ master ]

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 24
uses: actions/setup-java@v4
with:
java-version: '24'
distribution: 'zulu'
- name: Build with Maven
run: mvn -B verify
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ The following guidelines apply to all contributors.

### Making Changes

* Fork the `monstor-cdc/bsonpatch` repository
* Fork the `bsonpatch/bsonpatch` repository
* Make your changes and push them to a topic branch in your fork
* See our commit message guidelines further down in this document
* Submit a pull request to the `monstor-cdc/bsonpatch` repository
* Update `monstor-cdc/bsonpatch` GITHUB issue with the generated pull request link
* Submit a pull request to the `bsonpatch/bsonpatch` repository
* Update `bsonpatch/bsonpatch` GITHUB issue with the generated pull request link

### General Guidelines

Expand All @@ -34,7 +34,7 @@ The following guidelines apply to all contributors.
* The first line should be limited to 50 characters and should not end in a
period.
* Subsequent lines should be wrapped at 72 characters.
* Put `Closes: https://github.com/eBay/bsonpatch/issues/XXX` line at the very
* Put `Closes: https://github.com/bsonpatch/bsonpatch/issues/XXX` line at the very
end (where `XXX` is the actual issue number) if the proposed change is relevant to a tracked issue.

Note: In Git commits the first line of the commit message has special
Expand Down
11 changes: 6 additions & 5 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
bsonpatch library
Copyright 2017,2018,2022 eBay, Inc.
Copyright 2025

This product includes software developed at
eBay, Inc. (https://www.ebay.com/).
This software was ported (forked, renamed, repackaged, modified) from
the bsonpatch project, developed at ebay.com
https://github.com/eBay/bsonpatch

This software was ported (copied, renamed, repackaged, modified) from
Which, in turn, was ported (forked, renamed, repackaged, modified) from
the zjsonpatch project, developed at flipkart.com
https://github.com/flipkart-incubator/zjsonpatch.
https://github.com/flipkart-incubator/zjsonpatch
56 changes: 46 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
# This is an implementation of [RFC 6902 JSON Patch](https://datatracker.ietf.org/doc/html/rfc6902) written in Java.
# This is an implementation of [RFC 6902 JSON Patch](https://datatracker.ietf.org/doc/html/rfc6902) written in Java with extended BSON pointer.

This [JSON Patch](http://jsonpatch.com) implementation works directly with [BSON documents](http://bsonspec.org/) using the [MongoDB Java driver implementation of BSON](https://www.mongodb.com/json-and-bson).

The code here was ported (copied, renamed, repackaged, modified) from the [zjsonpatch project](https://github.com/flipkart-incubator/zjsonpatch).
The code here was forked (copied, renamed, repackaged, modified) from [eBay bsonpatch project](https://github.com/eBay/bsonpatch) which, in turn, was ported (forked, renamed, repackaged, modified) from the [zjsonpatch project](https://github.com/flipkart-incubator/zjsonpatch).

## Description & Use-Cases
- Java Library to find / apply JSON Patches according to [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902).
- JSON Patch defines a JSON document structure for representing changes to a JSON document.
- It can be used to avoid sending a whole document when only a part has changed, thus reducing network bandwidth requirements if data (in JSON format) is required to send across multiple systems over network or in case of multi DC transfer.
- When used in combination with the HTTP PATCH method as per [RFC 5789 HTTP PATCH](https://datatracker.ietf.org/doc/html/rfc5789), it will do partial updates for HTTP APIs in a standard way.
- When used in combination with the HTTP PATCH method as per [RFC 5789 HTTP PATCH](https://datatracker.ietf.org/doc/html/rfc5789), it will do partial updates for HTTP APIs in a standard way.
- Extended JSON pointer functionality (i.e. reference array elements via a key): `/array/id=123/data`
- The user has to ensure that a unique field is used as a reference key. Should there be more than one array
element matching the given key-value pair, the first element will be selected.
- Key based referencing may be slow for large arrays. Hence, standard index based array pointers should be used for large arrays.
- This library compares two [BsonValue](https://mongodb.github.io/mongo-java-driver/3.12/javadoc/org/bson/BsonValue.html) inputs and produces a [BsonArray](https://mongodb.github.io/mongo-java-driver/3.12/javadoc/org/bson/BsonArray.html) of the changes.


### Compatible with : Java 8 and above all versions

## Complexity
- To find JsonPatch : Ω(N+M) ,N and M represents number of keys in first and second JSON respectively / O(summation of la*lb) where la , lb represents JSON Array of length la / lb of against same key in first and second JSON ,since LCS is used to find difference between 2 JSON arrays there of order of quadratic.
- To find JsonPatch : Ω(N+M), N and M represents number of keys in first and second JSON respectively / O(summation of la*lb) where la , lb represents JSON Array of length la / lb of against same key in first and second JSON ,since LCS is used to find difference between 2 JSON arrays there of order of quadratic.
- To Optimize Diffs ( compact move and remove into Move ) : Ω(D) / O(D*D) where D represents number of diffs obtained before compaction into Move operation.
- To Apply Diff : O(D) where D represents number of diffs

### How to use:

### Current Version : 0.4.12
### Current Version : 0.5.0

Add following to `<dependencies/>` section of your pom.xml -

```xml
<dependency>
<groupId>com.ebay.bsonpatch</groupId>
<groupId>io.github.bsonpatch</groupId>
<artifactId>bsonpatch</artifactId>
<version>0.4.12</version>
<version>0.5.0</version>
</dependency>
```

Expand Down Expand Up @@ -78,26 +82,58 @@ Following patch will be returned:
```
here `"op"` represents the operation (`"move"`), `"from"` represent path from where value should be moved, `"path"` represents where value should be moved. The value that is moved is taken as the content at the `"from"` path.

### Extended JSON Pointer Example
JSON
```json
{
"a": [
{
"id": 1,
"data": "abc"
},
{
"id": 2,
"data": "def"
}
]
}
```

JSON path
```jsonpath
/a/id=2/data
```

Following JSON would be returned
```json
"def"
```

### Apply Json Patch In-Place
```xml
BsonPatch.applyInPlace(BsonArray patch, BsonValue source);
```
Given a `patch`, it will apply it to the `source` BSON mutating the instance, opposed to `BsonPatch.apply` which returns
a new instance with the patch applied, leaving the `source` unchanged.

This is an extension to the RFC, and has some additional limitations. Specifically, the source document cannot be fully change in place. This means the following operations are not supported:
* `remove` with an empty or root path;
* `replace` with an empty or root path;
* `move`, `add` or `copy` targeting an empty or root path.

### Tests:
1. 100+ selective hardcoded different input JSONs , with their driver test classes present under /test directory.
2. Apart from selective input, a deterministic random JSON generator is present under ( TestDataGenerator.java ), and its driver test class method is JsonDiffTest.testGeneratedJsonDiff().

#### *** Tests can only show presence of bugs and not their absence ***
#### *** Tests can only show the presence of bugs and not their absence ***

## Get Involved

* **Contributing**: Pull requests are welcome!
* Read [`CONTRIBUTING.md`](CONTRIBUTING.md)
* Submit [github issues](https://github.com/eBay/bsonpatch/issues) for any feature enhancements, bugs or documentation problems
* Submit [github issues](https://github.com/bsonpatch/bsonpatch/issues) for any feature enhancements, bugs or documentation problems

* **Support**: Questions/comments can posted as [github issues](https://github.com/eBay/bsonpatch/issues)
* **Support**: Questions/comments can posted as [github issues](https://github.com/bsonpatch/bsonpatch/issues)

## Maintainers

Expand Down
57 changes: 31 additions & 26 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,25 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.ebay.bsonpatch</groupId>
<groupId>io.github.bsonpatch</groupId>
<artifactId>bsonpatch</artifactId>
<version>0.4.12</version>
<version>0.5.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>${project.groupId}:${project.artifactId}</name>
<description>Java Library to find / apply JSON Patches according to RFC 6902 against BsonValues</description>
<url>https://github.com/ebay/bsonpatch/</url>

<scm>
<connection>scm:git:git://github.com/eBay/bsonpatch.git</connection>
<developerConnection>scm:git:ssh://github.com/eBay/bsonpatch.git</developerConnection>
<url>https://github.com/ebay/bsonpatch</url>
<tag>HEAD</tag>
</scm>
<url>https://github.com/bsonpatch/bsonpatch/</url>

<scm>
<connection>scm:git:git://github.com/bsonpatch/bsonpatch.git</connection>
<developerConnection>scm:git:ssh://github.com:bsonpatch/bsonpatch.git</developerConnection>
<url>https://github.com/bsonpatch/bsonpatch/tree/master</url>
</scm>

<developers>
<developer>
<id>dandoug</id>
<name>Dan Douglas</name>
<email>ddouglas@skibums.org</email>
<organization>eBay</organization>
<organizationUrl>https://www.ebay.com</organizationUrl>
</developer>
</developers>

Expand All @@ -40,14 +36,17 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<compileSource>1.8</compileSource>
<testCompileSource>1.8</testCompileSource>
<junitVersion>5.13.4</junitVersion>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<version>3.14.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
Expand All @@ -61,7 +60,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<version>3.3.1</version>
<executions>
<execution>
<id>attach-sources</id>
Expand Down Expand Up @@ -137,25 +136,31 @@
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.12.10</version>
<version>3.12.14</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.3</version>
<version>4.5.0</version>
</dependency>
<!-- For IOUtils.toString(inputStream, charset) and StringBuilderWriter -->

<dependency>
<scope>test</scope>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<scope>test</scope>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<version>2.20.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junitVersion}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junitVersion}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,16 @@
* under the License.
*/

package com.ebay.bsonpatch;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
package io.github.bsonpatch;

import org.apache.commons.collections4.ListUtils;
import org.bson.BsonArray;
import org.bson.BsonDocument;
import org.bson.BsonString;
import org.bson.BsonValue;

import java.util.*;


public final class BsonDiff {

Expand Down Expand Up @@ -266,7 +261,7 @@ private static JsonPointer updatePathWithCounters(List<Integer> counters, JsonPo
int value = counters.get(i);
if (value != 0) {
int currValue = tokens.get(i).getIndex();
tokens.set(i, new JsonPointer.RefToken(Integer.toString(currValue + value)));
tokens.set(i, JsonPointer.RefToken.parse(Integer.toString(currValue + value)));
}
}
return new JsonPointer(tokens);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,35 @@
* under the License.
*/

package com.ebay.bsonpatch;
package io.github.bsonpatch;

import static com.ebay.bsonpatch.InPlaceApplyProcessor.cloneBsonValue;
import org.bson.BsonArray;
import org.bson.BsonNull;
import org.bson.BsonValue;

import java.util.EnumSet;
import java.util.Iterator;

import org.bson.BsonArray;
import org.bson.BsonNull;
import org.bson.BsonValue;
import static io.github.bsonpatch.InPlaceApplyProcessor.cloneBsonValue;

public final class BsonPatch {

private BsonPatch() {}

private static BsonValue getPatchStringAttr(BsonValue bsonNode, String attr) {
BsonValue child = getPatchAttr(bsonNode, attr);

if (!child.isString())
throw new InvalidBsonPatchException("Invalid JSON Patch payload (non-text '" + attr + "' field)");

return child;
}

private static BsonValue getPatchAttr(BsonValue bsonNode, String attr) {
BsonValue child = bsonNode.asDocument().get(attr);
if (child == null)
throw new InvalidBsonPatchException("Invalid BSON Patch payload (missing '" + attr + "' field)");

return child;
}

Expand All @@ -54,8 +64,8 @@ private static void process(BsonArray patch, BsonPatchProcessor processor, EnumS
while (operations.hasNext()) {
BsonValue bsonNode = operations.next();
if (!bsonNode.isDocument()) throw new InvalidBsonPatchException("Invalid BSON Patch payload (not an object)");
Operation operation = Operation.fromRfcName(getPatchAttr(bsonNode, Constants.OP).asString().getValue().replaceAll("\"", ""));
JsonPointer path = JsonPointer.parse(getPatchAttr(bsonNode, Constants.PATH).asString().getValue());
Operation operation = Operation.fromRfcName(getPatchStringAttr(bsonNode, Constants.OP).asString().getValue().replaceAll("\"", ""));
JsonPointer path = JsonPointer.parse(getPatchStringAttr(bsonNode, Constants.PATH).asString().getValue());

try {
switch (operation) {
Expand Down Expand Up @@ -85,13 +95,13 @@ private static void process(BsonArray patch, BsonPatchProcessor processor, EnumS
}

case MOVE: {
JsonPointer fromPath = JsonPointer.parse(getPatchAttr(bsonNode, Constants.FROM).asString().getValue());
JsonPointer fromPath = JsonPointer.parse(getPatchStringAttr(bsonNode, Constants.FROM).asString().getValue());
processor.move(fromPath, path);
break;
}

case COPY: {
JsonPointer fromPath = JsonPointer.parse(getPatchAttr(bsonNode, Constants.FROM).asString().getValue());
JsonPointer fromPath = JsonPointer.parse(getPatchStringAttr(bsonNode, Constants.FROM).asString().getValue());
processor.copy(fromPath, path);
break;
}
Expand Down
Loading