Skip to content

Commit 91653fd

Browse files
authored
Merge pull request java-json-tools#10 from jtoelke/remove_optional_operation
Add remove? operation
2 parents 61c4eef + c54552f commit 91653fd

File tree

7 files changed

+275
-69
lines changed

7 files changed

+275
-69
lines changed

src/main/java/com/github/fge/jsonpatch/ExtendedJsonPatchFactory.java

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public static JsonPatchFactory create()
2525
new NamedType(CopyOperation.class, CopyOperation.OPERATION_NAME),
2626
new NamedType(MoveOperation.class, MoveOperation.OPERATION_NAME),
2727
new NamedType(RemoveOperation.class, RemoveOperation.OPERATION_NAME),
28+
new NamedType(RemoveOptionalOperation.class, RemoveOptionalOperation.OPERATION_NAME),
2829
new NamedType(ReplaceOperation.class, ReplaceOperation.OPERATION_NAME),
2930
new NamedType(TestOperation.class, TestOperation.OPERATION_NAME),
3031
new NamedType(OmitOperation.class, OmitOperation.OPERATION_NAME),
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
* Copyright (c) 2014, Francis Galiegue ([email protected])
3+
* Copyright (c) 2016, Jessica Beller ([email protected])
34
*
45
* This software is dual-licensed under:
56
*
@@ -21,90 +22,24 @@
2122

2223
import com.fasterxml.jackson.annotation.JsonCreator;
2324
import com.fasterxml.jackson.annotation.JsonProperty;
24-
import com.fasterxml.jackson.core.JsonGenerator;
25-
import com.fasterxml.jackson.core.JsonProcessingException;
2625
import com.fasterxml.jackson.databind.JsonNode;
27-
import com.fasterxml.jackson.databind.SerializerProvider;
28-
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
29-
import com.fasterxml.jackson.databind.node.ArrayNode;
30-
import com.fasterxml.jackson.databind.node.MissingNode;
31-
import com.fasterxml.jackson.databind.node.ObjectNode;
3226
import com.github.fge.jackson.jsonpointer.JsonPointer;
33-
import com.github.fge.jsonpatch.JsonPatchException;
34-
import com.github.fge.jsonpatch.JsonPatchMessages;
35-
import com.github.fge.jsonpatch.operation.JsonPatchOperation;
36-
import com.github.fge.msgsimple.bundle.MessageBundle;
37-
import com.github.fge.msgsimple.load.MessageBundles;
38-
import com.google.common.collect.Iterables;
39-
40-
import java.io.IOException;
27+
import com.github.fge.jsonpatch.operation.policy.PathMissingPolicy;
4128

