From d3d0e85924b3a5cf953b869391f281b7e837b10c Mon Sep 17 00:00:00 2001 From: sbeimin Date: Thu, 23 Oct 2014 13:18:45 +0200 Subject: [PATCH] Remove a given path from json --- .../java/com/jayway/jsonpath/JsonPath.java | 128 +++++++++++++++++- .../java/com/jayway/jsonpath/ReadContext.java | 10 ++ .../jayway/jsonpath/internal/JsonReader.java | 28 ++-- .../java/com/jayway/jsonpath/RemoveTest.java | 59 ++++++++ 4 files changed, 209 insertions(+), 16 deletions(-) create mode 100644 json-path/src/test/java/com/jayway/jsonpath/RemoveTest.java diff --git a/json-path/src/main/java/com/jayway/jsonpath/JsonPath.java b/json-path/src/main/java/com/jayway/jsonpath/JsonPath.java index ef3186e5..373c713a 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/JsonPath.java +++ b/json-path/src/main/java/com/jayway/jsonpath/JsonPath.java @@ -15,20 +15,28 @@ package com.jayway.jsonpath; -import com.jayway.jsonpath.internal.JsonReader; -import com.jayway.jsonpath.internal.Path; -import com.jayway.jsonpath.internal.PathCompiler; -import com.jayway.jsonpath.internal.Utils; -import com.jayway.jsonpath.spi.http.HttpProviderFactory; -import com.jayway.jsonpath.spi.json.JsonProvider; +import static com.jayway.jsonpath.internal.Utils.isTrue; +import static com.jayway.jsonpath.internal.Utils.notEmpty; +import static com.jayway.jsonpath.internal.Utils.notNull; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; -import static com.jayway.jsonpath.internal.Utils.*; +import com.jayway.jsonpath.internal.EvaluationContext; +import com.jayway.jsonpath.internal.JsonReader; +import com.jayway.jsonpath.internal.Path; +import com.jayway.jsonpath.internal.PathCompiler; +import com.jayway.jsonpath.internal.Utils; +import com.jayway.jsonpath.spi.http.HttpProviderFactory; +import com.jayway.jsonpath.spi.json.JsonProvider; /** *

@@ -452,7 +460,58 @@ public class JsonPath { return new JsonReader().parse(jsonInputStream).read(jsonPath, filters); } + //------------------------- + + + /** + * Creates a new JsonPath and applies it to the provided Json object + * + * @param json a json object + * @param jsonPath the json path + * @param filters filters to be applied to the filter place holders [?] in the path + * @return json object without elemens matched by given path + */ + @SuppressWarnings("rawtypes") + public static Map remove(Object json, String jsonPath, Predicate... filters) { + return parse(json).remove(jsonPath, Map.class, filters); + } + + /** + * Creates a new JsonPath and applies it to the provided Json object + * + * @param json a json string + * @param jsonPath the json path + * @param filters filters to be applied to the filter place holders [?] in the path + * @return list of objects matched by the given path + */ + public static String remove(String json, String jsonPath, Predicate... filters) { + return parse(json).remove(jsonPath, String.class, filters); + } + + /** + * Creates a new JsonPath and applies it to the provided Json object + * + * @param json url pointing to json doc + * @param jsonPath the json path + * @param filters filters to be applied to the filter place holders [?] in the path + * @return list of objects matched by the given path + */ + public static String remove(URL json, String jsonPath, Predicate... filters) throws IOException { + return parse(json).remove(jsonPath, String.class, filters); + } + /** + * Creates a new JsonPath and applies it to the provided Json object + * + * @param json json file + * @param jsonPath the json path + * @param filters filters to be applied to the filter place holders [?] in the path + * @return list of objects matched by the given path + */ + public static String remove(File json, String jsonPath, Predicate... filters) throws IOException { + return parse(json).remove(jsonPath, String.class, filters); + } + // -------------------------------------------------------- // // Static Fluent API @@ -589,4 +648,59 @@ public class JsonPath { public static ReadContext parse(URL json, Configuration configuration) throws IOException { return new JsonReader(configuration).parse(json); } + + private List splitPath(String pathSteps) { + List result = new ArrayList(); + for (String string : pathSteps.split("\\]")) + result.add(string.replaceAll("[\\[\\$\\']+", "")); + return result; + } + + /** + * Applies this JsonPath to the provided json document. + * Note that the document must be identified as either a List or Map by + * the {@link JsonProvider} + * + * @param jsonObject a container Object + * @param configuration configuration to use + * @param Class expected return type + * @return json object without elemens matched by the path + */ + @SuppressWarnings("unchecked") + public T removeTyped(Object jsonObject, Configuration configuration, Class returnType) { + boolean optSuppressExceptions = configuration.containsOption(Option.SUPPRESS_EXCEPTIONS); + Object result = jsonObject; + try { + EvaluationContext evalContext = path.evaluate(jsonObject, jsonObject, configuration); + for (String pathSteps : evalContext.getPathList()) + result = remove(result, splitPath(pathSteps)); + } catch (RuntimeException e){ + if(!optSuppressExceptions){ + throw e; + } + } + if(Map.class.isAssignableFrom(returnType)) + return (T) result; + if(String.class.isAssignableFrom(returnType)) + return (T) configuration.jsonProvider().toJson(result); + throw new RuntimeException("Unknown returnType: "+returnType.getSimpleName()); + } + + @SuppressWarnings("unchecked") + private T remove(T jsonObject, List pathList) { + if(jsonObject instanceof Map) { + Map map = (Map) jsonObject; + Set keys = new HashSet(map.keySet()); + for (String key : keys) { + if(key.equals(pathList.get(0))) { + if(pathList.size() > 1) + remove(map.get(key), pathList.subList(1, pathList.size())); + else + map.remove(key); + } + } + return (T) map; + } + return jsonObject; + } } diff --git a/json-path/src/main/java/com/jayway/jsonpath/ReadContext.java b/json-path/src/main/java/com/jayway/jsonpath/ReadContext.java index 5f78a21f..3cfb5184 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/ReadContext.java +++ b/json-path/src/main/java/com/jayway/jsonpath/ReadContext.java @@ -14,6 +14,7 @@ */ package com.jayway.jsonpath; + public interface ReadContext { /** @@ -84,4 +85,13 @@ public interface ReadContext { */ ReadContext withListeners(EvaluationListener... listener); + /** + * Removes the given path from this context + * + * @param path path to read + * @param filters filters + * @param + * @return result + */ + T remove(String jsonPath, Class returnType, Predicate... filters); } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/JsonReader.java b/json-path/src/main/java/com/jayway/jsonpath/internal/JsonReader.java index df307fe3..4fa260d9 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/JsonReader.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/JsonReader.java @@ -14,13 +14,8 @@ */ package com.jayway.jsonpath.internal; -import com.jayway.jsonpath.Configuration; -import com.jayway.jsonpath.EvaluationListener; -import com.jayway.jsonpath.JsonPath; -import com.jayway.jsonpath.ParseContext; -import com.jayway.jsonpath.Predicate; -import com.jayway.jsonpath.ReadContext; -import com.jayway.jsonpath.spi.http.HttpProviderFactory; +import static com.jayway.jsonpath.internal.Utils.notEmpty; +import static com.jayway.jsonpath.internal.Utils.notNull; import java.io.File; import java.io.FileInputStream; @@ -28,8 +23,13 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; -import static com.jayway.jsonpath.internal.Utils.notEmpty; -import static com.jayway.jsonpath.internal.Utils.notNull; +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.EvaluationListener; +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.ParseContext; +import com.jayway.jsonpath.Predicate; +import com.jayway.jsonpath.ReadContext; +import com.jayway.jsonpath.spi.http.HttpProviderFactory; public class JsonReader implements ParseContext, ReadContext { @@ -158,6 +158,16 @@ public class JsonReader implements ParseContext, ReadContext { return configuration.mappingProvider().map(obj, targetType, configuration); } + @Override + public T remove(String path, Class returnType, Predicate... filters) { + notEmpty(path, "path can not be null or empty"); + return remove(JsonPath.compile(path, filters), returnType); + } + + public T remove(JsonPath path, Class returnType) { + notNull(path, "path can not be null"); + return path.removeTyped(json, configuration, returnType); + } private final class LimitingEvaluationListener implements EvaluationListener { diff --git a/json-path/src/test/java/com/jayway/jsonpath/RemoveTest.java b/json-path/src/test/java/com/jayway/jsonpath/RemoveTest.java new file mode 100644 index 00000000..cddd22f0 --- /dev/null +++ b/json-path/src/test/java/com/jayway/jsonpath/RemoveTest.java @@ -0,0 +1,59 @@ +package com.jayway.jsonpath; + +import static com.jayway.jsonpath.JsonPath.using; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +public class RemoveTest extends BaseTest { + + @Test + public void fromJsonString() { + String result = JsonPath.remove("{\"val\":1,\"val2\":2}", "$.val"); + assertThat(result).isEqualTo("{\"val2\":2}"); + } + + @SuppressWarnings("unchecked") + @Test + public void fromMap() { + Map model = new HashMap(){{ + put("a", "a-val"); + put("b", "b-val"); + put("c", "c-val"); + }}; + + Configuration conf = Configuration.defaultConfiguration(); + + assertThat(using(conf).parse(model).remove("$.b", Map.class)) + .containsEntry("a", "a-val") + .containsEntry("c", "c-val").doesNotContainKey("b"); + } + + @Test + public void fromMapToString() { + Map model = new HashMap(){{ + put("a", "a-val"); + put("b", "b-val"); + put("c", "c-val"); + }}; + + Configuration conf = Configuration.defaultConfiguration(); + + assertThat(using(conf).parse(model).remove("$.b", String.class)) + .contains("\"a\":\"a-val\"") + .contains("\"c\":\"c-val\"") + .doesNotContain("\"b\":\"b-val\""); + } + + @SuppressWarnings("unchecked") + @Test + public void fromStringToMap() { + Configuration conf = Configuration.defaultConfiguration(); + + assertThat(using(conf).parse("{\"val\":1,\"val2\":2}").remove("$.val2", Map.class)) + .containsEntry("val", 1).doesNotContainKey("val2"); + } +} \ No newline at end of file