diff --git a/README.md b/README.md index 9a21e885..13b228a9 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ The function output is dictated by the function itself. | `first()` | Provides the first item of an array | Depends on the array | | `last()` | Provides the last item of an array | Depends on the array | | `index(X)` | Provides the item of an array of index: X, if the X is negative, take from backwards | Depends on the array | - +| `distinct()`| Provides an array containing only unique items from the input array | List | Filter Operators ----------------- diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/function/PathFunctionFactory.java b/json-path/src/main/java/com/jayway/jsonpath/internal/function/PathFunctionFactory.java index 3a31151f..0731d022 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/function/PathFunctionFactory.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/function/PathFunctionFactory.java @@ -8,6 +8,7 @@ import com.jayway.jsonpath.internal.function.numeric.Min; import com.jayway.jsonpath.internal.function.numeric.StandardDeviation; import com.jayway.jsonpath.internal.function.numeric.Sum; +import com.jayway.jsonpath.internal.function.sequence.Distinct; import com.jayway.jsonpath.internal.function.sequence.First; import com.jayway.jsonpath.internal.function.sequence.Index; import com.jayway.jsonpath.internal.function.sequence.Last; @@ -53,6 +54,7 @@ public class PathFunctionFactory { // Sequential Functions map.put("first", First.class); map.put("last", Last.class); + map.put("distinct", Distinct.class); map.put("index", Index.class); diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/function/sequence/Distinct.java b/json-path/src/main/java/com/jayway/jsonpath/internal/function/sequence/Distinct.java new file mode 100644 index 00000000..3f681789 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/function/sequence/Distinct.java @@ -0,0 +1,28 @@ +package com.jayway.jsonpath.internal.function.sequence; + +import com.jayway.jsonpath.JsonPathException; +import com.jayway.jsonpath.internal.EvaluationContext; +import com.jayway.jsonpath.internal.PathRef; +import com.jayway.jsonpath.internal.function.Parameter; +import com.jayway.jsonpath.internal.function.PathFunction; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +/** + * Take the unique items from collection of JSONArray + */ +public class Distinct implements PathFunction { + + @Override + public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List parameters) { + if (ctx.configuration().jsonProvider().isArray(model)) { + Iterable objects = ctx.configuration().jsonProvider().toIterable(model); + return StreamSupport.stream(objects.spliterator(), false) + .distinct() + .collect(Collectors.toList()); + } + throw new JsonPathException("Aggregation function attempted unique values using non array"); + } +} diff --git a/json-path/src/test/java/com/jayway/jsonpath/internal/function/BaseFunctionTest.java b/json-path/src/test/java/com/jayway/jsonpath/internal/function/BaseFunctionTest.java index b26a2b8f..1154a26d 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/internal/function/BaseFunctionTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/internal/function/BaseFunctionTest.java @@ -13,8 +13,19 @@ */ public class BaseFunctionTest { protected static final String NUMBER_SERIES = "{\"empty\": [], \"numbers\" : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}"; - protected static final String TEXT_SERIES = "{\"urls\": [\"http://api.worldbank.org/countries/all/?format=json\", \"http://api.worldbank.org/countries/all/?format=json\"], \"text\" : [ \"a\", \"b\", \"c\", \"d\", \"e\", \"f\" ]}"; + protected static final String TEXT_SERIES = "{\"urls\": [\"http://api.worldbank.org/countries/all/?format=json\", \"http://api.worldbank.org/countries/all/?format=json\"], \"text\" : [ \"a\", \"b\", \"c\", \"d\", \"e\", \"f\" ], \"text_with_duplicates\" : [ \"a\", \"b\", \"b\" ]}"; protected static final String TEXT_AND_NUMBER_SERIES = "{\"text\" : [ \"a\", \"b\", \"c\", \"d\", \"e\", \"f\" ], \"numbers\" : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}"; + protected static final String OBJECT_SERIES = "{\"empty\":[]," + + "\"objects\":[{\"a\": \"a-val\"}, {\"b\":\"b-val\"},{ \"a\":\"a-val\"}]," + + "\"array_of_objects\":[" + + "{\"a\":[{\"a\":\"a-val\"},{\"b\":\"b-val\"}]}," + + "{\"b\":[{\"b\":\"b-val\"}]}," + + "{\"a\":[{\"a\":\"a-val\"},{\"b\":\"b-val\"}]}]," + + "\"array_of_arrays\":[" + + "[{\"a\":\"a-val\"},{\"b\":\"b-val\"}]," + + "[{\"b\":\"b-val\"}]," + + "[{\"a\":\"a-val\"},{\"b\":\"b-val\"}]]" + + "}"; /** * Verify the function returns the correct result based on the input expectedValue diff --git a/json-path/src/test/java/com/jayway/jsonpath/internal/function/SequentialPathFunctionTest.java b/json-path/src/test/java/com/jayway/jsonpath/internal/function/SequentialPathFunctionTest.java index 70b2b953..97c1eaeb 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/internal/function/SequentialPathFunctionTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/internal/function/SequentialPathFunctionTest.java @@ -1,7 +1,14 @@ package com.jayway.jsonpath.internal.function; +import static com.jayway.jsonpath.JsonPath.using; + +import com.fasterxml.jackson.databind.ObjectMapper; import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.Configurations; +import java.util.Arrays; + +import com.jayway.jsonpath.spi.json.JacksonJsonProvider; +import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; import org.junit.Test; /** @@ -10,6 +17,7 @@ * -first * -last * -index(X) + * -distinct * * Created by git9527 on 6/11/22. */ @@ -50,4 +58,36 @@ public void testIndexOfText() throws Exception { verifyFunction(conf, "$.text.index(-1)", TEXT_SERIES, "f"); verifyFunction(conf, "$.text.index(1)", TEXT_SERIES, "b"); } + + @Test + public void testDistinctOfText() { + verifyFunction(conf, "$.text_with_duplicates.distinct()", TEXT_SERIES, Arrays.asList("a", "b")); + } + + @Test + public void testDistinctOfObjects() { + final Object expectedValue = Configuration.defaultConfiguration().jsonProvider() + .parse("[{\"a\":\"a-val\"}, {\"b\":\"b-val\"}]"); + verifyFunction(conf, "$.objects.distinct()", OBJECT_SERIES, expectedValue); + } + + @Test + public void testDistinctArrayOfObjects() { + final Object expectedValue = Configuration.defaultConfiguration().jsonProvider() + .parse("[{\"a\":[{\"a\":\"a-val\"}, {\"b\":\"b-val\"}]}, {\"b\":[{\"b\":\"b-val\"}]}]"); + verifyFunction(conf, "$.array_of_objects.distinct()", OBJECT_SERIES, expectedValue); + } + + @Test + public void testDistinctArrayOfArrays() { + final Object expectedValue = Configuration.defaultConfiguration().jsonProvider() + .parse("[[{\"a\":\"a-val\"}, {\"b\":\"b-val\"}], [{\"b\":\"b-val\"}]]"); + verifyFunction(conf, "$.array_of_arrays.distinct()", OBJECT_SERIES, expectedValue); + } + + @Test + public void testDistinctOfEmptyObjects() throws Exception { + final Object expectedValue = Configuration.defaultConfiguration().jsonProvider().parse("[]"); + verifyFunction(conf, "$.empty.distinct()", OBJECT_SERIES, expectedValue); + } }