4229
/**
4330
* JSON Path {@code remove} operation
4431
*
4532
* <p>This operation only takes one pointer ({@code path}) as an argument. It
4633
* is an error condition if no JSON value exists at that pointer.</p>
4734
*/
48-
public final class RemoveOperation
49-
implements JsonPatchOperation
35+
public final class RemoveOperation extends RemoveOperationBase
5036
{
5137
public static final String OPERATION_NAME = "remove";
5238

53-
protected static final MessageBundle BUNDLE
54-
= MessageBundles.getBundle(JsonPatchMessages.class);
55-
56-
protected final String op;
57-
58-
protected final JsonPointer path;
59-
6039
@JsonCreator
6140
public RemoveOperation(@JsonProperty("path") final JsonPointer path)
6241
{
63-
this.op = OPERATION_NAME;
64-
this.path = path;
42+
super(OPERATION_NAME, path, PathMissingPolicy.THROW);
6543
}
6644

67-
@Override
68-
public JsonNode apply(final JsonNode node)
69-
throws JsonPatchException
70-
{
71-
if (path.isEmpty())
72-
return MissingNode.getInstance();
73-
if (path.path(node).isMissingNode())
74-
throw new JsonPatchException(BUNDLE.getMessage(
75-
"jsonPatch.noSuchPath"));
76-
final JsonNode ret = node.deepCopy();
77-
final JsonNode parentNode = path.parent().get(ret);
78-
final String raw = Iterables.getLast(path).getToken().getRaw();
79-
if (parentNode.isObject())
80-
((ObjectNode) parentNode).remove(raw);
81-
else
82-
((ArrayNode) parentNode).remove(Integer.parseInt(raw));
83-
return ret;
84-
}
85-
86-
@Override
87-
public void serialize(final JsonGenerator jgen,
88-
final SerializerProvider provider)
89-
throws IOException, JsonProcessingException
90-
{
91-
jgen.writeStartObject();
92-
jgen.writeStringField("op", "remove");
93-
jgen.writeStringField("path", path.toString());
94-
jgen.writeEndObject();
95-
}
96-
97-
@Override
98-
public void serializeWithType(final JsonGenerator jgen,
99-
final SerializerProvider provider, final TypeSerializer typeSer)
100-
throws IOException, JsonProcessingException
101-
{
102-
serialize(jgen, provider);
103-
}
104-
105-
@Override
106-
public String toString()
107-
{
108-
return "op: " + op + "; path: \"" + path + '"';
109-
}
11045
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright (c) 2014, Francis Galiegue ([email protected])
3+
* Copyright (c) 2016, Jessica Beller ([email protected])
4+
*
5+
* This software is dual-licensed under:
6+
*
7+
* - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
8+
* later version;
9+
* - the Apache Software License (ASL) version 2.0.
10+
*
11+
* The text of this file and of both licenses is available at the root of this
12+
* project or, if you have the jar distribution, in directory META-INF/, under
13+
* the names LGPL-3.0.txt and ASL-2.0.txt respectively.
14+
*
15+
* Direct link to the sources:
16+
*
17+
* - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
18+
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
19+
*/
20+
21+
package com.github.fge.jsonpatch.operation;
22+
23+
import com.fasterxml.jackson.annotation.JsonCreator;
24+
import com.fasterxml.jackson.annotation.JsonProperty;
25+
import com.fasterxml.jackson.core.JsonGenerator;
26+
import com.fasterxml.jackson.core.JsonProcessingException;
27+
import com.fasterxml.jackson.databind.JsonNode;
28+
import com.fasterxml.jackson.databind.SerializerProvider;
29+
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
30+
import com.fasterxml.jackson.databind.node.ArrayNode;
31+
import com.fasterxml.jackson.databind.node.MissingNode;
32+
import com.fasterxml.jackson.databind.node.ObjectNode;
33+
import com.github.fge.jackson.jsonpointer.JsonPointer;
34+
import com.github.fge.jsonpatch.JsonPatchException;
35+
import com.github.fge.jsonpatch.JsonPatchMessages;
36+
import com.github.fge.jsonpatch.operation.JsonPatchOperation;
37+
import com.github.fge.jsonpatch.operation.policy.PathMissingPolicy;
38+
import com.github.fge.msgsimple.bundle.MessageBundle;
39+
import com.github.fge.msgsimple.load.MessageBundles;
40+
import com.google.common.collect.Iterables;
41+
42+
import java.io.IOException;
43+
44+
/**
45+
* RemoveOperationBase implements the basic concept of removing the requested path.
46+
*/
47+
public abstract class RemoveOperationBase
48+
implements JsonPatchOperation
49+
{
50+
protected static final MessageBundle BUNDLE
51+
= MessageBundles.getBundle(JsonPatchMessages.class);
52+
53+
private PathMissingPolicy pathMissingPolicy;
54+
55+
protected final String op;
56+
57+
protected final JsonPointer path;
58+
59+
@JsonCreator
60+
public RemoveOperationBase(final String op,
61+
@JsonProperty("path") final JsonPointer path,
62+
final PathMissingPolicy pathMissingPolicy)
63+
{
64+
this.op = op;
65+
this.path = path;
66+
this.pathMissingPolicy = pathMissingPolicy;
67+
}
68+
69+
@Override
70+
public JsonNode apply(final JsonNode node)
71+
throws JsonPatchException
72+
{
73+
final JsonNode ret = node.deepCopy();
74+
if (path.isEmpty())
75+
return MissingNode.getInstance();
76+
if (path.path(node).isMissingNode()) {
77+
switch (pathMissingPolicy) {
78+
case THROW:
79+
throw new JsonPatchException(BUNDLE.getMessage(
80+
"jsonPatch.noSuchPath"));
81+
case SKIP:
82+
return ret;
83+
}
84+
}
85+
final JsonNode parentNode = path.parent().get(ret);
86+
final String raw = Iterables.getLast(path).getToken().getRaw();
87+
if (parentNode.isObject())
88+
((ObjectNode) parentNode).remove(raw);
89+
else
90+
((ArrayNode) parentNode).remove(Integer.parseInt(raw));
91+
return ret;
92+
}
93+
94+
@Override
95+
public void serialize(final JsonGenerator jgen,
96+
final SerializerProvider provider)
97+
throws IOException, JsonProcessingException
98+
{
99+
jgen.writeStartObject();
100+
jgen.writeStringField("op", op);
101+
jgen.writeStringField("path", path.toString());
102+
jgen.writeEndObject();
103+
}
104+
105+
@Override
106+
public void serializeWithType(final JsonGenerator jgen,
107+
final SerializerProvider provider, final TypeSerializer typeSer)
108+
throws IOException, JsonProcessingException
109+
{
110+
serialize(jgen, provider);
111+
}
112+
113+
@Override
114+
public String toString()
115+
{
116+
return "op: " + op + "; path: \"" + path + '"';
117+
}
118+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2016, Jessica Beller ([email protected])
3+
*
4+
* This software is dual-licensed under:
5+
*
6+
* - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
7+
* later version;
8+
* - the Apache Software License (ASL) version 2.0.
9+
*
10+
* The text of this file and of both licenses is available at the root of this
11+
* project or, if you have the jar distribution, in directory META-INF/, under
12+
* the names LGPL-3.0.txt and ASL-2.0.txt respectively.
13+
*
14+
* Direct link to the sources:
15+
*
16+
* - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
17+
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
18+
*/
19+
20+
package com.github.fge.jsonpatch.operation;
21+
22+
import com.fasterxml.jackson.annotation.JsonCreator;
23+
import com.fasterxml.jackson.annotation.JsonProperty;
24+
import com.fasterxml.jackson.databind.JsonNode;
25+
import com.github.fge.jackson.jsonpointer.JsonPointer;
26+
import com.github.fge.jsonpatch.operation.policy.PathMissingPolicy;
27+
28+
/**
29+
* JSON Path {@code remove?} operation
30+
*
31+
* <p>This operation will remove ({@code path}) if it exists.</p>
32+
*/
33+
public final class RemoveOptionalOperation extends RemoveOperationBase
34+
{
35+
public static final String OPERATION_NAME = "remove?";
36+
37+
@JsonCreator
38+
public RemoveOptionalOperation(@JsonProperty("path") final JsonPointer path)
39+
{
40+
super(OPERATION_NAME, path, PathMissingPolicy.SKIP);
41+
}
42+
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2016, Jessica Beller ([email protected])
3+
*
4+
* This software is dual-licensed under:
5+
*
6+
* - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
7+
* later version;
8+
* - the Apache Software License (ASL) version 2.0.
9+
*
10+
* The text of this file and of both licenses is available at the root of this
11+
* project or, if you have the jar distribution, in directory META-INF/, under
12+
* the names LGPL-3.0.txt and ASL-2.0.txt respectively.
13+
*
14+
* Direct link to the sources:
15+
*
16+
* - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
17+
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
18+
*/
19+
20+
package com.github.fge.jsonpatch.operation;
21+
22+
import com.fasterxml.jackson.databind.JsonNode;
23+
import com.github.fge.jackson.JacksonUtils;
24+
import com.github.fge.jackson.jsonpointer.JsonPointer;
25+
import com.github.fge.jsonpatch.JsonPatchException;
26+
import org.testng.annotations.Test;
27+
28+
import java.io.IOException;
29+
30+
import static org.testng.Assert.*;
31+
32+
public final class RemoveOptionalOperationTest
33+
extends ExtendedJsonPatchOperationTest
34+
{
35+
public RemoveOptionalOperationTest()
36+
throws IOException
37+
{
38+
super(RemoveOptionalOperation.OPERATION_NAME);
39+
}
40+
41+
@Test
42+
public void removingRootReturnsMissingNode()
43+
throws JsonPatchException
44+
{
45+
final JsonNode node = JacksonUtils.nodeFactory().nullNode();
46+
final JsonPatchOperation op = new RemoveOptionalOperation(JsonPointer.empty());
47+
final JsonNode ret = op.apply(node);
48+
assertTrue(ret.isMissingNode());
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2016, Jessica Beller ([email protected])
3+
*
4+
* This software is dual-licensed under:
5+
*
6+
* - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
7+
* later version;
8+
* - the Apache Software License (ASL) version 2.0.
9+
*
10+
* The text of this file and of both licenses is available at the root of this
11+
* project or, if you have the jar distribution, in directory META-INF/, under
12+
* the names LGPL-3.0.txt and ASL-2.0.txt respectively.
13+
*
14+
* Direct link to the sources:
15+
*
16+
* - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
17+
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
18+
*/
19+
20+
package com.github.fge.jsonpatch.serialization;
21+
22+
import com.github.fge.jsonpatch.operation.RemoveOptionalOperation;
23+
24+
import java.io.IOException;
25+
26+
public final class RemoveOptionalOperationSerializationTest
27+
extends ExtendedJsonPatchOperationSerializationTest
28+
{
29+
public RemoveOptionalOperationSerializationTest()
30+
throws IOException
31+
{
32+
super(RemoveOptionalOperation.OPERATION_NAME);
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"errors": [],
3+
"ops": [
4+
{
5+
"op": { "op": "remove?", "path": "/x/y" },
6+
"node": { "x": { "a": "b", "y": {} } },
7+
"expected": { "x": { "a": "b" } }
8+
},
9+
{
10+
"op": { "op": "remove?", "path": "/0/2" },
11+
"node": [ [ "a", "b", "c"], "d", "e" ],
12+
"expected": [ [ "a", "b" ], "d", "e" ]
13+
},
14+
{
15+
"op": { "op": "remove?", "path": "/x/0" },
16+
"node": { "x": [ "y", "z" ], "foo": "bar" },
17+
"expected": { "x": [ "z" ], "foo": "bar" }
18+
},
19+
{
20+
"op": { "op": "remove?", "path": "/doesNotExist" },
21+
"node": { "x": "y" },
22+
"expected": { "x": "y" }
23+
}
24+
]
25+
}

0 commit comments

Comments
 (0)