Browse Source

Merge branch 'master' of github.com:jayway/JsonPath into optimizations

Conflicts:
	json-path/src/main/java/com/jayway/jsonpath/Criteria.java
pull/51/head
Jochen Berger 10 years ago
parent
commit
2b116b262b
  1. 2
      json-path-web-test/src/main/java/com/jayway/jsonpath/web/bench/Bench.java
  2. 6
      json-path/pom.xml
  3. 65
      json-path/src/main/java/com/jayway/jsonpath/Configuration.java
  4. 84
      json-path/src/main/java/com/jayway/jsonpath/Criteria.java
  5. 22
      json-path/src/main/java/com/jayway/jsonpath/JsonPath.java
  6. 7
      json-path/src/main/java/com/jayway/jsonpath/ReadContext.java
  7. 4
      json-path/src/main/java/com/jayway/jsonpath/ValueCompareException.java
  8. 18
      json-path/src/main/java/com/jayway/jsonpath/internal/JsonReader.java
  9. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/PathCompiler.java
  10. 14
      json-path/src/main/java/com/jayway/jsonpath/internal/compiler/EvaluationContextImpl.java
  11. 4
      json-path/src/main/java/com/jayway/jsonpath/internal/compiler/PathToken.java
  12. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/compiler/PredicatePathToken.java
  13. 13
      json-path/src/main/java/com/jayway/jsonpath/internal/compiler/ScanPathToken.java
  14. 40
      json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/ConverterBase.java
  15. 38
      json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/DateConverter.java
  16. 66
      json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/DefaultConversionProvider.java
  17. 90
      json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/GsonConverter.java
  18. 130
      json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/NumberConverter.java
  19. 25
      json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/StringConverter.java
  20. 75
      json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/AbstractJsonProvider.java
  21. 358
      json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/GsonProvider.java
  22. 21
      json-path/src/main/java/com/jayway/jsonpath/spi/converter/ConversionException.java
  23. 10
      json-path/src/main/java/com/jayway/jsonpath/spi/converter/ConversionProvider.java
  24. 69
      json-path/src/main/java/com/jayway/jsonpath/spi/converter/Converter.java
  25. 174
      json-path/src/main/java/com/jayway/jsonpath/spi/converter/ConverterFactory.java
  26. 28
      json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonProvider.java
  27. 31
      json-path/src/test/java/com/jayway/jsonpath/BaseTest.java
  28. 19
      json-path/src/test/java/com/jayway/jsonpath/ConverterTest.java
  29. 4
      json-path/src/test/java/com/jayway/jsonpath/FilterTest.java
  30. 12
      json-path/src/test/java/com/jayway/jsonpath/OptionsTest.java
  31. 10
      json-path/src/test/java/com/jayway/jsonpath/ReturnTypeTest.java
  32. 2
      json-path/src/test/java/com/jayway/jsonpath/old/FilterTest.java
  33. 5
      json-path/src/test/java/com/jayway/jsonpath/old/JsonPathTest.java
  34. 3
      json-path/src/test/java/com/jayway/jsonpath/old/internal/ScanPathTokenTest.java
  35. 7
      pom.xml
  36. 2
      system.properties

2
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);
}

6
json-path/pom.xml

@ -35,6 +35,12 @@
<artifactId>jackson-databind</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>

