From 34ef58cba91021766a0927fc16f19a394c719fe0 Mon Sep 17 00:00:00 2001 From: Kalle Stenflo Date: Mon, 15 Sep 2014 13:19:18 +0200 Subject: [PATCH 1/5] Criteria improvement. --- json-path/src/main/java/com/jayway/jsonpath/Criteria.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java index ee99028f..f998d912 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java +++ b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java @@ -281,17 +281,13 @@ public class Criteria implements Predicate { if (CriteriaType.EXISTS == criteriaType) { boolean exists = ((Boolean) expected); try { - Configuration c = ctx.configuration(); - if(c.containsOption(Option.ALWAYS_RETURN_LIST) || c.containsOption(Option.SUPPRESS_EXCEPTIONS)){ - c = c.options(); - } + Configuration c = Configuration.builder().jsonProvider(ctx.configuration().getProvider()).options().build(); path.evaluate(ctx.target(), c).getValue(); return exists; } catch (PathNotFoundException e) { return !exists; } } else { - try { final Object actual = path.evaluate(ctx.target(), ctx.configuration()).getValue(); return criteriaType.eval(expected, actual, ctx.configuration()); From d0a7d2f56489240f1b06fd1e710873965080cd29 Mon Sep 17 00:00:00 2001 From: Kalle Stenflo Date: Fri, 12 Sep 2014 14:46:35 +0200 Subject: [PATCH 2/5] GsonProvider --- json-path/pom.xml | 6 + .../java/com/jayway/jsonpath/Criteria.java | 1 + .../java/com/jayway/jsonpath/ReadContext.java | 7 + .../jayway/jsonpath/internal/JsonReader.java | 5 + .../compiler/EvaluationContextImpl.java | 6 +- .../internal/compiler/PredicatePathToken.java | 2 +- .../internal/compiler/ScanPathToken.java | 2 +- .../spi/json/AbstractJsonProvider.java | 6 +- .../internal/spi/json/GsonProvider.java | 195 ++++++++++++++++++ .../jsonpath/spi/json/JsonProvider.java | 4 +- .../com/jayway/jsonpath/ReturnTypeTest.java | 8 +- pom.xml | 7 + system.properties | 2 +- 13 files changed, 241 insertions(+), 10 deletions(-) create mode 100644 json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/GsonProvider.java diff --git a/json-path/pom.xml b/json-path/pom.xml index ede1decd..4d9d20b7 100644 --- a/json-path/pom.xml +++ b/json-path/pom.xml @@ -35,6 +35,12 @@ jackson-databind true + + com.google.code.gson + gson + true + + diff --git a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java index f998d912..93becbae 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java +++ b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java @@ -290,6 +290,7 @@ public class Criteria implements Predicate { } else { try { final Object actual = path.evaluate(ctx.target(), ctx.configuration()).getValue(); + return criteriaType.eval(expected, actual, ctx.configuration()); } catch (CompareException e) { return false; diff --git a/json-path/src/main/java/com/jayway/jsonpath/ReadContext.java b/json-path/src/main/java/com/jayway/jsonpath/ReadContext.java index 9fb8c6a3..71812a67 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/ReadContext.java +++ b/json-path/src/main/java/com/jayway/jsonpath/ReadContext.java @@ -16,6 +16,13 @@ package com.jayway.jsonpath; public interface ReadContext { + /** + * Returns the configuration used for reading + * + * @return an immutable configuration + */ + Configuration configuration(); + /** * Returns the JSON model that this context is reading * 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 526e12c6..394fcfd5 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 @@ -77,6 +77,11 @@ public class JsonReader implements ParseContext, ReadContext { return parse(is); } + @Override + public Configuration configuration() { + return configuration; + } + //------------------------------------------------ // // ReadContext impl diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/compiler/EvaluationContextImpl.java b/json-path/src/main/java/com/jayway/jsonpath/internal/compiler/EvaluationContextImpl.java index f1a36f7b..6de8b956 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/compiler/EvaluationContextImpl.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/compiler/EvaluationContextImpl.java @@ -60,9 +60,9 @@ public class EvaluationContextImpl implements EvaluationContext { if(resultIndex == 0){ throw new PathNotFoundException("No results for path: " + path.toString()); } - return (T) jsonProvider().getArrayIndex(valueResult, 0); + return (T) jsonProvider().unwrap(jsonProvider().getArrayIndex(valueResult, 0)); } - return (T) valueResult; + return (T) jsonProvider().unwrap(valueResult); } @SuppressWarnings("unchecked") @@ -78,7 +78,7 @@ public class EvaluationContextImpl implements EvaluationContext { public List getPathList() { List res = new ArrayList(); if(resultIndex > 0){ - Iterable objects = configuration.getProvider().toIterable(pathResult); + Iterable objects = configuration.getProvider().toIterable(pathResult); for (Object o : objects) { res.add((String)o); } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/compiler/PredicatePathToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/compiler/PredicatePathToken.java index 8090430c..199e095f 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/compiler/PredicatePathToken.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/compiler/PredicatePathToken.java @@ -44,7 +44,7 @@ public class PredicatePathToken extends PathToken { } } else if (ctx.jsonProvider().isArray(model)){ int idx = 0; - Iterable objects = ctx.jsonProvider().toIterable(model); + Iterable objects = ctx.jsonProvider().toIterable(model); for (Object idxModel : objects) { if (accept(idxModel, ctx.configuration())) { diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/compiler/ScanPathToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/compiler/ScanPathToken.java index a8e1323d..f7270789 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/compiler/ScanPathToken.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/compiler/ScanPathToken.java @@ -55,7 +55,7 @@ public class ScanPathToken extends PathToken { predicateMatches.put(currentPath, model); } - Iterable models = ctx.jsonProvider().toIterable(model); + Iterable models = ctx.jsonProvider().toIterable(model); int idx = 0; for (Object evalModel : models) { String evalPath = currentPath + "[" + idx + "]"; diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/AbstractJsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/AbstractJsonProvider.java index d43b98d4..5ba88a2a 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/AbstractJsonProvider.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/AbstractJsonProvider.java @@ -23,6 +23,10 @@ import java.util.Map; public abstract class AbstractJsonProvider implements JsonProvider { + public Object unwrap(Object obj){ + return obj; + } + /** * checks if object is an array * @@ -135,7 +139,7 @@ public abstract class AbstractJsonProvider implements JsonProvider { * @return the entries for an array or the values for a map */ @SuppressWarnings("unchecked") - public Iterable toIterable(Object obj) { + public Iterable toIterable(Object obj) { if (isArray(obj)) return ((Iterable) obj); else diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/GsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/GsonProvider.java new file mode 100644 index 00000000..898a99fe --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/GsonProvider.java @@ -0,0 +1,195 @@ +package com.jayway.jsonpath.internal.spi.json; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; +import com.google.gson.internal.LazilyParsedNumber; +import com.jayway.jsonpath.InvalidJsonException; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class GsonProvider extends AbstractJsonProvider { + + private static final JsonParser parser = new JsonParser(); + private static final Gson gson = new Gson(); + + + public Object unwrap(Object o){ + if(o == null){ + return null; + } + if(!(o instanceof JsonElement)){ + return o; + } + + Object unwrapped = null; + + JsonElement e = (JsonElement) o; + + if(e.isJsonNull()) { + unwrapped = null; + } else if(e.isJsonPrimitive()){ + + JsonPrimitive p = e.getAsJsonPrimitive(); + if(p.isString()){ + unwrapped = p.getAsString(); + } else if(p.isBoolean()){ + unwrapped = p.getAsBoolean(); + } else if(p.isNumber()){ + Number n = p.getAsNumber(); + if(n instanceof LazilyParsedNumber){ + LazilyParsedNumber lpn = (LazilyParsedNumber) n; + BigDecimal bigDecimal = new BigDecimal(lpn.toString()); + if(bigDecimal.scale() <= 0){ + if(bigDecimal.compareTo(new BigDecimal(Integer.MAX_VALUE)) <= 0){ + unwrapped = bigDecimal.intValue(); + } else { + unwrapped = bigDecimal.longValue(); + } + } else { + if(bigDecimal.compareTo(new BigDecimal(Float.MAX_VALUE)) <= 0){ + unwrapped = bigDecimal.floatValue(); + } else { + unwrapped = bigDecimal.doubleValue(); + } + } + } else { + unwrapped = n; + } + } + } else { + unwrapped = o; + } + return unwrapped; + } + + @Override + public Object parse(String json) throws InvalidJsonException { + return parser.parse(json); + } + + @Override + public Object parse(Reader jsonReader) throws InvalidJsonException { + return parser.parse(jsonReader); + } + + @Override + public Object parse(InputStream jsonStream) throws InvalidJsonException { + return parser.parse(new InputStreamReader(jsonStream)); + } + + @Override + public String toJson(Object obj) { + return ((JsonElement)obj).toString(); + } + + @Override + public Object createMap() { + return new JsonObject(); + } + + @Override + public Object createArray() { + return new JsonArray(); + } + + @Override + public boolean isArray(Object obj) { + return (obj instanceof JsonArray); + } + + @Override + public Object getArrayIndex(Object obj, int idx) { + return toJsonArray(obj).get(idx); + } + + @Override + public Object getMapValue(Object obj, String key) { + Object o = toJsonObject(obj).get(key); + if(o == null){ + return UNDEFINED; + } else { + return o; + } + } + + @Override + public void setProperty(Object obj, Object key, Object value) { + if (isMap(obj)) + toJsonObject(obj).add(key.toString(), toJsonElement(value)); + else { + JsonArray array = toJsonArray(obj); + int index; + if (key != null) { + index = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString()); + } else { + index = array.size(); + } + if(index == array.size()){ + array.add(toJsonElement(value)); + } else { + array.set(index, toJsonElement(value)); + } + + } + } + + @Override + public boolean isMap(Object obj) { + return (obj instanceof JsonObject); + } + + @Override + public Collection getPropertyKeys(Object obj) { + List keys = new ArrayList(); + for (Map.Entry entry : toJsonObject(obj).entrySet()) { + keys.add(entry.getKey()); + } + return keys; + } + + @Override + public int length(Object obj) { + if (isArray(obj)) { + return toJsonArray(obj).size(); + } else { + return toJsonObject(obj).entrySet().size(); + } + } + + @Override + public Iterable toIterable(Object obj) { + if (isArray(obj)) { + return toJsonArray(obj); + } else { + List values = new ArrayList(); + JsonObject jsonObject = toJsonObject(obj); + for (Map.Entry entry : jsonObject.entrySet()) { + values.add(entry.getValue()); + } + return values; + } + } + + private JsonElement toJsonElement(Object o){ + return gson.toJsonTree(o); + } + + private JsonArray toJsonArray(Object o){ + return (JsonArray) o; + } + + private JsonObject toJsonObject(Object o){ + return (JsonObject) o; + } +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonProvider.java index 67ca5ed6..534a9967 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonProvider.java +++ b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonProvider.java @@ -24,6 +24,8 @@ public interface JsonProvider { static final Object UNDEFINED = new Object(); + public Object unwrap(Object obj); + Object parse(String json) throws InvalidJsonException; Object parse(Reader jsonReader) throws InvalidJsonException; @@ -58,7 +60,7 @@ public interface JsonProvider { * @param obj an array or an object * @return the entries for an array or the values for a map */ - Iterable toIterable(Object obj); + Iterable toIterable(Object obj); /** diff --git a/json-path/src/test/java/com/jayway/jsonpath/ReturnTypeTest.java b/json-path/src/test/java/com/jayway/jsonpath/ReturnTypeTest.java index 8a6bc601..d749bade 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/ReturnTypeTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/ReturnTypeTest.java @@ -2,7 +2,6 @@ package com.jayway.jsonpath; import org.junit.Test; -import java.util.List; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; @@ -39,7 +38,12 @@ public class ReturnTypeTest extends BaseTest { @Test public void assert_arrays_can_be_read() { - assertThat(reader.read("$.store.book", List.class)).hasSize(4); + + Object result = reader.read("$.store.book"); + + assertThat(reader.configuration().getProvider().isArray(result)).isTrue(); + + assertThat(reader.configuration().getProvider().length(result)).isEqualTo(4); } @Test diff --git a/pom.xml b/pom.xml index d65ae4cf..347882de 100644 --- a/pom.xml +++ b/pom.xml @@ -182,6 +182,13 @@ jackson-databind ${jackson.version} + + + com.google.code.gson + gson + 2.3 + + io.fastjson boon diff --git a/system.properties b/system.properties index 4d46ac0e..3e06d15e 100644 --- a/system.properties +++ b/system.properties @@ -1 +1 @@ -java.runtime.version=1.7 \ No newline at end of file +java.runtime.version=1.6 \ No newline at end of file From 0c68aa8615554b86ea8d1a9c642e3a268acf3b5b Mon Sep 17 00:00:00 2001 From: Kalle Stenflo Date: Fri, 12 Sep 2014 17:16:20 +0200 Subject: [PATCH 3/5] GsonProvider --- .../com/jayway/jsonpath/internal/spi/json/GsonProvider.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/GsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/GsonProvider.java index 898a99fe..3cd2b801 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/GsonProvider.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/GsonProvider.java @@ -23,7 +23,6 @@ public class GsonProvider extends AbstractJsonProvider { private static final JsonParser parser = new JsonParser(); private static final Gson gson = new Gson(); - public Object unwrap(Object o){ if(o == null){ return null; @@ -90,7 +89,7 @@ public class GsonProvider extends AbstractJsonProvider { @Override public String toJson(Object obj) { - return ((JsonElement)obj).toString(); + return obj.toString(); } @Override @@ -113,6 +112,7 @@ public class GsonProvider extends AbstractJsonProvider { return toJsonArray(obj).get(idx); } + @Override public Object getMapValue(Object obj, String key) { Object o = toJsonObject(obj).get(key); @@ -140,7 +140,6 @@ public class GsonProvider extends AbstractJsonProvider { } else { array.set(index, toJsonElement(value)); } - } } From 15f1330987f1a6a0472e22c4c26603f8c89ea2d8 Mon Sep 17 00:00:00 2001 From: Kalle Stenflo Date: Tue, 16 Sep 2014 13:12:08 +0200 Subject: [PATCH 4/5] Reworked conversion handling. --- .../com/jayway/jsonpath/web/bench/Bench.java | 2 +- .../com/jayway/jsonpath/Configuration.java | 65 ++++-- .../java/com/jayway/jsonpath/Criteria.java | 71 ++----- .../java/com/jayway/jsonpath/JsonPath.java | 22 +- .../jsonpath/ValueCompareException.java | 4 + .../jayway/jsonpath/internal/JsonReader.java | 13 +- .../compiler/EvaluationContextImpl.java | 12 +- .../internal/spi/converter/ConverterBase.java | 40 ++++ .../internal/spi/converter/DateConverter.java | 38 ++++ .../converter/DefaultConversionProvider.java | 52 +++++ .../spi/converter/NumberConverter.java | 130 ++++++++++++ .../spi/converter/StringConverter.java | 26 +++ .../spi/json/AbstractJsonProvider.java | 38 ++++ .../internal/spi/json/GsonProvider.java | 200 +++++++++++++++--- .../spi/converter/ConversionException.java | 21 ++ .../spi/converter/ConversionProvider.java | 10 + .../jsonpath/spi/converter/Converter.java | 69 +++++- .../spi/converter/ConverterFactory.java | 174 --------------- .../jsonpath/spi/json/JsonProvider.java | 5 +- .../java/com/jayway/jsonpath/BaseTest.java | 30 +++ .../com/jayway/jsonpath/ConverterTest.java | 12 +- .../java/com/jayway/jsonpath/FilterTest.java | 23 +- .../java/com/jayway/jsonpath/OptionsTest.java | 2 +- .../com/jayway/jsonpath/ReturnTypeTest.java | 10 +- .../com/jayway/jsonpath/old/FilterTest.java | 2 +- .../old/internal/ScanPathTokenTest.java | 6 - 26 files changed, 750 insertions(+), 327 deletions(-) create mode 100644 json-path/src/main/java/com/jayway/jsonpath/ValueCompareException.java create mode 100644 json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/ConverterBase.java create mode 100644 json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/DateConverter.java create mode 100644 json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/DefaultConversionProvider.java create mode 100644 json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/NumberConverter.java create mode 100644 json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/StringConverter.java create mode 100644 json-path/src/main/java/com/jayway/jsonpath/spi/converter/ConversionException.java create mode 100644 json-path/src/main/java/com/jayway/jsonpath/spi/converter/ConversionProvider.java delete mode 100644 json-path/src/main/java/com/jayway/jsonpath/spi/converter/ConverterFactory.java diff --git a/json-path-web-test/src/main/java/com/jayway/jsonpath/web/bench/Bench.java b/json-path-web-test/src/main/java/com/jayway/jsonpath/web/bench/Bench.java index 991b7924..72084a40 100644 --- a/json-path-web-test/src/main/java/com/jayway/jsonpath/web/bench/Bench.java +++ b/json-path-web-test/src/main/java/com/jayway/jsonpath/web/bench/Bench.java @@ -70,7 +70,7 @@ public class Bench { } else if (res instanceof Boolean) { result = res.toString(); } else { - result = res != null ? Configuration.defaultConfiguration().getProvider().toJson(res) : "null"; + result = res != null ? Configuration.defaultConfiguration().jsonProvider().toJson(res) : "null"; } return new Result("jayway", time, result, error); } diff --git a/json-path/src/main/java/com/jayway/jsonpath/Configuration.java b/json-path/src/main/java/com/jayway/jsonpath/Configuration.java index 5419423d..b8ea15db 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/Configuration.java +++ b/json-path/src/main/java/com/jayway/jsonpath/Configuration.java @@ -14,7 +14,9 @@ */ package com.jayway.jsonpath; +import com.jayway.jsonpath.internal.spi.converter.DefaultConversionProvider; import com.jayway.jsonpath.internal.spi.json.JsonSmartJsonProvider; +import com.jayway.jsonpath.spi.converter.ConversionProvider; import com.jayway.jsonpath.spi.json.JsonProvider; import java.util.Collections; @@ -28,7 +30,7 @@ public class Configuration { private static Defaults DEFAULTS = new Defaults() { @Override - public JsonProvider provider() { + public JsonProvider jsonProvider() { return new JsonSmartJsonProvider(); } @@ -36,38 +38,54 @@ public class Configuration { public Set