diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayIndexFilter.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayIndexFilter.java index aa4ab526..45681d7a 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayIndexFilter.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ArrayIndexFilter.java @@ -14,6 +14,7 @@ */ package com.jayway.jsonpath.internal.filter; +import com.jayway.jsonpath.InvalidPathException; import com.jayway.jsonpath.spi.JsonProvider; import java.util.List; @@ -27,44 +28,73 @@ public class ArrayIndexFilter extends PathTokenFilter { private static final Pattern SINGLE_ARRAY_INDEX_PATTERN = Pattern.compile("\\[\\d+\\]"); private static final Pattern COMMA = Pattern.compile(","); private static final Pattern SPACE = Pattern.compile(" "); + private static final String OPERATOR = ":"; - private final String trimmedCondition; - + private boolean usesLenght; + public ArrayIndexFilter(String condition) { super(condition); + + // remove '[' and ']' String trimmedCondition = trim(condition, 1, 1); - if(trimmedCondition.contains("@.length")){ + this.usesLenght = trimmedCondition.contains("@.length"); + + // resolve '@.length' + if(usesLenght){ trimmedCondition = trim(trimmedCondition, 1, 1); trimmedCondition = trimmedCondition.replace("@.length", ""); - trimmedCondition = trimmedCondition + ":"; + trimmedCondition = trimmedCondition + OPERATOR; } this.trimmedCondition = trimmedCondition; } + @Override public Object filter(Object obj,JsonProvider jsonProvider) { List src = jsonProvider.toList(obj); List result = jsonProvider.createList(); - + if (trimmedCondition.contains(OPERATOR)) { + if (trimmedCondition.startsWith(OPERATOR)) { + String trimmedCondition = trim(this.trimmedCondition, 1, 0); + int get = Integer.parseInt(trimmedCondition); + for (int i = 0; i < get; i++) { + result.add(src.get(i)); + } + return result; + } else if (trimmedCondition.endsWith(OPERATOR)) { + String trimmedCondition = trim(SPACE.matcher(this.trimmedCondition).replaceAll(""), 0, 1); + int get = Integer.parseInt(trimmedCondition); + if(get > 0 || usesLenght){ + if(get > 0){ + get = get * -1; + } + return src.get(src.size() + get); + } else { + int start = src.size() + get; + int stop = src.size(); + + for (int i = start; i < stop; i ++){ + result.add(src.get(i)); + } + return result; + } - if (trimmedCondition.startsWith(":")) { - String trimmedCondition = trim(this.trimmedCondition, 1, 0); - int get = Integer.parseInt(trimmedCondition); - for (int i = 0; i < get; i++) { - result.add(src.get(i)); - } - return result; + } else { + String[] indexes = this.trimmedCondition.split(OPERATOR); - } else if (trimmedCondition.endsWith(":")) { - String trimmedCondition = trim(SPACE.matcher(this.trimmedCondition).replaceAll(""), 1, 1); - int get = Integer.parseInt(trimmedCondition); - return src.get(src.size() - get); + int start = Integer.parseInt(indexes[0]); + int stop = Integer.parseInt(indexes[1]); + for (int i = start; i < stop; i ++){ + result.add(src.get(i)); + } + return result; + } } else { String[] indexArr = COMMA.split(trimmedCondition); diff --git a/json-path/src/test/java/com/jayway/jsonpath/ArraySlicingTest.java b/json-path/src/test/java/com/jayway/jsonpath/ArraySlicingTest.java new file mode 100644 index 00000000..f6a1b0cf --- /dev/null +++ b/json-path/src/test/java/com/jayway/jsonpath/ArraySlicingTest.java @@ -0,0 +1,92 @@ +package com.jayway.jsonpath; + +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +/** + * User: kalle + * Date: 8/20/13 + * Time: 9:22 AM + * + * If you have a list + * nums = [1, 3, 5, 7, 8, 13, 20] + * then it is possible to slice by using a notation similar to element retrieval: + * + * nums[3] #equals 7, no slicing + * nums[:3] #equals [1, 3, 5], from index 0 (inclusive) until index 3 (exclusive) + * nums[1:5] #equals [3, 5, 7, 8] + * nums[-3:] #equals [8, 13, 20] + * nums[3:] #equals [8, 13, 20] + * + * Note that Python allows negative list indices. The index -1 represents the last element, -2 the penultimate element, etc. + * Python also allows a step property by appending an extra colon and a value. For example: + * + * nums[3::] #equals [7, 8, 13, 20], same as nums[3:] + * nums[::3] #equals [1, 7, 20] (starting at index 0 and getting every third element) + * nums[1:5:2] #equals [3, 7] (from index 1 until index 5 and getting every second element) + + * + */ +public class ArraySlicingTest { + + public static final String JSON_ARRAY = "[1, 3, 5, 7, 8, 13, 20]"; + + @Test + public void get_by_position(){ + int result = JsonPath.read(JSON_ARRAY, "$[3]"); + assertEquals(7, result); + } + + @Test + public void get_from_index(){ + List result = JsonPath.read(JSON_ARRAY, "$[:3]"); + assertThat(result, Matchers.contains(1,3,5)); + } + + @Test + public void get_between_index(){ + List result = JsonPath.read(JSON_ARRAY, "$[1:5]"); + assertThat(result, Matchers.contains(3, 5, 7, 8)); + } + + @Test + public void get_between_index_2(){ + List result = JsonPath.read(JSON_ARRAY, "$[0:1]"); + assertThat(result, Matchers.contains(1)); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void get_between_index_out_of_bounds(){ + List result = JsonPath.read(JSON_ARRAY, "$[1:15]"); + } + + @Test + public void get_from_tail_index(){ + List result = JsonPath.read(JSON_ARRAY, "$[-3:]"); + assertThat(result, Matchers.contains(8, 13, 20)); + } + + @Test + public void get_from_tail(){ + int result = JsonPath.read(JSON_ARRAY, "$[3:]"); + assertEquals(8, result); + } + + @Test + public void get_from_tail_length(){ + int result = JsonPath.read(JSON_ARRAY, "$[(@.length -3)]"); + assertEquals(8, result); + } + + @Test + public void get_indexes(){ + List result = JsonPath.read(JSON_ARRAY, "$[0,1,2]"); + assertThat(result, Matchers.contains(1,3,5)); + } + +} diff --git a/json-path/src/test/java/com/jayway/jsonpath/ComplianceTest.java b/json-path/src/test/java/com/jayway/jsonpath/ComplianceTest.java index 96ab8378..0472721e 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/ComplianceTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/ComplianceTest.java @@ -8,6 +8,7 @@ import java.util.Map; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; /** * test defined in http://jsonpath.googlecode.com/svn/trunk/tests/jsonpath-test-js.html @@ -44,7 +45,10 @@ public class ComplianceTest { new Double(3.14), new Boolean(true), (Comparable)null)); - assertThat(JsonPath.read(json, "$[-1:]"), is(equalTo(null))); + + List res = JsonPath.read(json, "$[-1:]"); + + assertTrue(res.get(0) == null); } @Test diff --git a/json-path/src/test/java/com/jayway/jsonpath/JsonPathTest.java b/json-path/src/test/java/com/jayway/jsonpath/JsonPathTest.java index 9f1fbb3c..d91c02df 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/JsonPathTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/JsonPathTest.java @@ -190,7 +190,7 @@ public class JsonPathTest { public void access_array_by_index_from_tail() throws Exception { assertThat(JsonPath.read(DOCUMENT, "$..book[(@.length-1)].author"), equalTo("J. R. R. Tolkien")); - assertThat(JsonPath.read(DOCUMENT, "$..book[-1:].author"), equalTo("J. R. R. Tolkien")); + assertThat(JsonPath.read(DOCUMENT, "$..book[1:].author"), equalTo("J. R. R. Tolkien")); } @Test