65
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<Option> options() {
return EnumSet.noneOf(Option.class);
}
@Override
public ConversionProvider conversionProvider() {
return new DefaultConversionProvider();
}
};
public static synchronized void setDefaults(Defaults defaults){
DEFAULTS = defaults;
}
private final JsonProvider provider;
private final JsonProvider jsonProvider;
private final ConversionProvider conversionProvider;
private final Set<Option> options;
private Configuration(JsonProvider provider, EnumSet<Option> options) {
notNull(provider, "provider can not be null");
private Configuration(JsonProvider jsonProvider, ConversionProvider conversionProvider, EnumSet<Option> options) {
notNull(jsonProvider, "jsonProvider can not be null");
notNull(conversionProvider, "conversionProvider can not be null");
notNull(options, "options can not be null");
this.provider = provider;
this.jsonProvider = jsonProvider;
this.conversionProvider = conversionProvider;
this.options = Collections.unmodifiableSet(options);
}
public Configuration provider(JsonProvider provider) {
return Configuration.builder().jsonProvider(provider).options(options).build();
public Configuration jsonProvider(JsonProvider newJsonProvider) {
return Configuration.builder().jsonProvider(newJsonProvider).conversionProvider(conversionProvider).options(options).build();
}
public JsonProvider jsonProvider() {
return jsonProvider;
}
public ConversionProvider conversionProvider() {
return conversionProvider;
}
public JsonProvider getProvider() {
return provider;
public Configuration conversionProvider(ConversionProvider newConversionProvider) {
return Configuration.builder().jsonProvider(jsonProvider).conversionProvider(newConversionProvider).options(options).build();
}
public Configuration addOptions(Option... options) {
EnumSet<Option> opts = EnumSet.noneOf(Option.class);
opts.addAll(this.options);
opts.addAll(asList(options));
return Configuration.builder().jsonProvider(provider).options(opts).build();
return Configuration.builder().jsonProvider(jsonProvider).conversionProvider(conversionProvider).options(opts).build();
}
public Configuration options(Option... options) {
return Configuration.builder().jsonProvider(provider).options(options).build();
return Configuration.builder().jsonProvider(jsonProvider).conversionProvider(conversionProvider).options(options).build();
}
public Set<Option> getOptions() {
@ -80,7 +98,7 @@ public class Configuration {
public static Configuration defaultConfiguration() {
return Configuration.builder().jsonProvider(DEFAULTS.provider()).options(DEFAULTS.options()).build();
return Configuration.builder().jsonProvider(DEFAULTS.jsonProvider()).options(DEFAULTS.options()).build();
}
public static ConfigurationBuilder builder() {
@ -89,11 +107,17 @@ public class Configuration {
public static class ConfigurationBuilder {
private JsonProvider provider;
private JsonProvider jsonProvider;
private ConversionProvider conversionProvider;
private EnumSet<Option> options = EnumSet.noneOf(Option.class);
public ConfigurationBuilder jsonProvider(JsonProvider provider) {
this.provider = provider;
this.jsonProvider = provider;
return this;
}
public ConfigurationBuilder conversionProvider(ConversionProvider provider) {
this.conversionProvider = provider;
return this;
}
@ -110,18 +134,23 @@ public class Configuration {
}
public Configuration build() {
if (provider == null) {
provider = DEFAULTS.provider();
if (jsonProvider == null) {
jsonProvider = DEFAULTS.jsonProvider();
}
if(conversionProvider == null){
conversionProvider = DEFAULTS.conversionProvider();
}
return new Configuration(provider, options);
return new Configuration(jsonProvider, conversionProvider, options);
}
}
public interface Defaults {
JsonProvider provider();
JsonProvider jsonProvider();
Set<Option> options();
ConversionProvider conversionProvider();
}
}

84
json-path/src/main/java/com/jayway/jsonpath/Criteria.java

@ -5,7 +5,6 @@ import com.jayway.jsonpath.internal.PathCompiler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
@ -34,7 +33,7 @@ public class Criteria implements Predicate {
EQ {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = (0 == safeCompare(expected, actual, configuration));
boolean res = (0 == configuration.jsonProvider().compare(expected, actual));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
@ -42,7 +41,7 @@ public class Criteria implements Predicate {
NE {
@Override
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = (0 != safeCompare(expected, actual, configuration));
boolean res = (0 != configuration.jsonProvider().compare(expected, actual));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
@ -53,7 +52,7 @@ public class Criteria implements Predicate {
if ((expected == null) ^ (actual == null)) {
return false;
}
boolean res = (0 > safeCompare(expected, actual, configuration));
boolean res = (0 > configuration.jsonProvider().compare(expected, actual));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
@ -64,7 +63,7 @@ public class Criteria implements Predicate {
if ((expected == null) ^ (actual == null)) {
return false;
}
boolean res = (0 >= safeCompare(expected, actual, configuration));
boolean res = (0 >= configuration.jsonProvider().compare(expected, actual));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
@ -75,7 +74,7 @@ public class Criteria implements Predicate {
if ((expected == null) ^ (actual == null)) {
return false;
}
boolean res = (0 < safeCompare(expected, actual, configuration));
boolean res = (0 < configuration.jsonProvider().compare(expected, actual));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
@ -86,7 +85,7 @@ public class Criteria implements Predicate {
if ((expected == null) ^ (actual == null)) {
return false;
}
boolean res = (0 <= safeCompare(expected, actual, configuration));
boolean res = (0 <= configuration.jsonProvider().compare(expected, actual));
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res);
return res;
}
@ -97,7 +96,7 @@ public class Criteria implements Predicate {
boolean res = false;
Collection exps = (Collection) expected;
for (Object exp : exps) {
if (0 == safeCompare(exp, actual, configuration)) {
if (0 == configuration.jsonProvider().compare(exp, actual)) {
res = true;
break;
}
@ -120,11 +119,11 @@ public class Criteria implements Predicate {
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = true;
Collection exps = (Collection) expected;
if (configuration.getProvider().isArray(actual)) {
if (configuration.jsonProvider().isArray(actual)) {
for (Object exp : exps) {
boolean found = false;
for (Object check : configuration.getProvider().toIterable(actual)) {
if (0 == safeCompare(exp, check, configuration)) {
for (Object check : configuration.jsonProvider().toIterable(actual)) {
if (0 == configuration.jsonProvider().compare(exp, check)) {
found = true;
break;
}
@ -134,7 +133,7 @@ public class Criteria implements Predicate {
break;
}
}
logger.debug("[{}] {} [{}] => {}", join(", ", configuration.getProvider().toIterable(actual)), name(), join(", ", exps), res);
logger.debug("[{}] {} [{}] => {}", join(", ", configuration.jsonProvider().toIterable(actual)), name(), join(", ", exps), res);
} else {
res = false;
logger.debug("[{}] {} [{}] => {}", "<NOT AN ARRAY>", name(), join(", ", exps), res);
@ -147,12 +146,12 @@ public class Criteria implements Predicate {
boolean eval(Object expected, Object actual, Configuration configuration) {
int size = (Integer) expected;
boolean res;
if (configuration.getProvider().isArray(actual)) {
int length = configuration.getProvider().length(actual);
res = length == size;
if (configuration.jsonProvider().isArray(actual)) {
int length = configuration.jsonProvider().length(actual);
res = (length == size);
logger.debug("Array with size {} {} {} => {}", length, name(), size, res);
} else if (actual instanceof String) {
int length = ((String) actual).length();
} else if (configuration.jsonProvider().isString(actual)) {
int length = configuration.jsonProvider().length(actual);
res = length == size;
logger.debug("String with length {} {} {} => {}", length, name(), size, res);
} else {
@ -212,8 +211,8 @@ public class Criteria implements Predicate {
boolean eval(Object expected, Object actual, Configuration configuration) {
boolean res = false;
if (actual != null) {
if (configuration.getProvider().isArray(actual)) {
int len = configuration.getProvider().length(actual);
if (configuration.jsonProvider().isArray(actual)) {
int len = configuration.jsonProvider().length(actual);
res = (0 != len);
logger.debug("array length = {} {} => {}", len, name(), res);
} else if (actual instanceof String) {
@ -281,21 +280,18 @@ 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().jsonProvider()).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());
} catch (CompareException e) {
} catch (ValueCompareException e) {
return false;
} catch (PathNotFoundException e) {
return false;
@ -571,44 +567,6 @@ public class Criteria implements Predicate {
return this;
}
private static int safeCompare(Object expected, Object actual, Configuration configuration) {
boolean expectedIsNullish = isNullish(expected);
boolean actualIsNullish = isNullish(actual);
if (expectedIsNullish && !actualIsNullish) {
return -1;
} else if (!expectedIsNullish && actualIsNullish) {
return 1;
} else if (expectedIsNullish && actualIsNullish) {
return 0;
} else if (expected instanceof String && actual instanceof String) {
return ((String) expected).compareTo((String) actual);
} else if (expected instanceof Number && actual instanceof Number) {
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(actual.toString()));
} else if (expected instanceof String && actual instanceof Number) {
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(actual.toString()));
} else if (expected instanceof String && actual instanceof Boolean) {
Boolean e = Boolean.valueOf((String)expected);
Boolean a = (Boolean) actual;
return e.compareTo(a);
} else if (expected instanceof Boolean && actual instanceof Boolean) {
Boolean e = (Boolean) expected;
Boolean a = (Boolean) actual;
return e.compareTo(a);
} else {
logger.debug("Can not compare a {} with a {}", expected.getClass().getName(), actual.getClass().getName());
throw new CompareException();
}
}
private static boolean isNullish(Object o){
return (o == null || ((o instanceof String) && ("null".equals(o))));
}
private static class CompareException extends RuntimeException {
}
public static Criteria create(String path, String operator, String expected) {
if (! expected.isEmpty() && expected.charAt(0) == '\'' && expected.charAt(expected.length()-1) == '\'') {
expected = expected.substring(1, expected.length() - 1);

22
json-path/src/main/java/com/jayway/jsonpath/JsonPath.java

@ -176,8 +176,8 @@ public class JsonPath {
} else {
Object res = path.evaluate(jsonObject, configuration).getValue();
if(optAlwaysReturnList && path.isDefinite()){
Object array = configuration.getProvider().createArray();
configuration.getProvider().setProperty(array, 0, res);
Object array = configuration.jsonProvider().createArray();
configuration.jsonProvider().setProperty(array, 0, res);
return (T)array;
} else {
return (T)res;
@ -189,12 +189,12 @@ public class JsonPath {
}
}
if(optAsPathList){
return (T)configuration.getProvider().createArray();
return (T)configuration.jsonProvider().createArray();
} else {
if(optAlwaysReturnList){
return (T)configuration.getProvider().createArray();
return (T)configuration.jsonProvider().createArray();
} else {
return (T)(path.isDefinite() ? null : configuration.getProvider().createArray());
return (T)(path.isDefinite() ? null : configuration.jsonProvider().createArray());
}
}
@ -226,7 +226,7 @@ public class JsonPath {
notEmpty(json, "json can not be null or empty");
notNull(configuration, "jsonProvider can not be null");
return read(configuration.getProvider().parse(json), configuration);
return read(configuration.jsonProvider().parse(json), configuration);
}
/**
@ -259,7 +259,7 @@ public class JsonPath {
InputStream in = null;
try {
in = HttpProviderFactory.getProvider().get(jsonURL);
return read(configuration.getProvider().parse(in), configuration);
return read(configuration.jsonProvider().parse(in), configuration);
} finally {
Utils.closeQuietly(in);
}
@ -297,7 +297,7 @@ public class JsonPath {
FileInputStream fis = null;
try {
fis = new FileInputStream(jsonFile);
return read(configuration.getProvider().parse(fis), configuration);
return read(configuration.jsonProvider().parse(fis), configuration);
} finally {
Utils.closeQuietly(fis);
}
@ -331,7 +331,7 @@ public class JsonPath {
notNull(configuration, "configuration can not be null");
try {
return read(configuration.getProvider().parse(jsonInputStream), configuration);
return read(configuration.jsonProvider().parse(jsonInputStream), configuration);
} finally {
Utils.closeQuietly(jsonInputStream);
}
@ -455,8 +455,8 @@ public class JsonPath {
/**
* Creates a {@link ParseContext} that will parse a given JSON input.
*
* @param provider provider to use when parsing JSON
* @return a parsing context based on given provider
* @param provider jsonProvider to use when parsing JSON
* @return a parsing context based on given jsonProvider
*/
public static ParseContext using(JsonProvider provider) {
return new JsonReader(Configuration.builder().jsonProvider(provider).build());

7
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
*

4
json-path/src/main/java/com/jayway/jsonpath/ValueCompareException.java

@ -0,0 +1,4 @@
package com.jayway.jsonpath;
public class ValueCompareException extends RuntimeException {
}

18
json-path/src/main/java/com/jayway/jsonpath/internal/JsonReader.java

@ -5,7 +5,6 @@ import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.ParseContext;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.ReadContext;
import com.jayway.jsonpath.spi.converter.ConverterFactory;
import com.jayway.jsonpath.spi.http.HttpProviderFactory;
import java.io.File;
@ -46,14 +45,14 @@ public class JsonReader implements ParseContext, ReadContext {
@Override
public ReadContext parse(String json) {
notEmpty(json, "json string can not be null or empty");
this.json = configuration.getProvider().parse(json);
this.json = configuration.jsonProvider().parse(json);
return this;
}
@Override
public ReadContext parse(InputStream json) {
notNull(json, "json input stream can not be null");
this.json = configuration.getProvider().parse(json);
this.json = configuration.jsonProvider().parse(json);
return this;
}
@ -77,6 +76,11 @@ public class JsonReader implements ParseContext, ReadContext {
return parse(is);
}
@Override
public Configuration configuration() {
return configuration;
}
//------------------------------------------------
//
// ReadContext impl
@ -95,7 +99,7 @@ public class JsonReader implements ParseContext, ReadContext {
@Override
public <T> T read(String path, Class<T> type, Predicate... filters) {
return convert(read(path, filters), type);
return convert(read(path, filters), type, configuration);
}
@Override
@ -106,11 +110,11 @@ public class JsonReader implements ParseContext, ReadContext {
@Override
public <T> T read(JsonPath path, Class<T> type) {
return convert(read(path), type);
return convert(read(path), type, configuration);
}
private <T> T convert(Object obj, Class<T> type){
return ConverterFactory.createConverter(type).convert(obj, configuration);
private <T> T convert(Object obj, Class<T> targetType, Configuration configuration){
return configuration.conversionProvider().convert(obj, targetType, configuration);
}
}

2
json-path/src/main/java/com/jayway/jsonpath/internal/PathCompiler.java

@ -5,8 +5,8 @@ import com.jayway.jsonpath.Filter;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.compiler.ArrayPathToken;
import com.jayway.jsonpath.internal.compiler.PredicatePathToken;
import com.jayway.jsonpath.internal.compiler.PathToken;
import com.jayway.jsonpath.internal.compiler.PredicatePathToken;
import com.jayway.jsonpath.internal.compiler.PropertyPathToken;
import com.jayway.jsonpath.internal.compiler.RootPathToken;
import com.jayway.jsonpath.internal.compiler.ScanPathToken;

14
json-path/src/main/java/com/jayway/jsonpath/internal/compiler/EvaluationContextImpl.java

@ -29,18 +29,18 @@ public class EvaluationContextImpl implements EvaluationContext {
notNull(configuration, "configuration can not be null");
this.path = path;
this.configuration = configuration;
this.valueResult = configuration.getProvider().createArray();
this.pathResult = configuration.getProvider().createArray();
this.valueResult = configuration.jsonProvider().createArray();
this.pathResult = configuration.jsonProvider().createArray();
}
public void addResult(String path, Object model) {
configuration.getProvider().setProperty(valueResult, resultIndex, model);
configuration.getProvider().setProperty(pathResult, resultIndex, path);
configuration.jsonProvider().setProperty(valueResult, resultIndex, model);
configuration.jsonProvider().setProperty(pathResult, resultIndex, path);
resultIndex++;
}
public JsonProvider jsonProvider() {
return configuration.getProvider();
return configuration.jsonProvider();
}
public Set<Option> options() {
@ -62,7 +62,7 @@ public class EvaluationContextImpl implements EvaluationContext {
}
return (T) jsonProvider().getArrayIndex(valueResult, 0);
}
return (T) valueResult;
return (T)valueResult;
}
@SuppressWarnings("unchecked")
@ -78,7 +78,7 @@ public class EvaluationContextImpl implements EvaluationContext {
public List<String> getPathList() {
List<String> res = new ArrayList<String>();
if(resultIndex > 0){
Iterable<Object> objects = configuration.getProvider().toIterable(pathResult);
Iterable<?> objects = configuration.jsonProvider().toIterable(pathResult);
for (Object o : objects) {
res.add((String)o);
}

4
json-path/src/main/java/com/jayway/jsonpath/internal/compiler/PathToken.java

@ -27,7 +27,7 @@ public abstract class PathToken {
if(propertyVal == JsonProvider.UNDEFINED){
if(isLeaf()) {
if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)){
propertyVal = null;
propertyVal = ctx.jsonProvider().createNull();
} else {
if(ctx.options().contains(Option.SUPPRESS_EXCEPTIONS)){
return;
@ -58,7 +58,7 @@ public abstract class PathToken {
Object propertyVal = readObjectProperty(property, model, ctx);
if(propertyVal == JsonProvider.UNDEFINED){
if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)){
propertyVal = null;
propertyVal = ctx.jsonProvider().createNull();;
} else {
continue;
}

2
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<Object> objects = ctx.jsonProvider().toIterable(model);
Iterable<?> objects = ctx.jsonProvider().toIterable(model);
for (Object idxModel : objects) {
if (accept(idxModel, ctx.configuration())) {

13
json-path/src/main/java/com/jayway/jsonpath/internal/compiler/ScanPathToken.java

@ -1,11 +1,11 @@
package com.jayway.jsonpath.internal.compiler;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import com.jayway.jsonpath.spi.json.JsonProvider;
/**
*
*/
@ -55,7 +55,7 @@ public class ScanPathToken extends PathToken {
predicateMatches.put(currentPath, model);
}
Iterable<Object> models = ctx.jsonProvider().toIterable(model);
Iterable<?> models = ctx.jsonProvider().toIterable(model);
int idx = 0;
for (Object evalModel : models) {
String evalPath = currentPath + "[" + idx + "]";
@ -197,8 +197,11 @@ public class ScanPathToken extends PathToken {
@Override
public boolean matches(Object model) {
Collection<String> keys = ctx.jsonProvider().getPropertyKeys(model);
return keys.containsAll(propertyPathToken.getProperties());
if(ctx.jsonProvider().isMap(model)){
Collection<String> keys = ctx.jsonProvider().getPropertyKeys(model);
return keys.containsAll(propertyPathToken.getProperties());
}
return false;
}
}
}

40
json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/ConverterBase.java

@ -0,0 +1,40 @@
package com.jayway.jsonpath.internal.spi.converter;
import com.jayway.jsonpath.spi.converter.ConversionException;
import com.jayway.jsonpath.spi.converter.Converter;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public abstract class ConverterBase implements Converter{
private final Set<Converter.ConvertiblePair> convertiblePairs = new HashSet<Converter.ConvertiblePair>();
protected void register(Class<?> srcType, Class<?> targetType){
convertiblePairs.add(new ConvertiblePair(srcType, targetType));
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.unmodifiableSet(convertiblePairs);
}
void assertValidConversion(Object src, Class<?> srcType, Class<?> targetType) {
if (src == null) {
return;
}
if (!srcType.isAssignableFrom(src.getClass())) {
throw new ConversionException("Source: " + src.getClass() + " is not assignable from: " + srcType.getName());
}
if(!canConvert(srcType, targetType)){
throw new ConversionException("Can not convert: " + srcType.getName() + " to: " + targetType.getName());
}
}
boolean canConvert(Class<?> srcType, Class<?> targetType){
return convertiblePairs.contains(new Converter.ConvertiblePair(srcType, targetType));
}
}

38
json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/DateConverter.java

@ -0,0 +1,38 @@
package com.jayway.jsonpath.internal.spi.converter;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.spi.converter.ConversionException;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
public class DateConverter extends ConverterBase {
public DateConverter() {
register(Long.class, Date.class);
register(String.class, Date.class);
}
@Override
public Object convert(Object src, Class<?> srcType, Class<?> targetType, Configuration conf) {
assertValidConversion(src, srcType, targetType);
if(src == null){
return null;
}
if(Long.class.isAssignableFrom(srcType)){
return new Date((Long) src);
}
else if(String.class.isAssignableFrom(srcType)){
try {
return DateFormat.getInstance().parse(src.toString());
} catch (ParseException e) {
throw new ConversionException(e);
}
}
throw new ConversionException("Can not convert: " + srcType.getName() + " to: " + targetType.getName());
}
}

66
json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/DefaultConversionProvider.java

@ -0,0 +1,66 @@
package com.jayway.jsonpath.internal.spi.converter;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.spi.converter.ConversionProvider;
import com.jayway.jsonpath.spi.converter.Converter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
public class DefaultConversionProvider implements ConversionProvider {
private static final Logger logger = LoggerFactory.getLogger(DefaultConversionProvider.class);
private HashMap<Class<?>, HashMap<Class<?>, Converter>> converters = new HashMap<Class<?>, HashMap<Class<?>, Converter>>();
public DefaultConversionProvider(){
addConverters(new NumberConverter());
addConverters(new StringConverter());
addConverters(new DateConverter());
try {
Class.forName("com.google.gson.Gson");
addConverters(new GsonConverter());
} catch (ClassNotFoundException e) {
logger.debug("Gson not found on class path. No converters configured.");
}
}
public void addConverters(ConverterBase converter) {
for (Converter.ConvertiblePair convertible : converter.getConvertibleTypes()) {
if(!converters.containsKey(convertible.getTargetType())){
converters.put(convertible.getTargetType(), new HashMap<Class<?>, Converter>());
}
converters.get(convertible.getTargetType()).put(convertible.getSourceType(), converter);
}
}
@Override
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
HashMap<Class<?>, Converter> targetConverters = converters.get(targetType);
return targetConverters != null && targetConverters.containsKey(sourceType);
}
@Override
public <T> T convert(Object source, Class<T> targetType, Configuration configuration) {
if(source == null){
return null;
}
HashMap<Class<?>, Converter> targetConverters = converters.get(targetType);
if(targetConverters != null){
Converter converter = targetConverters.get(source.getClass());
if(converter != null){
return (T)converter.convert(source, source.getClass(), targetType, configuration);
}
converter = targetConverters.get(Object.class);
if(converter != null){
return (T)converter.convert(source, source.getClass(), targetType, configuration);
}
}
return (T)source;
}
}

90
json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/GsonConverter.java

@ -0,0 +1,90 @@
package com.jayway.jsonpath.internal.spi.converter;
import com.google.gson.JsonArray;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.spi.converter.ConversionException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class GsonConverter extends ConverterBase {
public GsonConverter() {
register(JsonPrimitive.class, Integer.class);
register(JsonPrimitive.class, Long.class);
register(JsonPrimitive.class, Float.class);
register(JsonPrimitive.class, Double.class);
register(JsonPrimitive.class, BigDecimal.class);
register(JsonPrimitive.class, BigInteger.class);
register(JsonPrimitive.class, Date.class);
register(JsonPrimitive.class, String.class);
register(JsonPrimitive.class, Boolean.class);
register(JsonArray.class, List.class);
register(JsonArray.class, Map.class);
}
@Override
public Object convert(Object src, Class<?> srcType, Class<?> targetType, Configuration conf) {
assertValidConversion(src, srcType, targetType);
if (src == null || src.getClass().equals(JsonNull.class)) {
return null;
}
if (JsonPrimitive.class.isAssignableFrom(srcType)) {
JsonPrimitive primitive = (JsonPrimitive) src;
if (targetType.equals(Long.class)) {
return primitive.getAsLong();
} else if (targetType.equals(Integer.class)) {
return primitive.getAsInt();
} else if (targetType.equals(BigInteger.class)) {
return primitive.getAsBigInteger();
} else if (targetType.equals(Byte.class)) {
return primitive.getAsByte();
} else if (targetType.equals(BigDecimal.class)) {
return primitive.getAsBigDecimal();
} else if (targetType.equals(Double.class)) {
return primitive.getAsDouble();
} else if (targetType.equals(Float.class)) {
return primitive.getAsFloat();
} else if (targetType.equals(String.class)) {
return primitive.getAsString();
} else if (targetType.equals(Boolean.class)) {
return primitive.getAsBoolean();
} else if (targetType.equals(Date.class)) {
if(primitive.isNumber()){
return new Date(primitive.getAsLong());
} else if(primitive.isString()){
try {
return DateFormat.getInstance().parse(primitive.getAsString());
} catch (ParseException e) {
throw new ConversionException(e);
}
}
}
} else if (JsonObject.class.isAssignableFrom(srcType)) {
} else if (JsonArray.class.isAssignableFrom(srcType)) {
}
return null;
}
}

130
json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/NumberConverter.java

@ -0,0 +1,130 @@
package com.jayway.jsonpath.internal.spi.converter;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.spi.converter.ConversionException;
import java.math.BigDecimal;
public class NumberConverter extends ConverterBase {
public NumberConverter() {
//to long
register(Integer.class, Long.class);
register(Double.class, Long.class);
register(Float.class, Long.class);
register(BigDecimal.class, Long.class);
register(String.class, Long.class);
//to int
register(Long.class, Integer.class);
register(Double.class, Integer.class);
register(Float.class, Integer.class);
register(BigDecimal.class, Integer.class);
register(String.class, Integer.class);
//to double
register(Long.class, Double.class);
register(Integer.class, Double.class);
register(Float.class, Double.class);
register(BigDecimal.class, Double.class);
register(String.class, Double.class);
//to float
register(Long.class, Float.class);
register(Integer.class, Float.class);
register(Double.class, Float.class);
register(BigDecimal.class, Float.class);
register(String.class, Float.class);
//to BigDecimal
register(Long.class, BigDecimal.class);
register(Integer.class, BigDecimal.class);
register(Double.class, BigDecimal.class);
register(Float.class, BigDecimal.class);
register(String.class, BigDecimal.class);
}
@Override
public Object convert(Object src, Class<?> srcType, Class<?> targetType, Configuration conf) {
assertValidConversion(src, srcType, targetType);
if (src == null) {
return null;
}
//to long
if(targetType.equals(Long.class)) {
if (Integer.class.isAssignableFrom(srcType)) {
return ((Integer) src).longValue();
} else if (Double.class.isAssignableFrom(srcType)) {
return ((Double) src).longValue();
} else if (BigDecimal.class.isAssignableFrom(srcType)) {
return ((BigDecimal) src).longValue();
} else if (Float.class.isAssignableFrom(srcType)) {
return ((Float) src).longValue();
} else if (String.class.isAssignableFrom(srcType)) {
return Long.parseLong(src.toString());
}
}
else if(targetType.equals(Integer.class)) {
//to int
if (Long.class.isAssignableFrom(srcType)) {
return ((Long) src).intValue();
} else if (Double.class.isAssignableFrom(srcType)) {
return ((Double) src).intValue();
} else if (BigDecimal.class.isAssignableFrom(srcType)) {
return ((BigDecimal) src).intValue();
} else if (Float.class.isAssignableFrom(srcType)) {
return ((Float) src).intValue();
} else if (String.class.isAssignableFrom(srcType)) {
return Integer.parseInt(src.toString());
}
}
else if(targetType.equals(Double.class)) {
//to double
if (Long.class.isAssignableFrom(srcType)) {
return ((Long) src).doubleValue();
} else if (Integer.class.isAssignableFrom(srcType)) {
return ((Integer) src).doubleValue();
} else if (BigDecimal.class.isAssignableFrom(srcType)) {
return ((BigDecimal) src).doubleValue();
} else if (Float.class.isAssignableFrom(srcType)) {
return ((Float) src).doubleValue();
} else if (String.class.isAssignableFrom(srcType)) {
return Double.parseDouble(src.toString());
}
}
else if(targetType.equals(Float.class)) {
//to float
if (Long.class.isAssignableFrom(srcType) && targetType.equals(Float.class)) {
return ((Long) src).floatValue();
} else if (Integer.class.isAssignableFrom(srcType) && targetType.equals(Float.class)) {
return ((Integer) src).floatValue();
} else if (BigDecimal.class.isAssignableFrom(srcType) && targetType.equals(Float.class)) {
return ((BigDecimal) src).floatValue();
} else if (Double.class.isAssignableFrom(srcType) && targetType.equals(Float.class)) {
return ((Double) src).floatValue();
} else if (String.class.isAssignableFrom(srcType) && targetType.equals(Float.class)) {
return Float.parseFloat(src.toString());
}
}
else if(targetType.equals(BigDecimal.class)) {
//to BigDecimal
if (Long.class.isAssignableFrom(srcType) && targetType.equals(BigDecimal.class)) {
return new BigDecimal(src.toString());
} else if (Integer.class.isAssignableFrom(srcType) && targetType.equals(BigDecimal.class)) {
return new BigDecimal(src.toString());
} else if (Float.class.isAssignableFrom(srcType) && targetType.equals(BigDecimal.class)) {
return new BigDecimal(src.toString());
} else if (Double.class.isAssignableFrom(srcType) && targetType.equals(BigDecimal.class)) {
return new BigDecimal(src.toString());
} else if (String.class.isAssignableFrom(srcType) && targetType.equals(BigDecimal.class)) {
return new BigDecimal(src.toString());
}
}
throw new ConversionException("Can not convert: " + srcType.getName() + " to: " + targetType.getName());
}
}

25
json-path/src/main/java/com/jayway/jsonpath/internal/spi/converter/StringConverter.java

@ -0,0 +1,25 @@
package com.jayway.jsonpath.internal.spi.converter;
import com.jayway.jsonpath.Configuration;
public class StringConverter extends ConverterBase {
public StringConverter() {
register(Object.class, String.class);
}
@Override
public Object convert(Object src, Class<?> srcType, Class<?> targetType, Configuration conf) {
assertValidConversion(src, srcType, targetType);
if (src == null) {
return null;
}
return src.toString();
}
@Override
boolean canConvert(Class<?> srcType, Class<?> targetType){
return true;
}
}

75
json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/AbstractJsonProvider.java

@ -14,8 +14,12 @@
*/
package com.jayway.jsonpath.internal.spi.json;
import com.jayway.jsonpath.ValueCompareException;
import com.jayway.jsonpath.spi.json.JsonProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -23,6 +27,52 @@ import java.util.Map;
public abstract class AbstractJsonProvider implements JsonProvider {
private static final Logger logger = LoggerFactory.getLogger(AbstractJsonProvider.class);
public Object unwrap(Object obj){
return obj;
}
public int compare(Object expected, Object providerParsed) throws ValueCompareException {
boolean expNullish = isNullish(expected);
boolean provNullish = isNullish(providerParsed);
if (expNullish && !provNullish) {
return -1;
} else if (!expNullish && provNullish) {
return 1;
} else if (expNullish && provNullish) {
return 0;
} else if (expected instanceof String && providerParsed instanceof String) {
return ((String) expected).compareTo((String) providerParsed);
} else if (expected instanceof Number && providerParsed instanceof Number) {
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(providerParsed.toString()));
} else if (expected instanceof String && providerParsed instanceof Number) {
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(providerParsed.toString()));
} else if (expected instanceof String && providerParsed instanceof Boolean) {
Boolean e = Boolean.valueOf((String)expected);
Boolean a = (Boolean) providerParsed;
return e.compareTo(a);
} else if (expected instanceof Boolean && providerParsed instanceof Boolean) {
Boolean e = (Boolean) expected;
Boolean a = (Boolean) providerParsed;
return e.compareTo(a);
} else {
logger.debug("Can not compare a {} with a {}", expected.getClass().getName(), providerParsed.getClass().getName());
throw new ValueCompareException();
}
}
private static boolean isNullish(Object o){
return (o == null || ((o instanceof String) && ("null".equals(o))));
}
@Override
public Object createNull(){
return null;
}
/**
* checks if object is an array
*
@ -33,6 +83,10 @@ public abstract class AbstractJsonProvider implements JsonProvider {
return (obj instanceof List);
}
public boolean isString(Object obj){
return (obj instanceof String);
}
/**
* Extracts a value from an array
@ -96,20 +150,15 @@ public abstract class AbstractJsonProvider implements JsonProvider {
}
/**
* Returns the keys from the given object or the indexes from an array
* Returns the keys from the given object
*
* @param obj an array or an object
* @return the keys for an object or the indexes for an array
* @param obj an object
* @return the keys for an object
*/
@SuppressWarnings("unchecked")
public Collection<String> getPropertyKeys(Object obj) {
if (isArray(obj)) {
List l = (List) obj;
List<String> keys = new ArrayList<String>(l.size());
for (int i = 0; i < l.size(); i++) {
keys.add(String.valueOf(i));
}
return keys;
throw new UnsupportedOperationException();
} else {
return ((Map) obj).keySet();
}
@ -124,8 +173,12 @@ public abstract class AbstractJsonProvider implements JsonProvider {
public int length(Object obj) {
if (isArray(obj)) {
return ((List) obj).size();
} else if (isMap(obj)){
return getPropertyKeys(obj).size();
} else if(obj instanceof String){
return ((String)obj).length();
}
return getPropertyKeys(obj).size();
throw new RuntimeException("length operation can not applied to " + obj!=null?obj.getClass().getName():"null");
}
/**
@ -135,7 +188,7 @@ public abstract class AbstractJsonProvider implements JsonProvider {
* @return the entries for an array or the values for a map
*/
@SuppressWarnings("unchecked")
public Iterable<Object> toIterable(Object obj) {
public Iterable<? extends Object> toIterable(Object obj) {
if (isArray(obj))
return ((Iterable) obj);
else

358
json-path/src/main/java/com/jayway/jsonpath/internal/spi/json/GsonProvider.java

@ -0,0 +1,358 @@
package com.jayway.jsonpath.internal.spi.json;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.internal.LazilyParsedNumber;
import com.jayway.jsonpath.InvalidJsonException;
import com.jayway.jsonpath.ValueCompareException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Type;
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 Logger logger = LoggerFactory.getLogger(GsonProvider.class);
private static final JsonParser parser = new JsonParser();
private static final Gson gson = new GsonBuilder().registerTypeAdapter(Number.class, new NumberTypeAdapter()).create();
static {
//ConverterFactory.registerConverter();
}
public int compare(Object expected, Object providerParsed) throws ValueCompareException {
JsonElement element = (JsonElement) providerParsed;
boolean nullish = isNullish(expected);
if (nullish && !element.isJsonNull()) {
return -1;
} else if (!nullish && element.isJsonNull()) {
return 1;
} else if (nullish && element.isJsonNull()) {
return 0;
}
if(element.isJsonPrimitive()){
JsonPrimitive primitive = element.getAsJsonPrimitive();
if (expected instanceof String && primitive.isString()) {
return ((String) expected).compareTo(primitive.getAsString());
} else if (expected instanceof Number && primitive.isNumber()) {
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(primitive.toString()));
} else if (expected instanceof String && primitive.isNumber()) {
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(primitive.toString()));
} else if (expected instanceof String && primitive.isBoolean()) {
Boolean e = Boolean.valueOf((String)expected);
Boolean a = primitive.getAsBoolean();
return e.compareTo(a);
} else if (expected instanceof Boolean && primitive.isBoolean()) {
Boolean e = (Boolean) expected;
Boolean a = primitive.getAsBoolean();
return e.compareTo(a);
}
}
logger.debug("Can not compare a {} with a {}", expected.getClass().getName(), providerParsed.getClass().getName());
throw new ValueCompareException();
}
private static boolean isNullish(Object o){
return (o == null || ((o instanceof String) && ("null".equals(o))));
}
public Object unwrap(Object o) {
return 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()) {
unwrapped = unwrapNumber(p.getAsNumber());
}
} else {
//unwrapped = o;
if (e.isJsonArray()) {
JsonArray res = new JsonArray();
for (JsonElement jsonElement : e.getAsJsonArray()) {
if(jsonElement.isJsonPrimitive()){
JsonPrimitive primitive = jsonElement.getAsJsonPrimitive();
if(primitive.isNumber()){
res.add(new JsonPrimitive(unwrapNumber(primitive.getAsNumber())));
}
}
}
unwrapped = res;
//unwrapped = gson.fromJson(e, List.class);
} else {
unwrapped = gson.fromJson(e, Map.class);
}
}
return unwrapped;
*/
}
private Number unwrapNumber(Number n){
Number unwrapped;
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;
}
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 obj.toString();
}
@Override
public Object createNull(){
return JsonNull.INSTANCE;
}
@Override
public Object createMap() {
return new JsonObject();
}
@Override
public Object createArray() {
return new JsonArray();
}
@Override
public boolean isArray(Object obj) {
return (obj instanceof JsonArray);
}
public boolean isString(Object obj){
if(obj == null) {
return false;
}
JsonElement element = toJsonElement(obj);
if(element.isJsonPrimitive()){
return element.getAsJsonPrimitive().isString();
}
return false;
}
@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(), createJsonElement(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(createJsonElement(value));
} else {
array.set(index, createJsonElement(value));
}
}
}
@Override
public boolean isMap(Object obj) {
return (obj instanceof JsonObject) ;
}
@Override
public Collection<String> getPropertyKeys(Object obj) {
List<String> keys = new ArrayList<String>();
for (Map.Entry<String, JsonElement> entry : toJsonObject(obj).entrySet()) {
keys.add(entry.getKey());
}
return keys;
}
@Override
public int length(Object obj) {
if (isArray(obj)) {
return toJsonArray(obj).size();
} else if(isMap(obj)){
return toJsonObject(obj).entrySet().size();
} else {
if(obj instanceof JsonElement){
JsonElement element = toJsonElement(obj);
if(element.isJsonPrimitive()){
return element.toString().length();
}
}
}
throw new RuntimeException("length operation can not applied to " + obj!=null?obj.getClass().getName():"null");
}
@Override
public Iterable<?> toIterable(Object obj) {
if (isArray(obj)) {
return toJsonArray(obj);
} else {
List<JsonElement> values = new ArrayList<JsonElement>();
JsonObject jsonObject = toJsonObject(obj);
for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
values.add(entry.getValue());
}
return values;
}
}
private JsonElement createJsonElement(Object o) {
return gson.toJsonTree(o);
}
private JsonArray toJsonArray(Object o) {
return (JsonArray) o;
}
private JsonObject toJsonObject(Object o) {
return (JsonObject) o;
}
private JsonElement toJsonElement(Object o) {
return (JsonElement) o;
}
public static class NumberTypeAdapter
implements JsonSerializer<Number>, JsonDeserializer<Number>,
InstanceCreator<Number> {
public JsonElement serialize(Number src, Type typeOfSrc, JsonSerializationContext
context) {
return new JsonPrimitive(src);
}
public Number deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)
throws JsonParseException {
Number res = null;
JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
if (jsonPrimitive.isNumber()) {
Number n = jsonPrimitive.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) {
res = bigDecimal.intValue();
} else {
res = bigDecimal.longValue();
}
} else {
if (bigDecimal.compareTo(new BigDecimal(Float.MAX_VALUE)) <= 0) {
res = bigDecimal.floatValue();
} else {
res = bigDecimal.doubleValue();
}
}
}
} else {
throw new IllegalStateException("Expected a number field, but was " + json);
}
return res;
}
public Number createInstance(Type type) {
return 1L;
}
}
}

21
json-path/src/main/java/com/jayway/jsonpath/spi/converter/ConversionException.java

@ -0,0 +1,21 @@
package com.jayway.jsonpath.spi.converter;
public class ConversionException extends RuntimeException {
public ConversionException(String message, Throwable cause) {
super(message, cause);
}
public ConversionException(Throwable cause) {
super(cause);
}
public ConversionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public ConversionException(String message) {
super(message);
}
}

10
json-path/src/main/java/com/jayway/jsonpath/spi/converter/ConversionProvider.java

@ -0,0 +1,10 @@
package com.jayway.jsonpath.spi.converter;
import com.jayway.jsonpath.Configuration;
public interface ConversionProvider {
boolean canConvert(Class<?> sourceType, Class<?> targetType);
<T> T convert(Object source, Class<T> targetType, Configuration configuration);
}

69
json-path/src/main/java/com/jayway/jsonpath/spi/converter/Converter.java

@ -2,6 +2,71 @@ package com.jayway.jsonpath.spi.converter;
import com.jayway.jsonpath.Configuration;
public interface Converter<T>{
T convert(Object o, Configuration conf);
import java.util.Set;
import static com.jayway.jsonpath.internal.Utils.notNull;
public interface Converter {
Set<ConvertiblePair> getConvertibleTypes();
Object convert(Object src, Class<?> srcType, Class<?> targetType, Configuration conf);
/**
* Holder for a source-to-target class pair.
*/
public static final class ConvertiblePair {
private final Class<?> sourceType;
private final Class<?> targetType;
/**
* Create a new source-to-target pair.
*
* @param sourceType the source type
* @param targetType the target type
*/
public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
notNull(sourceType, "Source type must not be null");
notNull(targetType, "Target type must not be null");
this.sourceType = sourceType;
this.targetType = targetType;
}
public Class<?> getSourceType() {
return this.sourceType;
}
public Class<?> getTargetType() {
return this.targetType;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ConvertiblePair)) return false;
ConvertiblePair that = (ConvertiblePair) o;
if (!sourceType.equals(that.sourceType)) return false;
if (!targetType.equals(that.targetType)) return false;
return true;
}
@Override
public int hashCode() {
int result = sourceType.hashCode();
result = 31 * result + targetType.hashCode();
return result;
}
@Override
public String toString() {
return this.sourceType.getName() + " -> " + this.targetType.getName();
}
}
}

174
json-path/src/main/java/com/jayway/jsonpath/spi/converter/ConverterFactory.java

@ -1,174 +0,0 @@
package com.jayway.jsonpath.spi.converter;
import com.jayway.jsonpath.Configuration;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ConverterFactory {
private static Map<Class<?>, Converter<?>> converters = new ConcurrentHashMap<Class<?>, Converter<?>>();
static {
registerConverter(Long.class, new LongConverter());
registerConverter(Integer.class, new IntegerConverter());
registerConverter(BigDecimal.class, new BigDecimalConverter());
registerConverter(Double.class, new DoubleConverter());
registerConverter(Date.class, new DateConverter());
registerConverter(String.class, new StringConverter());
}
public static <T> Converter<T> createConverter(Class<T> target){
Converter<T> converter = (Converter<T>) converters.get(target);
if(converter == null){
converter = new Converter<T>() {
@Override
public T convert(Object o, Configuration conf) {
return (T)o;
}
};
}
return converter;
}
public static <T> void registerConverter(Class<T> target, Converter<T> converter){
converters.put(target, converter);
}
public static <T> void unRegisterConverter(Class<T> target){
converters.remove(target);
}
private static class StringConverter implements Converter<String> {
@Override
public String convert(Object o, Configuration conf) {
if(o == null){
return null;
} else {
return o.toString();
}
}
}
private static class DateConverter implements Converter<Date> {
@Override
public Date convert(Object o, Configuration conf) {
if(o == null){
return null;
} else if(o instanceof Date){
return (Date)o;
} else if(o instanceof Long){
return new Date(((Long)o).longValue());
} else if(o instanceof String){
try {
return DateFormat.getInstance().parse(o.toString());
} catch (ParseException e) {
throw new IllegalArgumentException("Can not convert: " + o.getClass().getName() + " to: " + Integer.class.getName(), e);
}
}
throw new IllegalArgumentException("Can not convert: " + o.getClass().getName() + " to: " + Integer.class.getName());
}
}
private static class IntegerConverter implements Converter<Integer> {
@Override
public Integer convert(Object o, Configuration conf) {
if(o == null){
return null;
} else if(o instanceof Integer){
return (Integer)o;
} else if(o instanceof Long){
return ((Long)o).intValue();
} else if(o instanceof Double){
return ((Double)o).intValue();
} else if(o instanceof BigDecimal){
return ((BigDecimal)o).intValue();
} else if(o instanceof Float){
return ((Float)o).intValue();
} else if(o instanceof String){
return Integer.parseInt(o.toString());
}
throw new IllegalArgumentException("Can not convert: " + o.getClass().getName() + " to: " + Integer.class.getName());
}
}
private static class LongConverter implements Converter<Long> {
@Override
public Long convert(Object o, Configuration conf) {
if(o == null){
return null;
} else if(o instanceof Long){
return (Long)o;
} else if(o instanceof Integer){
return ((Integer)o).longValue();
} else if(o instanceof Double){
return ((Double)o).longValue();
} else if(o instanceof BigDecimal){
return ((BigDecimal)o).longValue();
} else if(o instanceof Float){
return ((Float)o).longValue();
} else if(o instanceof String){
return Long.parseLong(o.toString());
}
throw new IllegalArgumentException("Can not convert: " + o.getClass().getName() + " to: " + Long.class.getName());
}
}
private static class DoubleConverter implements Converter<Double> {
@Override
public Double convert(Object o, Configuration conf) {
if(o == null){
return null;
} else if(o instanceof Double){
return (Double)o;
} else if(o instanceof Integer){
return Double.valueOf(o.toString());
} else if(o instanceof Long){
return Double.valueOf(o.toString());
} else if(o instanceof BigDecimal){
return ((BigDecimal)o).doubleValue();
} else if(o instanceof Float){
return ((Float)o).doubleValue();
} else if(o instanceof String){
return Double.parseDouble(o.toString());
}
throw new IllegalArgumentException("Can not convert: " + o.getClass().getName() + " to: " + Double.class.getName());
}
}
private static class BigDecimalConverter implements Converter<BigDecimal> {
@Override
public BigDecimal convert(Object o, Configuration conf) {
if(o == null){
return null;
} else if(o instanceof BigDecimal){
return (BigDecimal)o;
} else if(o instanceof Integer){
return new BigDecimal(o.toString());
} else if(o instanceof Long){
return new BigDecimal(o.toString());
} else if(o instanceof Float){
return BigDecimal.valueOf(((Float)o).doubleValue());
} else if(o instanceof String){
return new BigDecimal(o.toString());
}
throw new IllegalArgumentException("Can not convert: " + o.getClass().getName() + " to: " + BigDecimal.class.getName());
}
}
}

28
json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonProvider.java

@ -15,6 +15,7 @@
package com.jayway.jsonpath.spi.json;
import com.jayway.jsonpath.InvalidJsonException;
import com.jayway.jsonpath.ValueCompareException;
import java.io.InputStream;
import java.io.Reader;
@ -24,6 +25,10 @@ public interface JsonProvider {
static final Object UNDEFINED = new Object();
Object unwrap(Object obj);
int compare(Object expected, Object providerParsed) throws ValueCompareException;
Object parse(String json) throws InvalidJsonException;
Object parse(Reader jsonReader) throws InvalidJsonException;
@ -32,6 +37,8 @@ public interface JsonProvider {
String toJson(Object obj);
Object createNull();
Object createMap();
Object createArray();
@ -45,9 +52,18 @@ public interface JsonProvider {
boolean isArray(Object obj);
/**
* Get the length of an array or object
* checks if object is a string
*
* @param obj an array or an object
* @param obj object to check
* @return true if obj is an array
*/
boolean isString(Object obj);
/**
* Get the length of an json array, json object or a json string
*
* @param obj an array or object or a string
* @return the number of entries in the array or object
*/
int length(Object obj);
@ -58,14 +74,14 @@ public interface JsonProvider {
* @param obj an array or an object
* @return the entries for an array or the values for a map
*/
Iterable<Object> toIterable(Object obj);
Iterable<?> toIterable(Object obj);
/**
* Returns the keys from the given object or the indexes from an array
* Returns the keys from the given object
*
* @param obj an array or an object
* @return the keys for an object or the indexes for an array
* @param obj an object
* @return the keys for an object
*/
Collection<String> getPropertyKeys(Object obj);

31
json-path/src/test/java/com/jayway/jsonpath/BaseTest.java

@ -1,6 +1,37 @@
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.spi.converter.DefaultConversionProvider;
import com.jayway.jsonpath.internal.spi.json.GsonProvider;
import com.jayway.jsonpath.spi.converter.ConversionProvider;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.EnumSet;
import java.util.Set;
public class BaseTest {
/*
static {
Configuration.setDefaults(new Configuration.Defaults() {
@Override
public JsonProvider jsonProvider() {
return new GsonProvider();
}
@Override
public Set<Option> options() {
return EnumSet.noneOf(Option.class);
}
@Override
public ConversionProvider conversionProvider() {
return new DefaultConversionProvider();
}
});
}
*/
public static final String JSON_DOCUMENT = "{\n" +
" \"string-property\" : \"string-value\", \n" +

19
json-path/src/test/java/com/jayway/jsonpath/ConverterTest.java

@ -1,9 +1,15 @@
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.spi.converter.DefaultConversionProvider;
import com.jayway.jsonpath.internal.spi.json.GsonProvider;
import com.jayway.jsonpath.spi.converter.ConversionProvider;
import com.jayway.jsonpath.spi.json.JsonProvider;
import org.junit.Test;
import java.math.BigDecimal;
import java.util.Date;
import java.util.EnumSet;
import java.util.Set;
import static com.jayway.jsonpath.JsonPath.parse;
import static java.util.Collections.singletonMap;
@ -11,35 +17,36 @@ import static org.assertj.core.api.Assertions.assertThat;
public class ConverterTest extends BaseTest {
@Test
public void an_Integer_can_be_converted_to_a_Long() {
assertThat(parse(singletonMap("val", 1)).read("val", Long.class)).isEqualTo(1L);
assertThat(parse("{\"val\": 1}").read("val", Long.class)).isEqualTo(1L);
}
@Test
public void an_String_can_be_converted_to_a_Long() {
assertThat(parse(singletonMap("val", "1")).read("val", Long.class)).isEqualTo(1L);
assertThat(parse("{\"val\": 1}").read("val", Long.class)).isEqualTo(1L);
}
@Test
public void an_Integer_can_be_converted_to_a_String() {
assertThat(parse(singletonMap("val", 1)).read("val", String.class)).isEqualTo("1");
assertThat(parse("{\"val\": 1}").read("val", String.class)).isEqualTo("1");
}
@Test
public void an_Integer_can_be_converted_to_a_Double() {
assertThat(parse(singletonMap("val", 1)).read("val", Double.class)).isEqualTo(1D);
assertThat(parse("{\"val\": 1}").read("val", Double.class)).isEqualTo(1D);
}
@Test
public void a_BigDecimal_can_be_converted_to_a_Long() {
assertThat(parse(singletonMap("val", new BigDecimal(1.5D))).read("val", Long.class)).isEqualTo(1L);
assertThat(parse("{\"val\": 1.5}").read("val", Long.class)).isEqualTo(1L);
}
@Test
public void a_Long_can_be_converted_to_a_Date() {
Date now = new Date();
assertThat(parse(singletonMap("val", now.getTime())).read("val", Date.class)).isEqualTo(now);
assertThat(parse("{\"val\": "+now.getTime()+"}").read("val", Date.class)).isEqualTo(now);
}
}

4
json-path/src/test/java/com/jayway/jsonpath/FilterTest.java

@ -12,8 +12,8 @@ import static org.assertj.core.api.Assertions.assertThat;
public class FilterTest extends BaseTest {
Configuration conf = Configuration.defaultConfiguration();
Object json = conf.getProvider().parse(
Object json = Configuration.defaultConfiguration().jsonProvider().parse(
"{" +
" \"int-key\" : 1, " +
" \"long-key\" : 3000000000, " +

12
json-path/src/test/java/com/jayway/jsonpath/OptionsTest.java

@ -20,7 +20,7 @@ public class OptionsTest extends BaseTest {
Configuration conf = Configuration.defaultConfiguration();
assertThat(using(conf).parse(singletonMap("foo", "bar")).read("$.baz")).isNull();
assertThat(using(conf).parse("{\"foo\" : \"bar\"}").read("$.baz")).isNull();
}
@Test
@ -28,7 +28,7 @@ public class OptionsTest extends BaseTest {
Configuration conf = Configuration.builder().options(DEFAULT_PATH_LEAF_TO_NULL).build();
assertThat(using(conf).parse(singletonMap("foo", "bar")).read("$.baz")).isNull();
assertThat(using(conf).parse("{\"foo\" : \"bar\"}").read("$.baz", Object.class)).isNull();
}
@Test
@ -36,7 +36,7 @@ public class OptionsTest extends BaseTest {
Configuration conf = Configuration.defaultConfiguration();
assertThat(using(conf).parse(singletonMap("foo", "bar")).read("$.foo")).isInstanceOf(String.class);
assertThat(using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isInstanceOf(String.class);
}
@Test
@ -44,21 +44,21 @@ public class OptionsTest extends BaseTest {
Configuration conf = Configuration.builder().options(ALWAYS_RETURN_LIST).build();
assertThat(using(conf).parse(singletonMap("foo", "bar")).read("$.foo")).isInstanceOf(List.class);
assertThat(using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isInstanceOf(List.class);
}
@Test
public void a_path_evaluation_is_returned_as_VALUE_by_default() {
Configuration conf = Configuration.defaultConfiguration();
assertThat(using(conf).parse(singletonMap("foo", "bar")).read("$.foo")).isEqualTo("bar");
assertThat(using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isEqualTo("bar");
}
@Test
public void a_path_evaluation_can_be_returned_as_PATH_LIST() {
Configuration conf = Configuration.builder().options(AS_PATH_LIST).build();
List<String> pathList = using(conf).parse(singletonMap("foo", "bar")).read("$.foo");
List<String> pathList = using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo");
assertThat(pathList).containsOnly("$['foo']");
}

10
json-path/src/test/java/com/jayway/jsonpath/ReturnTypeTest.java

@ -10,6 +10,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@SuppressWarnings("ALL")
public class ReturnTypeTest extends BaseTest {
private static ReadContext reader = JsonPath.parse(JSON_DOCUMENT);
@Test
@ -19,7 +20,7 @@ public class ReturnTypeTest extends BaseTest {
@Test
public void assert_ints_can_be_read() {
assertThat(reader.read("$.int-max-property")).isEqualTo(Integer.MAX_VALUE);
assertThat(reader.read("$.int-max-property", Integer.class)).isEqualTo(Integer.MAX_VALUE);
}
@Test
@ -39,6 +40,13 @@ public class ReturnTypeTest extends BaseTest {
@Test
public void assert_arrays_can_be_read() {
/*
Object result = reader.read("$.store.book");
assertThat(reader.configuration().jsonProvider().isArray(result)).isTrue();
assertThat(reader.configuration().jsonProvider().length(result)).isEqualTo(4);
*/
assertThat(reader.read("$.store.book", List.class)).hasSize(4);
}

2
json-path/src/test/java/com/jayway/jsonpath/old/FilterTest.java

@ -347,7 +347,7 @@ public class FilterTest extends BaseTest {
Predicate customFilter = new Predicate () {
@Override
public boolean apply(PredicateContext ctx) {
if (ctx.configuration().getProvider().getMapValue(ctx.target(), "name").equals("rootGrandChild_A")) {
if (ctx.configuration().jsonProvider().getMapValue(ctx.target(), "name").equals("rootGrandChild_A")) {
return true;
}
return false;

5
json-path/src/test/java/com/jayway/jsonpath/old/JsonPathTest.java

@ -1,5 +1,6 @@
package com.jayway.jsonpath.old;
import com.jayway.jsonpath.BaseTest;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
@ -16,7 +17,7 @@ import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.*;
public class JsonPathTest {
public class JsonPathTest extends BaseTest {
static {
@ -61,7 +62,7 @@ public class JsonPathTest {
" }\n" +
"}";
public final static Object OBJ_DOCUMENT = new JsonSmartJsonProvider().parse(DOCUMENT);
public final static Object OBJ_DOCUMENT = JsonPath.parse(DOCUMENT).json();
private final static String PRODUCT_JSON = "{\n" +

3
json-path/src/test/java/com/jayway/jsonpath/old/internal/ScanPathTokenTest.java

@ -5,6 +5,7 @@ import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.internal.PathCompiler;
import com.jayway.jsonpath.internal.spi.json.JsonSmartJsonProvider;
import org.junit.Ignore;
import org.junit.Test;
import java.util.List;
@ -91,6 +92,8 @@ public class ScanPathTokenTest {
@Test
@Ignore("does not work currently")
//FIXME handle $.. like the Goessner implementation
public void a_root_scan() {
Object o = JsonPath.read(DOCUMENT, "$..");

7
pom.xml

@ -182,6 +182,13 @@
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>io.fastjson</groupId>
<artifactId>boon</artifactId>

2
system.properties

@ -1 +1 @@
java.runtime.version=1.7
java.runtime.version=1.6
Loading…
Cancel
Save