diff --git a/src/main/java/com/github/fge/jsonpatch/action/JsonPatchOperationAction.java b/src/main/java/com/github/fge/jsonpatch/action/JsonPatchOperationAction.java new file mode 100644 index 00000000..339613eb --- /dev/null +++ b/src/main/java/com/github/fge/jsonpatch/action/JsonPatchOperationAction.java @@ -0,0 +1,15 @@ +package com.github.fge.jsonpatch.action; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializable; +import com.github.fge.jsonpatch.JsonPatchException; +import com.github.fge.jsonpatch.operation.policy.PathMissingPolicy; + +/** + * + */ +public interface JsonPatchOperationAction extends JsonSerializable +{ + public JsonNode apply(final JsonNode node, final PathMissingPolicy pathMissingPolicy) + throws JsonPatchException; +} diff --git a/src/main/java/com/github/fge/jsonpatch/action/OmitAction.java b/src/main/java/com/github/fge/jsonpatch/action/OmitAction.java new file mode 100644 index 00000000..0a995cd7 --- /dev/null +++ b/src/main/java/com/github/fge/jsonpatch/action/OmitAction.java @@ -0,0 +1,93 @@ +package com.github.fge.jsonpatch.action; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializable; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.MissingNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.fge.jackson.JsonNumEquals; +import com.github.fge.jackson.jsonpointer.JsonPointer; +import com.github.fge.jsonpatch.JsonPatchException; +import com.github.fge.jsonpatch.JsonPatchMessages; +import com.github.fge.jsonpatch.operation.policy.PathMissingPolicy; +import com.github.fge.jsonpatch.properties.PathValueProperties; +import com.github.fge.msgsimple.bundle.MessageBundle; +import com.github.fge.msgsimple.load.MessageBundles; +import com.google.common.base.Equivalence; +import com.google.common.collect.Iterables; + +import java.io.IOException; + +public final class OmitAction + implements JsonPatchOperationAction +{ + private final PathValueProperties properties; + + private static final MessageBundle BUNDLE + = MessageBundles.getBundle(JsonPatchMessages.class); + private static final Equivalence EQUIVALENCE + = JsonNumEquals.getInstance(); + + public OmitAction(final PathValueProperties properties) + { + this.properties = properties; + } + + public JsonNode apply(final JsonNode node, final PathMissingPolicy pathMissingPolicy) + throws JsonPatchException + { + final String op = properties.getOp(); + final JsonPointer path = properties.getPath(); + final JsonNode value = properties.getValue(); + + final JsonNode ret = node.deepCopy(); + if (path.isEmpty()) { + if (EQUIVALENCE.equivalent(ret, value)) { + return MissingNode.getInstance(); + } else { + return ret; + } + } + final JsonNode valueAtPath = path.path(ret); + if (valueAtPath.isMissingNode()) { + switch (pathMissingPolicy) { + case THROW: + throw new JsonPatchException(BUNDLE.getMessage( + "jsonPatch.noSuchPath")); + case SKIP: + return ret; + default: + throw new JsonPatchException(BUNDLE.getMessage( + "jsonPatch.invalidPolicy")); + } + } + + if (EQUIVALENCE.equivalent(valueAtPath, value)) { + final JsonNode parent = path.parent().get(ret); + final String rawToken = Iterables.getLast(path).getToken().getRaw(); + if (parent.isObject()) + ((ObjectNode) parent).remove(rawToken); + else + ((ArrayNode) parent).remove(Integer.parseInt(rawToken)); + } + return ret; + } + + public final void serialize(final JsonGenerator jgen, + final SerializerProvider provider) + throws IOException, JsonProcessingException + { + properties.serialize(jgen, provider); + } + + public final void serializeWithType(final JsonGenerator jgen, + final SerializerProvider provider, final TypeSerializer typeSer) + throws IOException, JsonProcessingException + { + properties.serializeWithType(jgen, provider, typeSer); + } +} diff --git a/src/main/java/com/github/fge/jsonpatch/operation/JsonPatchOperationComposed.java b/src/main/java/com/github/fge/jsonpatch/operation/JsonPatchOperationComposed.java new file mode 100644 index 00000000..a2e5bd5c --- /dev/null +++ b/src/main/java/com/github/fge/jsonpatch/operation/JsonPatchOperationComposed.java @@ -0,0 +1,47 @@ +package com.github.fge.jsonpatch.operation; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.github.fge.jsonpatch.JsonPatchException; +import com.github.fge.jsonpatch.action.JsonPatchOperationAction; +import com.github.fge.jsonpatch.operation.policy.PathMissingPolicy; + +import java.io.IOException; + +/** + * TODO: rename me! + */ +public class JsonPatchOperationComposed implements JsonPatchOperation +{ + private JsonPatchOperationAction action; + private PathMissingPolicy pathMissingPolicy; + + public JsonPatchOperationComposed(JsonPatchOperationAction action, PathMissingPolicy pathMissingPolicy) + { + this.action = action; + this.pathMissingPolicy = pathMissingPolicy; + } + + public JsonNode apply(final JsonNode node) + throws JsonPatchException + { + return action.apply(node, pathMissingPolicy); + } + + public final void serialize(final JsonGenerator jgen, + final SerializerProvider provider) + throws IOException, JsonProcessingException + { + action.serialize(jgen, provider); + } + + public final void serializeWithType(final JsonGenerator jgen, + final SerializerProvider provider, final TypeSerializer typeSer) + throws IOException, JsonProcessingException + { + action.serializeWithType(jgen, provider, typeSer); + } +} diff --git a/src/main/java/com/github/fge/jsonpatch/operation/JsonPatchOperationFactory.java b/src/main/java/com/github/fge/jsonpatch/operation/JsonPatchOperationFactory.java index 849ee037..9d059fbb 100644 --- a/src/main/java/com/github/fge/jsonpatch/operation/JsonPatchOperationFactory.java +++ b/src/main/java/com/github/fge/jsonpatch/operation/JsonPatchOperationFactory.java @@ -11,12 +11,6 @@ public interface JsonPatchOperationFactory */ public String getOperationName(); - /** - * Gets the class of JsonPatchOperation that this factory will create. - * @return - */ - public Class getOperationClass(); - /** * Creates a JsonPatchOperation from a JsonNode * @param node The JsonNode to create the JsonPatchOperation from diff --git a/src/main/java/com/github/fge/jsonpatch/operation/JsonPatchOperationFactoryBase.java b/src/main/java/com/github/fge/jsonpatch/operation/JsonPatchOperationFactoryBase.java index 6b04e789..ffb04ed2 100644 --- a/src/main/java/com/github/fge/jsonpatch/operation/JsonPatchOperationFactoryBase.java +++ b/src/main/java/com/github/fge/jsonpatch/operation/JsonPatchOperationFactoryBase.java @@ -18,6 +18,12 @@ public abstract class JsonPatchOperationFactoryBase implements JsonPatchOperatio private static final MessageBundle BUNDLE = MessageBundles.getBundle(JsonPatchMessages.class); + /** + * Gets the class of JsonPatchOperation that this factory will create. + * @return + */ + public abstract Class getOperationClass(); + public JsonPatchOperation create(JsonNode node) throws JsonPatchException { diff --git a/src/main/java/com/github/fge/jsonpatch/operation/OmitOperationFactory.java b/src/main/java/com/github/fge/jsonpatch/operation/OmitOperationFactory.java index 69acd39b..902e7bc7 100644 --- a/src/main/java/com/github/fge/jsonpatch/operation/OmitOperationFactory.java +++ b/src/main/java/com/github/fge/jsonpatch/operation/OmitOperationFactory.java @@ -1,13 +1,34 @@ package com.github.fge.jsonpatch.operation; -public final class OmitOperationFactory extends JsonPatchOperationFactoryBase +import com.fasterxml.jackson.databind.JsonNode; +import com.github.fge.jackson.JacksonUtils; +import com.github.fge.jsonpatch.JsonPatchException; +import com.github.fge.jsonpatch.JsonPatchMessages; +import com.github.fge.jsonpatch.action.OmitAction; +import com.github.fge.jsonpatch.operation.policy.PathMissingPolicy; +import com.github.fge.jsonpatch.properties.PathValueProperties; +import com.github.fge.msgsimple.bundle.MessageBundle; +import com.github.fge.msgsimple.load.MessageBundles; + +import java.io.IOException; + +public final class OmitOperationFactory implements JsonPatchOperationFactory { + private static final MessageBundle BUNDLE + = MessageBundles.getBundle(JsonPatchMessages.class); + public String getOperationName() { return OmitOperation.OPERATION_NAME; } - public Class getOperationClass() + public JsonPatchOperation create(JsonNode node) + throws JsonPatchException { - return OmitOperation.class; + try { + PathValueProperties properties = JacksonUtils.getReader().withType(PathValueProperties.class).readValue(node); + return new JsonPatchOperationComposed(new OmitAction(properties), PathMissingPolicy.THROW); + } catch (IOException e) { + throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.deserFailed"), e); + } } } diff --git a/src/main/java/com/github/fge/jsonpatch/operation/policy/PathMissingPolicy.java b/src/main/java/com/github/fge/jsonpatch/operation/policy/PathMissingPolicy.java index 506e3a54..72d1bb9d 100644 --- a/src/main/java/com/github/fge/jsonpatch/operation/policy/PathMissingPolicy.java +++ b/src/main/java/com/github/fge/jsonpatch/operation/policy/PathMissingPolicy.java @@ -5,5 +5,5 @@ */ public enum PathMissingPolicy { - THROW, SKIP + THROW, SKIP, NOT_APPLICABLE } diff --git a/src/main/java/com/github/fge/jsonpatch/properties/JsonPatchOperationProperties.java b/src/main/java/com/github/fge/jsonpatch/properties/JsonPatchOperationProperties.java new file mode 100644 index 00000000..16a8da37 --- /dev/null +++ b/src/main/java/com/github/fge/jsonpatch/properties/JsonPatchOperationProperties.java @@ -0,0 +1,10 @@ +package com.github.fge.jsonpatch.properties; + +import com.fasterxml.jackson.databind.JsonSerializable; + +/** + * + */ +interface JsonPatchOperationProperties extends JsonSerializable +{ +} diff --git a/src/main/java/com/github/fge/jsonpatch/properties/PathValueProperties.java b/src/main/java/com/github/fge/jsonpatch/properties/PathValueProperties.java new file mode 100644 index 00000000..e93644de --- /dev/null +++ b/src/main/java/com/github/fge/jsonpatch/properties/PathValueProperties.java @@ -0,0 +1,87 @@ +package com.github.fge.jsonpatch.properties; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.github.fge.jackson.jsonpointer.JsonPointer; +import com.github.fge.jsonpatch.JsonPatchMessages; +import com.github.fge.msgsimple.bundle.MessageBundle; +import com.github.fge.msgsimple.load.MessageBundles; + +import java.io.IOException; + +/** + * + */ +public class PathValueProperties + implements JsonPatchOperationProperties +{ + protected static final MessageBundle BUNDLE + = MessageBundles.getBundle(JsonPatchMessages.class); + + protected final String op; + + protected final JsonPointer path; + + @JsonSerialize + protected final JsonNode value; + + /** + * @param op operation name + * @param path affected path + * @param value JSON value + */ + @JsonCreator + public PathValueProperties(@JsonProperty("op") final String op, + @JsonProperty("path") final JsonPointer path, + @JsonProperty("value") final JsonNode value) + { + this.op = op; + this.path = path; + this.value = value.deepCopy(); + } + + public String getOp() + { + return op; + } + + public JsonPointer getPath() + { + return path; + } + + public JsonNode getValue() + { + return value.deepCopy(); + } + + public final void serialize(final JsonGenerator jgen, + final SerializerProvider provider) + throws IOException, JsonProcessingException + { + jgen.writeStartObject(); + jgen.writeStringField("op", op); + jgen.writeStringField("path", path.toString()); + jgen.writeFieldName("value"); + jgen.writeTree(value); + jgen.writeEndObject(); + } + + public final void serializeWithType(final JsonGenerator jgen, + final SerializerProvider provider, final TypeSerializer typeSer) + throws IOException, JsonProcessingException + { + serialize(jgen, provider); + } + + public final String toString() + { + return "op: " + op + "; path: \"" + path + "\"; value: " + value; + } +} diff --git a/src/main/resources/com/github/fge/jsonpatch/messages.properties b/src/main/resources/com/github/fge/jsonpatch/messages.properties index 668240ca..4dfb36c3 100644 --- a/src/main/resources/com/github/fge/jsonpatch/messages.properties +++ b/src/main/resources/com/github/fge/jsonpatch/messages.properties @@ -27,4 +27,5 @@ jsonPatch.noSuchIndex=no such index in target array jsonPatch.noSuchPath=no such path in target JSON document jsonPatch.parentNotContainer=parent of path to add to is not a container jsonPatch.valueTestFailure=value differs from expectations +jsonPatch.invalidPolicy=invalid json operation policy mergePatch.notContainer=value is neither an object or an array (found %s)