-
Notifications
You must be signed in to change notification settings - Fork 186
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
238 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
src/main/java/com/github/fge/jsonpatch/AdditionOperation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package com.github.fge.jsonpatch; | ||
|
||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.node.ArrayNode; | ||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import com.github.fge.jackson.jsonpointer.JsonPointer; | ||
import com.github.fge.jackson.jsonpointer.ReferenceToken; | ||
import com.github.fge.jackson.jsonpointer.TokenResolver; | ||
import com.google.common.collect.Iterables; | ||
|
||
/** | ||
* Represents an operation that can add a {@code value} given a {@code path}. | ||
*/ | ||
public abstract class AdditionOperation | ||
extends PathValueOperation | ||
{ | ||
private static final ReferenceToken LAST_ARRAY_ELEMENT | ||
= ReferenceToken.fromRaw("-"); | ||
|
||
private boolean overwriteExisting; | ||
|
||
/** | ||
* @param op operation name | ||
* @param path affected path | ||
* @param value value to add | ||
* @param overwriteExisting whether the operation is allowed to overwrite an existing value at the specified path | ||
*/ | ||
protected AdditionOperation(final String op, final JsonPointer path, final JsonNode value, boolean overwriteExisting) | ||
{ | ||
super(op, path, value); | ||
this.overwriteExisting = overwriteExisting; | ||
} | ||
|
||
@Override | ||
public JsonNode apply(final JsonNode node) | ||
throws JsonPatchException | ||
{ | ||
if (path.isEmpty()) | ||
return value; | ||
|
||
/* | ||
* Check the parent node: it must exist and be a container (ie an array | ||
* or an object) for the addition operation to work. | ||
*/ | ||
final JsonNode parentNode = path.parent().path(node); | ||
if (parentNode.isMissingNode()) | ||
throw new JsonPatchException(BUNDLE.getMessage( | ||
"jsonPatch.noSuchParent")); | ||
if (!parentNode.isContainerNode()) | ||
throw new JsonPatchException(BUNDLE.getMessage( | ||
"jsonPatch.parentNotContainer")); | ||
return parentNode.isArray() | ||
? addToArray(path, node) | ||
: addToObject(path, node); | ||
} | ||
|
||
private JsonNode addToArray(final JsonPointer path, final JsonNode node) | ||
throws JsonPatchException | ||
{ | ||
final JsonNode ret = node.deepCopy(); | ||
final ArrayNode target = (ArrayNode) path.parent().get(ret); | ||
final TokenResolver<JsonNode> token = Iterables.getLast(path); | ||
|
||
if (token.getToken().equals(LAST_ARRAY_ELEMENT)) { | ||
target.add(value); | ||
return ret; | ||
} | ||
|
||
final int size = target.size(); | ||
final int index; | ||
try { | ||
index = Integer.parseInt(token.toString()); | ||
} catch (NumberFormatException ignored) { | ||
throw new JsonPatchException(BUNDLE.getMessage( | ||
"jsonPatch.notAnIndex")); | ||
} | ||
|
||
if (index < 0 || index > size) | ||
throw new JsonPatchException(BUNDLE.getMessage( | ||
"jsonPatch.noSuchIndex")); | ||
|
||
target.insert(index, value); | ||
return ret; | ||
} | ||
|
||
private JsonNode addToObject(final JsonPointer path, final JsonNode node) | ||
throws JsonPatchException | ||
{ | ||
if (!overwriteExisting) | ||
{ | ||
final JsonNode existingNode = path.path(node); | ||
if (!existingNode.isMissingNode()) | ||
throw new JsonPatchException(BUNDLE.getMessage( | ||
"jsonPatch.valueAtPathAlreadyExists")); | ||
} | ||
|
||
final JsonNode ret = node.deepCopy(); | ||
final ObjectNode target = (ObjectNode) path.parent().get(ret); | ||
target.put(Iterables.getLast(path).getToken().getRaw(), value); | ||
return ret; | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
src/main/java/com/github/fge/jsonpatch/CreateOperation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.github.fge.jsonpatch; | ||
|
||
import com.fasterxml.jackson.annotation.JsonCreator; | ||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.github.fge.jackson.jsonpointer.JsonPointer; | ||
|
||
/** | ||
* JSON Patch {@code create} operation. | ||
* | ||
* <p>For this operation, {@code path} is the JSON Pointer where the value | ||
* should be added, and {@code value} is the value to add.</p> | ||
* | ||
* <p>This operation behaves like {@code add}, except for JSON Objects, | ||
* where it will raise an error if the target {@code path} points to an | ||
* existing value. This is designed to prevent clients from actually | ||
* overwriting values they don't think exist.</p> | ||
*/ | ||
public final class CreateOperation | ||
extends AdditionOperation | ||
{ | ||
@JsonCreator | ||
public CreateOperation(@JsonProperty("path") final JsonPointer path, | ||
@JsonProperty("value") final JsonNode value) | ||
{ | ||
super("create", path, value, false); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
src/test/java/com/github/fge/jsonpatch/CreateOperationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.github.fge.jsonpatch; | ||
|
||
import java.io.IOException; | ||
|
||
public final class CreateOperationTest | ||
extends JsonPatchOperationTest | ||
{ | ||
public CreateOperationTest() | ||
throws IOException | ||
{ | ||
super("create"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
{ | ||
"errors": [ | ||
{ | ||
"op": { "op": "create", "path": "/a/b/c", "value": 1 }, | ||
"node": { "a": "b" }, | ||
"message": "jsonPatch.noSuchParent" | ||
}, | ||
{ | ||
"op": { "op": "create", "path": "/a", "value": 1 }, | ||
"node": { "a": "b" }, | ||
"message": "jsonPatch.valueAtPathAlreadyExists" | ||
}, | ||
{ | ||
"op": { "op": "create", "path": "/a", "value": 1 }, | ||
"node": { "a": null }, | ||
"message": "jsonPatch.valueAtPathAlreadyExists" | ||
}, | ||
{ | ||
"op": { "op": "create", "path": "/obj/inner/b", "value": [ 1, 2 ] }, | ||
"node": { | ||
"obj": { | ||
"inner": { | ||
"a": "hello", | ||
"b": "world" | ||
} | ||
} | ||
}, | ||
"message": "jsonPatch.valueAtPathAlreadyExists" | ||
}, | ||
{ | ||
"op": { "op": "create", "path": "/~1", "value": 1 }, | ||
"node": [], | ||
"message": "jsonPatch.notAnIndex" | ||
}, | ||
{ | ||
"op": { "op": "create", "path": "/3", "value": 1 }, | ||
"node": [ 1, 2 ], | ||
"message": "jsonPatch.noSuchIndex" | ||
}, | ||
{ | ||
"op": { "op": "create", "path": "/-2", "value": 1 }, | ||
"node": [ 1, 2 ], | ||
"message": "jsonPatch.noSuchIndex" | ||
}, | ||
{ | ||
"op": { "op": "create", "path": "/foo/f", "value": "bar" }, | ||
"node": { "foo": "bar" }, | ||
"message": "jsonPatch.parentNotContainer" | ||
} | ||
], | ||
"ops": [ | ||
{ | ||
"op": { "op": "create", "path": "", "value": null }, | ||
"node": {}, | ||
"expected": null | ||
}, | ||
{ | ||
"op": { "op": "create", "path": "/a", "value": "b" }, | ||
"node": {}, | ||
"expected": { "a": "b" } | ||
}, | ||
{ | ||
"op": { "op": "create", "path": "/array/-", "value": 1 }, | ||
"node": { "array": [ 2, null, {}, 1 ] }, | ||
"expected": { "array": [ 2, null, {}, 1, 1 ] } | ||
}, | ||
{ | ||
"op": { "op": "create", "path": "/array/2", "value": "hello" }, | ||
"node": { "array": [ 2, null, {}, 1] }, | ||
"expected": { "array": [ 2, null, "hello", {}, 1 ] } | ||
}, | ||
{ | ||
"op": { "op": "create", "path": "/obj/inner/b", "value": [ 1, 2 ] }, | ||
"node": { | ||
"obj": { | ||
"inner": { | ||
"a": "hello" | ||
} | ||
} | ||
}, | ||
"expected": { | ||
"obj": { | ||
"inner": { | ||
"a": "hello", | ||
"b": [ 1, 2 ] | ||
} | ||
} | ||
} | ||
} | ||
] | ||
} |