28 changed files with 892 additions and 82 deletions
@ -0,0 +1,18 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2011 the original author or authors. |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.jayway.jsonpath; |
||||||
|
|
||||||
|
public interface DocumentContext extends ReadContext, WriteContext { |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2011 the original author or authors. |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.jayway.jsonpath; |
||||||
|
|
||||||
|
public class InvalidModificationException extends JsonPathException { |
||||||
|
public InvalidModificationException(String message) { |
||||||
|
super(message); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2011 the original author or authors. |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.jayway.jsonpath; |
||||||
|
|
||||||
|
public interface WriteContext { |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the configuration used for reading |
||||||
|
* |
||||||
|
* @return an immutable configuration |
||||||
|
*/ |
||||||
|
Configuration configuration(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the JSON model that this context is operating on |
||||||
|
* |
||||||
|
* @return json model |
||||||
|
*/ |
||||||
|
<T> T json(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the value a the given path |
||||||
|
* |
||||||
|
* @param path path to set |
||||||
|
* @param newValue new value |
||||||
|
* @param filters filters |
||||||
|
* @return a document context |
||||||
|
*/ |
||||||
|
DocumentContext set(String path, Object newValue, Predicate... filters); |
||||||
|
|
||||||
|
/** |
||||||
|
* Deletes the given path |
||||||
|
* |
||||||
|
* @param path path to delete |
||||||
|
* @param filters filters |
||||||
|
* @return a document context |
||||||
|
*/ |
||||||
|
DocumentContext delete(String path, Predicate... filters); |
||||||
|
|
||||||
|
/** |
||||||
|
* Add value to array at the given path |
||||||
|
* |
||||||
|
* @param path path to array |
||||||
|
* @param value value to add |
||||||
|
* @param filters filters |
||||||
|
* @return a document context |
||||||
|
*/ |
||||||
|
DocumentContext add(String path, Object value, Predicate... filters); |
||||||
|
|
||||||
|
/** |
||||||
|
* Add or update the key with a the given value at the given path |
||||||
|
* |
||||||
|
* @param path path to array |
||||||
|
* @param key key to add |
||||||
|
* @param value value of key |
||||||
|
* @param filters filters |
||||||
|
* @return a document context |
||||||
|
*/ |
||||||
|
DocumentContext put(String path, String key, Object value, Predicate... filters); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,239 @@ |
|||||||
|
package com.jayway.jsonpath.internal; |
||||||
|
|
||||||
|
import com.jayway.jsonpath.Configuration; |
||||||
|
import com.jayway.jsonpath.InvalidModificationException; |
||||||
|
import com.jayway.jsonpath.spi.json.JsonProvider; |
||||||
|
|
||||||
|
import java.util.Collection; |
||||||
|
|
||||||
|
public abstract class PathRef implements Comparable<PathRef> { |
||||||
|
|
||||||
|
public static final PathRef NO_OP = new PathRef(null){ |
||||||
|
@Override |
||||||
|
public Object getAccessor() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void set(Object newVal, Configuration configuration) {} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void delete(Configuration configuration) {} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void add(Object newVal, Configuration configuration) {} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void put(String key, Object newVal, Configuration configuration) {} |
||||||
|
}; |
||||||
|
|
||||||
|
protected Object parent; |
||||||
|
|
||||||
|
|
||||||
|
private PathRef(Object parent) { |
||||||
|
this.parent = parent; |
||||||
|
} |
||||||
|
|
||||||
|
abstract Object getAccessor(); |
||||||
|
|
||||||
|
public abstract void set(Object newVal, Configuration configuration); |
||||||
|
|
||||||
|
public abstract void delete(Configuration configuration); |
||||||
|
|
||||||
|
public abstract void add(Object newVal, Configuration configuration); |
||||||
|
|
||||||
|
public abstract void put(String key, Object newVal, Configuration configuration); |
||||||
|
|
||||||
|
@Override |
||||||
|
public int compareTo(PathRef o) { |
||||||
|
return this.getAccessor().toString().compareTo(o.getAccessor().toString()) * -1; |
||||||
|
} |
||||||
|
|
||||||
|
public static PathRef create(Object obj, String property){ |
||||||
|
return new ObjectPropertyPathRef(obj, property); |
||||||
|
} |
||||||
|
|
||||||
|
public static PathRef create(Object obj, Collection<String> properties){ |
||||||
|
return new ObjectMultiPropertyPathRef(obj, properties); |
||||||
|
} |
||||||
|
|
||||||
|
public static PathRef create(Object array, int index){ |
||||||
|
return new ArrayIndexPathRef(array, index); |
||||||
|
} |
||||||
|
|
||||||
|
public static PathRef createRoot(Object root){ |
||||||
|
return new RootPathRef(root); |
||||||
|
} |
||||||
|
|
||||||
|
private static class RootPathRef extends PathRef { |
||||||
|
|
||||||
|
private RootPathRef(Object parent) { |
||||||
|
super(parent); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
Object getAccessor() { |
||||||
|
return "$"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void set(Object newVal, Configuration configuration) { |
||||||
|
throw new InvalidModificationException("Invalid delete operation"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void delete(Configuration configuration) { |
||||||
|
throw new InvalidModificationException("Invalid delete operation"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void add(Object newVal, Configuration configuration) { |
||||||
|
if(configuration.jsonProvider().isArray(parent)){ |
||||||
|
configuration.jsonProvider().setProperty(parent, null, newVal); |
||||||
|
} else { |
||||||
|
throw new InvalidModificationException("Invalid add operation. $ is not an array"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void put(String key, Object newVal, Configuration configuration) { |
||||||
|
if(configuration.jsonProvider().isMap(parent)){ |
||||||
|
configuration.jsonProvider().setProperty(parent, key, newVal); |
||||||
|
} else { |
||||||
|
throw new InvalidModificationException("Invalid add operation. $ is not an array"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
private static class ArrayIndexPathRef extends PathRef { |
||||||
|
|
||||||
|
private int index; |
||||||
|
|
||||||
|
private ArrayIndexPathRef(Object parent, int index) { |
||||||
|
super(parent); |
||||||
|
this.index = index; |
||||||
|
} |
||||||
|
|
||||||
|
public void set(Object newVal, Configuration configuration){ |
||||||
|
configuration.jsonProvider().setArrayIndex(parent, index, newVal); |
||||||
|
} |
||||||
|
|
||||||
|
public void delete(Configuration configuration){ |
||||||
|
configuration.jsonProvider().removeProperty(parent, index); |
||||||
|
} |
||||||
|
|
||||||
|
public void add(Object value, Configuration configuration){ |
||||||
|
Object target = configuration.jsonProvider().getArrayIndex(parent, index); |
||||||
|
if(target == JsonProvider.UNDEFINED || target == null){ |
||||||
|
return; |
||||||
|
} |
||||||
|
if(configuration.jsonProvider().isArray(target)){ |
||||||
|
configuration.jsonProvider().setProperty(target, null, value); |
||||||
|
} else { |
||||||
|
throw new InvalidModificationException("Can only add to an array"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void put(String key, Object value, Configuration configuration){ |
||||||
|
Object target = configuration.jsonProvider().getArrayIndex(parent, index); |
||||||
|
if(target == JsonProvider.UNDEFINED || target == null){ |
||||||
|
return; |
||||||
|
} |
||||||
|
if(configuration.jsonProvider().isMap(target)){ |
||||||
|
configuration.jsonProvider().setProperty(target, key, value); |
||||||
|
} else { |
||||||
|
throw new InvalidModificationException("Can only add properties to a map"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getAccessor() { |
||||||
|
return index; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static class ObjectPropertyPathRef extends PathRef { |
||||||
|
|
||||||
|
private String property; |
||||||
|
|
||||||
|
private ObjectPropertyPathRef(Object parent, String property) { |
||||||
|
super(parent); |
||||||
|
this.property = property; |
||||||
|
} |
||||||
|
|
||||||
|
public void set(Object newVal, Configuration configuration){ |
||||||
|
configuration.jsonProvider().setProperty(parent, property, newVal); |
||||||
|
} |
||||||
|
|
||||||
|
public void delete(Configuration configuration){ |
||||||
|
configuration.jsonProvider().removeProperty(parent, property); |
||||||
|
} |
||||||
|
|
||||||
|
public void add(Object value, Configuration configuration){ |
||||||
|
Object target = configuration.jsonProvider().getMapValue(parent, property); |
||||||
|
if(target == JsonProvider.UNDEFINED || target == null){ |
||||||
|
return; |
||||||
|
} |
||||||
|
if(configuration.jsonProvider().isArray(target)){ |
||||||
|
configuration.jsonProvider().setProperty(target, null, value); |
||||||
|
} else { |
||||||
|
throw new InvalidModificationException("Can only add to an array"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void put(String key, Object value, Configuration configuration){ |
||||||
|
Object target = configuration.jsonProvider().getMapValue(parent, property); |
||||||
|
if(target == JsonProvider.UNDEFINED || target == null){ |
||||||
|
return; |
||||||
|
} |
||||||
|
if(configuration.jsonProvider().isMap(target)){ |
||||||
|
configuration.jsonProvider().setProperty(target, key, value); |
||||||
|
} else { |
||||||
|
throw new InvalidModificationException("Can only add properties to a map"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getAccessor() { |
||||||
|
return property; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class ObjectMultiPropertyPathRef extends PathRef { |
||||||
|
|
||||||
|
private Collection<String> properties; |
||||||
|
|
||||||
|
private ObjectMultiPropertyPathRef(Object parent, Collection<String> properties) { |
||||||
|
super(parent); |
||||||
|
this.properties = properties; |
||||||
|
} |
||||||
|
|
||||||
|
public void set(Object newVal, Configuration configuration){ |
||||||
|
for (String property : properties) { |
||||||
|
configuration.jsonProvider().setProperty(parent, property, newVal); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void delete(Configuration configuration){ |
||||||
|
for (String property : properties) { |
||||||
|
configuration.jsonProvider().removeProperty(parent, property); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void add(Object newVal, Configuration configuration) { |
||||||
|
throw new InvalidModificationException("Add can not be performed to multiple properties"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void put(String key, Object newVal, Configuration configuration) { |
||||||
|
throw new InvalidModificationException("Add can not be performed to multiple properties"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getAccessor() { |
||||||
|
return Utils.join("&&", properties); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,219 @@ |
|||||||
|
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 static com.jayway.jsonpath.JsonPath.parse; |
||||||
|
import static java.util.Collections.emptyMap; |
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
public class WriteTest extends BaseTest { |
||||||
|
|
||||||
|
private static final Map<String, Object> EMPTY_MAP = emptyMap(); |
||||||
|
|
||||||
|
@Test |
||||||
|
public void an_array_child_property_can_be_updated() { |
||||||
|
|
||||||
|
Object o = parse(JSON_DOCUMENT).set("$.store.book[*].display-price", 1).json(); |
||||||
|
|
||||||
|
List<Integer> result = parse(o).read("$.store.book[*].display-price"); |
||||||
|
|
||||||
|
assertThat(result).containsExactly(1, 1, 1, 1); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
public void an_root_property_can_be_updated() { |
||||||
|
|
||||||
|
Object o = parse(JSON_DOCUMENT).set("$.int-max-property", 1).json(); |
||||||
|
|
||||||
|
Integer result = parse(o).read("$.int-max-property"); |
||||||
|
|
||||||
|
assertThat(result).isEqualTo(1); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void an_deep_scan_can_update() { |
||||||
|
|
||||||
|
Object o = parse(JSON_DOCUMENT).set("$..display-price", 1).json(); |
||||||
|
|
||||||
|
List<Integer> result = parse(o).read("$..display-price"); |
||||||
|
|
||||||
|
assertThat(result).containsExactly(1, 1, 1, 1, 1); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
public void an_filter_can_update() { |
||||||
|
|
||||||
|
Object o = parse(JSON_DOCUMENT).set("$.store.book[?(@.display-price)].display-price", 1).json(); |
||||||
|
|
||||||
|
List<Integer> result = parse(o).read("$.store.book[?(@.display-price)].display-price"); |
||||||
|
|
||||||
|
assertThat(result).containsExactly(1, 1, 1, 1); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void a_path_can_be_deleted() { |
||||||
|
|
||||||
|
Object o = parse(JSON_DOCUMENT).delete("$.store.book[*].display-price").json(); |
||||||
|
|
||||||
|
List<Integer> result = parse(o).read("$.store.book[*].display-price"); |
||||||
|
|
||||||
|
assertThat(result).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void operations_can_chained() { |
||||||
|
|
||||||
|
Object o = parse(JSON_DOCUMENT) |
||||||
|
.delete("$.store.book[*].display-price") |
||||||
|
.set("$.store.book[*].category", "A") |
||||||
|
.json(); |
||||||
|
|
||||||
|
List<Integer> prices = parse(o).read("$.store.book[*].display-price"); |
||||||
|
List<String> categories = parse(o).read("$.store.book[*].category"); |
||||||
|
|
||||||
|
assertThat(prices).isEmpty(); |
||||||
|
assertThat(categories).containsExactly("A", "A", "A", "A"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void an_array_can_be_updated() { |
||||||
|
|
||||||
|
List<Integer> ints = parse("[0,1,2,3]").set("$[?(@ == 1)]", 9).json(); |
||||||
|
|
||||||
|
assertThat(ints).containsExactly(0, 9, 2, 3); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void an_array_index_can_be_updated() { |
||||||
|
|
||||||
|
String res = parse(JSON_DOCUMENT).set("$.store.book[0]", "a").read("$.store.book[0]"); |
||||||
|
|
||||||
|
assertThat(res).isEqualTo("a"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void an_array_slice_can_be_updated() { |
||||||
|
|
||||||
|
List<String> res = parse(JSON_DOCUMENT).set("$.store.book[0:2]", "a").read("$.store.book[0:2]"); |
||||||
|
|
||||||
|
assertThat(res).containsExactly("a", "a"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void an_array_criteria_can_be_updated() { |
||||||
|
|
||||||
|
List<String> res = parse(JSON_DOCUMENT) |
||||||
|
.set("$.store.book[?(@.category == 'fiction')]", "a") |
||||||
|
.read("$.store.book[?(@ == 'a')]"); |
||||||
|
|
||||||
|
assertThat(res).containsExactly("a", "a", "a"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void an_array_criteria_can_be_deleted() { |
||||||
|
|
||||||
|
List<String> res = parse(JSON_DOCUMENT) |
||||||
|
.delete("$.store.book[?(@.category == 'fiction')]") |
||||||
|
.read("$.store.book[*].category"); |
||||||
|
|
||||||
|
assertThat(res).containsExactly("reference"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
public void multi_prop_delete() { |
||||||
|
|
||||||
|
List<Map<String, Object>> res = parse(JSON_DOCUMENT).delete("$.store.book[*]['author', 'category']").read("$.store.book[*]['author', 'category']"); |
||||||
|
|
||||||
|
assertThat(res).containsExactly(EMPTY_MAP, EMPTY_MAP, EMPTY_MAP, EMPTY_MAP); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void multi_prop_update() { |
||||||
|
|
||||||
|
Map<String, Object> expected = new HashMap<String, Object>(){{ |
||||||
|
put("author", "a"); |
||||||
|
put("category", "a"); |
||||||
|
}}; |
||||||
|
|
||||||
|
List<Map<String, Object>> res = parse(JSON_DOCUMENT).set("$.store.book[*]['author', 'category']", "a").read("$.store.book[*]['author', 'category']"); |
||||||
|
|
||||||
|
assertThat(res).containsExactly(expected, expected, expected, expected); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
public void multi_prop_update_not_all_defined() { |
||||||
|
|
||||||
|
Map<String, Object> expected = new HashMap<String, Object>(){{ |
||||||
|
put("author", "a"); |
||||||
|
put("isbn", "a"); |
||||||
|
}}; |
||||||
|
|
||||||
|
List<Map<String, Object>> res = parse(JSON_DOCUMENT).set("$.store.book[*]['author', 'isbn']", "a").read("$.store.book[*]['author', 'isbn']"); |
||||||
|
|
||||||
|
assertThat(res).containsExactly(expected, expected, expected, expected); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void add_to_array() { |
||||||
|
Object res = parse(JSON_DOCUMENT).add("$.store.book", 1).read("$.store.book[4]"); |
||||||
|
assertThat(res).isEqualTo(1); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void add_to_object() { |
||||||
|
Object res = parse(JSON_DOCUMENT).put("$.store.book[0]", "new-key", "new-value").read("$.store.book[0].new-key"); |
||||||
|
assertThat(res).isEqualTo("new-value"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void item_can_be_added_to_root_array() { |
||||||
|
List<Integer> model = new LinkedList<Integer>(); |
||||||
|
model.add(1); |
||||||
|
model.add(2); |
||||||
|
|
||||||
|
List<Integer> ints = parse(model).add("$", 3).read("$"); |
||||||
|
|
||||||
|
assertThat(ints).containsExactly(1,2,3); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void key_val_can_be_added_to_root_object() { |
||||||
|
Map model = new HashMap(); |
||||||
|
model.put("a", "a-val"); |
||||||
|
|
||||||
|
String newVal = parse(model).put("$", "new-key", "new-val").read("$.new-key"); |
||||||
|
|
||||||
|
assertThat(newVal).isEqualTo("new-val"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = InvalidModificationException.class) |
||||||
|
public void add_to_object_on_array() { |
||||||
|
parse(JSON_DOCUMENT).put("$.store.book", "new-key", "new-value"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = InvalidModificationException.class) |
||||||
|
public void add_to_array_on_object() { |
||||||
|
parse(JSON_DOCUMENT).add("$.store.book[0]", "new-value"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Test(expected = InvalidModificationException.class) |
||||||
|
public void root_object_can_not_be_updated() { |
||||||
|
Map model = new HashMap(); |
||||||
|
model.put("a", "a-val"); |
||||||
|
|
||||||
|
parse(model).set("$[?(@.a == 'a-val')]", 1); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue