Skip to content

Add remove? operation #10

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

Merged
merged 1 commit into from
Dec 22, 2016
Merged
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
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" }
}
]
}