Skip to content

Commit

Permalink
Merge pull request java-json-tools#10 from jtoelke/remove_optional_op…
Browse files Browse the repository at this point in the history
…eration

Add remove? operation
  • Loading branch information
jtoelke authored Dec 22, 2016
2 parents 61c4eef + c54552f commit 91653fd
Show file tree
Hide file tree
Showing 7 changed files with 275 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public static JsonPatchFactory create()
new NamedType(CopyOperation.class, CopyOperation.OPERATION_NAME),
new NamedType(MoveOperation.class, MoveOperation.OPERATION_NAME),
new NamedType(RemoveOperation.class, RemoveOperation.OPERATION_NAME),
new NamedType(RemoveOptionalOperation.class, RemoveOptionalOperation.OPERATION_NAME),
new NamedType(ReplaceOperation.class, ReplaceOperation.OPERATION_NAME),
new NamedType(TestOperation.class, TestOperation.OPERATION_NAME),
new NamedType(OmitOperation.class, OmitOperation.OPERATION_NAME),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2014, Francis Galiegue ([email protected])
* Copyright (c) 2016, Jessica Beller ([email protected])
*
* This software is dual-licensed under:
*
Expand All @@ -21,90 +22,24 @@

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.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.jsonpointer.JsonPointer;
import com.github.fge.jsonpatch.JsonPatchException;
import com.github.fge.jsonpatch.JsonPatchMessages;
import com.github.fge.jsonpatch.operation.JsonPatchOperation;
import com.github.fge.msgsimple.bundle.MessageBundle;
import com.github.fge.msgsimple.load.MessageBundles;
import com.google.common.collect.Iterables;

import java.io.IOException;
import com.github.fge.jsonpatch.operation.policy.PathMissingPolicy;

/**
* JSON Path {@code remove} operation
*
* <p>This operation only takes one pointer ({@code path}) as an argument. It
* is an error condition if no JSON value exists at that pointer.</p>
*/
public final class RemoveOperation
implements JsonPatchOperation
public final class RemoveOperation extends RemoveOperationBase
{
public static final String OPERATION_NAME = "remove";

protected static final MessageBundle BUNDLE
= MessageBundles.getBundle(JsonPatchMessages.class);

protected final String op;

protected final JsonPointer path;

@JsonCreator
public RemoveOperation(@JsonProperty("path") final JsonPointer path)
{
this.op = OPERATION_NAME;
this.path = path;
super(OPERATION_NAME, path, PathMissingPolicy.THROW);
}

@Override
public JsonNode apply(final JsonNode node)
throws JsonPatchException
{
if (path.isEmpty())
return MissingNode.getInstance();
if (path.path(node).isMissingNode())
throw new JsonPatchException(BUNDLE.getMessage(
"jsonPatch.noSuchPath"));
final JsonNode ret = node.deepCopy();
final JsonNode parentNode = path.parent().get(ret);
final String raw = Iterables.getLast(path).getToken().getRaw();
if (parentNode.isObject())
((ObjectNode) parentNode).remove(raw);
else
((ArrayNode) parentNode).remove(Integer.parseInt(raw));
return ret;
}

@Override
public void serialize(final JsonGenerator jgen,
final SerializerProvider provider)
throws IOException, JsonProcessingException
{
jgen.writeStartObject();
jgen.writeStringField("op", "remove");
jgen.writeStringField("path", path.toString());
jgen.writeEndObject();
}

@Override
public void serializeWithType(final JsonGenerator jgen,
final SerializerProvider provider, final TypeSerializer typeSer)
throws IOException, JsonProcessingException
{
serialize(jgen, provider);
}

@Override
public String toString()
{
return "op: " + op + "; path: \"" + path + '"';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright (c) 2014, Francis Galiegue ([email protected])
* Copyright (c) 2016, Jessica Beller ([email protected])
*
* This software is dual-licensed under:
*
* - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
* later version;
* - the Apache Software License (ASL) version 2.0.
*
* The text of this file and of both licenses is available at the root of this
* project or, if you have the jar distribution, in directory META-INF/, under
* the names LGPL-3.0.txt and ASL-2.0.txt respectively.
*
* Direct link to the sources:
*
* - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
*/

package com.github.fge.jsonpatch.operation;

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.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.jsonpointer.JsonPointer;
import com.github.fge.jsonpatch.JsonPatchException;
import com.github.fge.jsonpatch.JsonPatchMessages;
import com.github.fge.jsonpatch.operation.JsonPatchOperation;
import com.github.fge.jsonpatch.operation.policy.PathMissingPolicy;
import com.github.fge.msgsimple.bundle.MessageBundle;
import com.github.fge.msgsimple.load.MessageBundles;
import com.google.common.collect.Iterables;

import java.io.IOException;

/**
* RemoveOperationBase implements the basic concept of removing the requested path.
*/
public abstract class RemoveOperationBase
implements JsonPatchOperation
{
protected static final MessageBundle BUNDLE
= MessageBundles.getBundle(JsonPatchMessages.class);

private PathMissingPolicy pathMissingPolicy;

protected final String op;

protected final JsonPointer path;

@JsonCreator
public RemoveOperationBase(final String op,
@JsonProperty("path") final JsonPointer path,
final PathMissingPolicy pathMissingPolicy)
{
this.op = op;
this.path = path;
this.pathMissingPolicy = pathMissingPolicy;
}

@Override
public JsonNode apply(final JsonNode node)
throws JsonPatchException
{
final JsonNode ret = node.deepCopy();
if (path.isEmpty())
return MissingNode.getInstance();
if (path.path(node).isMissingNode()) {
switch (pathMissingPolicy) {
case THROW:
throw new JsonPatchException(BUNDLE.getMessage(
"jsonPatch.noSuchPath"));
case SKIP:
return ret;
}
}
final JsonNode parentNode = path.parent().get(ret);
final String raw = Iterables.getLast(path).getToken().getRaw();
if (parentNode.isObject())
((ObjectNode) parentNode).remove(raw);
else
((ArrayNode) parentNode).remove(Integer.parseInt(raw));
return ret;
}

@Override
public void serialize(final JsonGenerator jgen,
final SerializerProvider provider)
throws IOException, JsonProcessingException
{
jgen.writeStartObject();
jgen.writeStringField("op", op);
jgen.writeStringField("path", path.toString());
jgen.writeEndObject();
}

@Override
public void serializeWithType(final JsonGenerator jgen,
final SerializerProvider provider, final TypeSerializer typeSer)
throws IOException, JsonProcessingException
{
serialize(jgen, provider);
}

@Override
public String toString()
{
return "op: " + op + "; path: \"" + path + '"';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2016, Jessica Beller ([email protected])
*
* This software is dual-licensed under:
*
* - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
* later version;
* - the Apache Software License (ASL) version 2.0.
*
* The text of this file and of both licenses is available at the root of this
* project or, if you have the jar distribution, in directory META-INF/, under
* the names LGPL-3.0.txt and ASL-2.0.txt respectively.
*
* Direct link to the sources:
*
* - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
*/

package com.github.fge.jsonpatch.operation;

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;
import com.github.fge.jsonpatch.operation.policy.PathMissingPolicy;

/**
* JSON Path {@code remove?} operation
*
* <p>This operation will remove ({@code path}) if it exists.</p>
*/
public final class RemoveOptionalOperation extends RemoveOperationBase
{
public static final String OPERATION_NAME = "remove?";

@JsonCreator
public RemoveOptionalOperation(@JsonProperty("path") final JsonPointer path)
{
super(OPERATION_NAME, path, PathMissingPolicy.SKIP);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2016, Jessica Beller ([email protected])
*
* This software is dual-licensed under:
*
* - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
* later version;
* - the Apache Software License (ASL) version 2.0.
*
* The text of this file and of both licenses is available at the root of this
* project or, if you have the jar distribution, in directory META-INF/, under
* the names LGPL-3.0.txt and ASL-2.0.txt respectively.
*
* Direct link to the sources:
*
* - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
*/

package com.github.fge.jsonpatch.operation;

import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jackson.JacksonUtils;
import com.github.fge.jackson.jsonpointer.JsonPointer;
import com.github.fge.jsonpatch.JsonPatchException;
import org.testng.annotations.Test;

import java.io.IOException;

import static org.testng.Assert.*;

public final class RemoveOptionalOperationTest
extends ExtendedJsonPatchOperationTest
{
public RemoveOptionalOperationTest()
throws IOException
{
super(RemoveOptionalOperation.OPERATION_NAME);
}

@Test
public void removingRootReturnsMissingNode()
throws JsonPatchException
{
final JsonNode node = JacksonUtils.nodeFactory().nullNode();
final JsonPatchOperation op = new RemoveOptionalOperation(JsonPointer.empty());
final JsonNode ret = op.apply(node);
assertTrue(ret.isMissingNode());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2016, Jessica Beller ([email protected])
*
* This software is dual-licensed under:
*
* - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
* later version;
* - the Apache Software License (ASL) version 2.0.
*
* The text of this file and of both licenses is available at the root of this
* project or, if you have the jar distribution, in directory META-INF/, under
* the names LGPL-3.0.txt and ASL-2.0.txt respectively.
*
* Direct link to the sources:
*
* - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
*/

package com.github.fge.jsonpatch.serialization;

import com.github.fge.jsonpatch.operation.RemoveOptionalOperation;

import java.io.IOException;

public final class RemoveOptionalOperationSerializationTest
extends ExtendedJsonPatchOperationSerializationTest
{
public RemoveOptionalOperationSerializationTest()
throws IOException
{
super(RemoveOptionalOperation.OPERATION_NAME);
}
}
25 changes: 25 additions & 0 deletions src/test/resources/jsonpatch/extended/remove?.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"errors": [],
"ops": [
{
"op": { "op": "remove?", "path": "/x/y" },
"node": { "x": { "a": "b", "y": {} } },
"expected": { "x": { "a": "b" } }
},
{
"op": { "op": "remove?", "path": "/0/2" },
"node": [ [ "a", "b", "c"], "d", "e" ],
"expected": [ [ "a", "b" ], "d", "e" ]
},
{
"op": { "op": "remove?", "path": "/x/0" },
"node": { "x": [ "y", "z" ], "foo": "bar" },
"expected": { "x": [ "z" ], "foo": "bar" }
},
{
"op": { "op": "remove?", "path": "/doesNotExist" },
"node": { "x": "y" },
"expected": { "x": "y" }
}
]
}

0 comments on commit 91653fd

Please sign in to comment.