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 b46af94e..65b16e7a 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/JsonPath.java +++ b/json-path/src/main/java/com/jayway/jsonpath/JsonPath.java @@ -276,6 +276,17 @@ public class JsonPath { return resultByConfiguration(jsonObject, configuration, evaluationContext); } + public T renameKey(Object jsonObject, String oldKeyName, String newKeyName, Configuration configuration){ + notNull(jsonObject, "json can not be null"); + notEmpty(newKeyName, "newKeyName can not be null or empty"); + notNull(configuration, "configuration can not be null"); + EvaluationContext evaluationContext = path.evaluate(jsonObject, jsonObject, configuration, true); + for (PathRef updateOperation : evaluationContext.updateOperations()) { + updateOperation.renameKey(oldKeyName, newKeyName, configuration); + } + return resultByConfiguration(jsonObject, configuration, evaluationContext); + } + /** * Applies this JsonPath to the provided json string * diff --git a/json-path/src/main/java/com/jayway/jsonpath/WriteContext.java b/json-path/src/main/java/com/jayway/jsonpath/WriteContext.java index 0f827895..e34412c1 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/WriteContext.java +++ b/json-path/src/main/java/com/jayway/jsonpath/WriteContext.java @@ -116,6 +116,7 @@ public interface WriteContext { */ DocumentContext put(String path, String key, Object value, Predicate... filters); + /** * Add or update the key with a the given value at the given path * @@ -126,4 +127,28 @@ public interface WriteContext { */ DocumentContext put(JsonPath path, String key, Object value); -} + /** + * Renames the last key element of a given path. + * @param path The path to the old key. Should be resolved to a map + * or an array including map items. + * @param oldKeyName The old key name. + * @param newKeyName The new key name. + * @param filters filters. + * @return a document content. + */ + DocumentContext renameKey(String path, String oldKeyName, String newKeyName, Predicate... filters); + + /** + * Renames the last key element of a given path. + * @param path The path to the old key. Should be resolved to a map + * or an array including map items. + * @param oldKeyName The old key name. + * @param newKeyName The new key name. + * @return a document content. + */ + DocumentContext renameKey(JsonPath path, String oldKeyName, String newKeyName); + +// DocumentContext replace(String path, String newKey, Predicate... filters); +// DocumentContext replace(JsonPath path, String newKey); + +} \ No newline at end of file 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 7f207802..b755f5fb 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 @@ -231,6 +231,23 @@ public class JsonReader implements ParseContext, DocumentContext { return put(compile(path, filters), key, value); } + @Override + public DocumentContext renameKey(String path, String oldKeyName, String newKeyName, Predicate... filters) { + return renameKey(compile(path, filters), oldKeyName, newKeyName); + } + + @Override + public DocumentContext renameKey(JsonPath path, String oldKeyName, String newKeyName) { + List modified = path.renameKey(json, oldKeyName, newKeyName, configuration.addOptions(Option.AS_PATH_LIST)); + if(logger.isDebugEnabled()){ + for (String p : modified) { + logger.debug("Rename path {} new value {}", p, newKeyName); + } + } + return this; + } + + @Override public DocumentContext put(JsonPath path, String key, Object value){ List modified = path.put(json, key, value, configuration.addOptions(Option.AS_PATH_LIST)); diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java b/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java index e8be7057..2c277781 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java @@ -2,6 +2,7 @@ package com.jayway.jsonpath.internal; import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.InvalidModificationException; +import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.spi.json.JsonProvider; import java.util.Collection; @@ -25,6 +26,10 @@ public abstract class PathRef implements Comparable { @Override public void put(String key, Object newVal, Configuration configuration) {} + + @Override + public void renameKey(String oldKeyName, String newKeyName, Configuration configuration) {} + }; protected Object parent; @@ -44,6 +49,21 @@ public abstract class PathRef implements Comparable { public abstract void put(String key, Object newVal, Configuration configuration); + public abstract void renameKey(String oldKey,String newKeyName, Configuration configuration); + + protected void renameInMap(Object targetMap, String oldKeyName, String newKeyName, Configuration configuration){ + if(configuration.jsonProvider().isMap(targetMap)){ + configuration.jsonProvider().setProperty(targetMap, newKeyName, configuration.jsonProvider().getMapValue(targetMap, oldKeyName)); + configuration.jsonProvider().removeProperty(targetMap, oldKeyName); + } else { + throw new InvalidModificationException("Can only rename properties in a map"); + } + } + + protected boolean targetInvalid(Object target){ + return target == JsonProvider.UNDEFINED || target == null; + } + @Override public int compareTo(PathRef o) { return this.getAccessor().toString().compareTo(o.getAccessor().toString()) * -1; @@ -103,6 +123,16 @@ public abstract class PathRef implements Comparable { throw new InvalidModificationException("Invalid put operation. $ is not a map"); } } + + @Override + public void renameKey(String oldKeyName, String newKeyName, Configuration configuration) { + Object target = parent; + if(targetInvalid(target)){ + return; + } + renameInMap(target, oldKeyName, newKeyName, configuration); + } + } private static class ArrayIndexPathRef extends PathRef { @@ -123,7 +153,7 @@ public abstract class PathRef implements Comparable { public void add(Object value, Configuration configuration){ Object target = configuration.jsonProvider().getArrayIndex(parent, index); - if(target == JsonProvider.UNDEFINED || target == null){ + if(targetInvalid(target)){ return; } if(configuration.jsonProvider().isArray(target)){ @@ -135,7 +165,7 @@ public abstract class PathRef implements Comparable { public void put(String key, Object value, Configuration configuration){ Object target = configuration.jsonProvider().getArrayIndex(parent, index); - if(target == JsonProvider.UNDEFINED || target == null){ + if(targetInvalid(target)){ return; } if(configuration.jsonProvider().isMap(target)){ @@ -145,6 +175,15 @@ public abstract class PathRef implements Comparable { } } + @Override + public void renameKey(String oldKeyName, String newKeyName, Configuration configuration) { + Object target = configuration.jsonProvider().getArrayIndex(parent, index); + if(targetInvalid(target)){ + return; + } + renameInMap(target, oldKeyName, newKeyName, configuration); + } + @Override public Object getAccessor() { return index; @@ -172,7 +211,7 @@ public abstract class PathRef implements Comparable { public void add(Object value, Configuration configuration){ Object target = configuration.jsonProvider().getMapValue(parent, property); - if(target == JsonProvider.UNDEFINED || target == null){ + if(targetInvalid(target)){ return; } if(configuration.jsonProvider().isArray(target)){ @@ -184,7 +223,7 @@ public abstract class PathRef implements Comparable { public void put(String key, Object value, Configuration configuration){ Object target = configuration.jsonProvider().getMapValue(parent, property); - if(target == JsonProvider.UNDEFINED || target == null){ + if(targetInvalid(target)){ return; } if(configuration.jsonProvider().isMap(target)){ @@ -194,6 +233,15 @@ public abstract class PathRef implements Comparable { } } + @Override + public void renameKey(String oldKeyName, String newKeyName, Configuration configuration) { + Object target = configuration.jsonProvider().getMapValue(parent, property); + if(targetInvalid(target)){ + return; + } + renameInMap(target, oldKeyName, newKeyName, configuration); + } + @Override public Object getAccessor() { return property; @@ -228,7 +276,12 @@ public abstract class PathRef implements Comparable { @Override public void put(String key, Object newVal, Configuration configuration) { - throw new InvalidModificationException("Add can not be performed to multiple properties"); + throw new InvalidModificationException("Put can not be performed to multiple properties"); + } + + @Override + public void renameKey(String oldKeyName, String newKeyName, Configuration configuration) { + throw new InvalidModificationException("Rename can not be performed to multiple properties"); } @Override diff --git a/json-path/src/test/java/com/jayway/jsonpath/WriteTest.java b/json-path/src/test/java/com/jayway/jsonpath/WriteTest.java index d295a8a7..e319b023 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/WriteTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/WriteTest.java @@ -2,10 +2,7 @@ package com.jayway.jsonpath; import org.junit.Test; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.util.*; import static com.jayway.jsonpath.JsonPath.parse; import static java.util.Collections.emptyMap; @@ -214,6 +211,38 @@ public class WriteTest extends BaseTest { parse(model).set("$[?(@.a == 'a-val')]", 1); } + @Test + public void a_path_can_be_renamed(){ + Object o = parse(JSON_DOCUMENT).renameKey("$.store", "book", "updated-book").json(); + List result = parse(o).read("$.store.updated-book"); + + assertThat(result).isNotEmpty(); + } + + @Test + public void keys_in_root_containing_map_can_be_renamed(){ + Object o = parse(JSON_DOCUMENT).renameKey("$", "store", "new-store").json(); + List result = parse(o).read("$.new-store[*]"); + assertThat(result).isNotEmpty(); + } + + @Test + public void map_array_items_can_be_renamed(){ + Object o = parse(JSON_DOCUMENT).renameKey("$.store.book[*]", "category", "renamed-category").json(); + List result = parse(o).read("$.store.book[*].renamed-category"); + assertThat(result).isNotEmpty(); + } + @Test(expected = InvalidModificationException.class) + public void non_map_array_items_cannot_be_renamed(){ + List model = new LinkedList(); + model.add(1); + model.add(2); + parse(model).renameKey("$[*]", "oldKey", "newKey"); + } -} + @Test(expected = InvalidModificationException.class) + public void multiple_properties_cannot_be_renamed(){ + parse(JSON_DOCUMENT).renameKey("$.store.book[*]['author', 'category']", "old-key", "new-key"); + } +} \ No newline at end of file