Skip to content

Introduce composed JsonPatchOperation as opposed to inherited #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -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;
}
93 changes: 93 additions & 0 deletions src/main/java/com/github/fge/jsonpatch/action/OmitAction.java
Original file line number Diff line number Diff line change
@@ -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<JsonNode> 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();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this not move below this if/else and just return node in the else clause?

if (path.isEmpty()) {
if (EQUIVALENCE.equivalent(ret, value)) {
return MissingNode.getInstance();
} else {
return ret;
}
}
final JsonNode valueAtPath = path.path(ret);
if (valueAtPath.isMissingNode()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you move this above the previous if/else, it feels like this policy could be killed and turned into a "resolver" which gets applied before the "action" that gets called. Might be easier for us to talk about in chat.

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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@ public interface JsonPatchOperationFactory
*/
public String getOperationName();

/**
* Gets the class of JsonPatchOperation that this factory will create.
* @return
*/
public Class<? extends JsonPatchOperation> getOperationClass();

/**
* Creates a JsonPatchOperation from a JsonNode
* @param node The JsonNode to create the JsonPatchOperation from
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<? extends JsonPatchOperation> getOperationClass();

public JsonPatchOperation create(JsonNode node)
throws JsonPatchException
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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<? extends JsonPatchOperation> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
*/
public enum PathMissingPolicy
{
THROW, SKIP
THROW, SKIP, NOT_APPLICABLE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.github.fge.jsonpatch.properties;

import com.fasterxml.jackson.databind.JsonSerializable;

/**
*
*/
interface JsonPatchOperationProperties extends JsonSerializable
{
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)