Kalle Stenflo
10 years ago
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