Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
570 changes: 75 additions & 495 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion json-path/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ jar {
'Automatic-Module-Name': 'json.path',
'Implementation-Title': 'json-path', 'Implementation-Version': archiveVersion,
'Import-Package': 'org.json.*;resolution:=optional, com.google.gson.*;resolution:=optional, com.fasterxml.jackson.*;resolution:=optional, org.apache.tapestry5.json.*;resolution:=optional, org.codehaus.jettison.*;resolution:=optional, jakarta.json.*;resolution:=optional, *',
'Export-Package': 'com.jayway.jsonpath,com.jayway.jsonpath.spi,com.jayway.jsonpath.spi.cache,com.jayway.jsonpath.spi.json,com.jayway.jsonpath.spi.mapper'
'Export-Package': 'com.jayway.jsonpath,com.jayway.jsonpath.spi,com.jayway.jsonpath.spi.cache,com.jayway.jsonpath.spi.function,com.jayway.jsonpath.spi.json,com.jayway.jsonpath.spi.mapper'
)
}

Expand Down
161 changes: 149 additions & 12 deletions json-path/src/main/java/com/jayway/jsonpath/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
package com.jayway.jsonpath;

import com.jayway.jsonpath.internal.DefaultsImpl;
import com.jayway.jsonpath.spi.function.DefaultFunctionProvider;
import com.jayway.jsonpath.spi.function.FunctionProvider;
import com.jayway.jsonpath.spi.function.PathFunction;
import com.jayway.jsonpath.spi.json.JsonProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;

Expand Down Expand Up @@ -48,18 +51,25 @@ private static Defaults getEffectiveDefaults(){

private final JsonProvider jsonProvider;
private final MappingProvider mappingProvider;
private final FunctionProvider functionProvider;
private final Set<Option> options;
private final Collection<EvaluationListener> evaluationListeners;

private Configuration(JsonProvider jsonProvider, MappingProvider mappingProvider, EnumSet<Option> options, Collection<EvaluationListener> evaluationListeners) {
private Configuration(JsonProvider jsonProvider, MappingProvider mappingProvider, EnumSet<Option> options, Collection<EvaluationListener> evaluationListeners, FunctionProvider functionProvider) {
notNull(jsonProvider, "jsonProvider can not be null");
notNull(mappingProvider, "mappingProvider can not be null");
notNull(options, "setOptions can not be null");
notNull(evaluationListeners, "evaluationListeners can not be null");
notNull(functionProvider, "functionProvider can not be null");
this.jsonProvider = jsonProvider;
this.mappingProvider = mappingProvider;
this.options = Collections.unmodifiableSet(options);
this.evaluationListeners = Collections.unmodifiableCollection(evaluationListeners);
this.functionProvider = functionProvider;
}

private Configuration(JsonProvider jsonProvider, MappingProvider mappingProvider, EnumSet<Option> options, Collection<EvaluationListener> evaluationListeners) {
this(jsonProvider, mappingProvider, options, evaluationListeners, getEffectiveDefaults().functionProvider());
}

/**
Expand All @@ -68,7 +78,7 @@ private Configuration(JsonProvider jsonProvider, MappingProvider mappingProvider
* @return a new configuration
*/
public Configuration addEvaluationListeners(EvaluationListener... evaluationListener){
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(options).evaluationListener(evaluationListener).build();
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).functionProvider(functionProvider).options(options).evaluationListener(evaluationListener).build();
}

/**
Expand All @@ -77,7 +87,7 @@ public Configuration addEvaluationListeners(EvaluationListener... evaluationList
* @return a new configuration
*/
public Configuration setEvaluationListeners(EvaluationListener... evaluationListener){
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(options).evaluationListener(evaluationListener).build();
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).functionProvider(functionProvider).options(options).evaluationListener(evaluationListener).build();
}

/**
Expand All @@ -94,7 +104,7 @@ public Collection<EvaluationListener> getEvaluationListeners(){
* @return a new configuration
*/
public Configuration jsonProvider(JsonProvider newJsonProvider) {
return Configuration.builder().jsonProvider(newJsonProvider).mappingProvider(mappingProvider).options(options).evaluationListener(evaluationListeners).build();
return Configuration.builder().jsonProvider(newJsonProvider).mappingProvider(mappingProvider).functionProvider(functionProvider).options(options).evaluationListener(evaluationListeners).build();
}

/**
Expand All @@ -111,7 +121,7 @@ public JsonProvider jsonProvider() {
* @return a new configuration
*/
public Configuration mappingProvider(MappingProvider newMappingProvider) {
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(newMappingProvider).options(options).evaluationListener(evaluationListeners).build();
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(newMappingProvider).functionProvider(functionProvider).options(options).evaluationListener(evaluationListeners).build();
}

/**
Expand All @@ -122,6 +132,65 @@ public MappingProvider mappingProvider() {
return mappingProvider;
}

/**
* Creates a new Configuration based on the given {@link com.jayway.jsonpath.spi.function.FunctionProvider}
* @param newFunctionProvider function provider to use in new configuration
* @return a new configuration
*/
public Configuration functionProvider(FunctionProvider newFunctionProvider) {
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).functionProvider(newFunctionProvider).options(options).evaluationListener(evaluationListeners).build();
}

/**
* Returns {@link com.jayway.jsonpath.spi.function.FunctionProvider} used by this configuration
* @return functionProvider used
*/
public FunctionProvider functionProvider() {
return functionProvider;
}

/**
* Creates a new configuration with the registered custom function by class.
* @param name function name
* @param functionClass function implementation class
* @return a new configuration
*/
public Configuration registerFunction(String name, Class<? extends PathFunction> functionClass) {
Map<String, Object> functions = new LinkedHashMap<String, Object>();
if (functionProvider instanceof DefaultFunctionProvider) {
functions.putAll(((DefaultFunctionProvider) functionProvider).getCustomFunctions());
}
functions.put(name, functionClass);
return Configuration.builder()
.jsonProvider(jsonProvider)
.mappingProvider(mappingProvider)
.functionProvider(new DefaultFunctionProvider(functions))
.options(options)
.evaluationListener(evaluationListeners)
.build();
}

/**
* Creates a new configuration with the registered custom function by instance.
* @param name function name
* @param function function instance
* @return a new configuration
*/
public Configuration registerFunction(String name, PathFunction function) {
Map<String, Object> functions = new LinkedHashMap<String, Object>();
if (functionProvider instanceof DefaultFunctionProvider) {
functions.putAll(((DefaultFunctionProvider) functionProvider).getCustomFunctions());
}
functions.put(name, function);
return Configuration.builder()
.jsonProvider(jsonProvider)
.mappingProvider(mappingProvider)
.functionProvider(new DefaultFunctionProvider(functions))
.options(options)
.evaluationListener(evaluationListeners)
.build();
}

/**
* Creates a new configuration by adding the new options to the options used in this configuration.
* @param options options to add
Expand All @@ -131,7 +200,7 @@ public Configuration addOptions(Option... options) {
EnumSet<Option> opts = EnumSet.noneOf(Option.class);
opts.addAll(this.options);
opts.addAll(asList(options));
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(opts).evaluationListener(evaluationListeners).build();
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).functionProvider(functionProvider).options(opts).evaluationListener(evaluationListeners).build();
}

/**
Expand All @@ -140,7 +209,7 @@ public Configuration addOptions(Option... options) {
* @return the new configuration instance
*/
public Configuration setOptions(Option... options) {
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(options).evaluationListener(evaluationListeners).build();
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).functionProvider(functionProvider).options(options).evaluationListener(evaluationListeners).build();
}

/**
Expand All @@ -166,7 +235,12 @@ public boolean containsOption(Option option){
*/
public static Configuration defaultConfiguration() {
Defaults defaults = getEffectiveDefaults();
return Configuration.builder().jsonProvider(defaults.jsonProvider()).options(defaults.options()).build();
return Configuration.builder()
.jsonProvider(defaults.jsonProvider())
.mappingProvider(defaults.mappingProvider())
.functionProvider(defaults.functionProvider())
.options(defaults.options())
.build();
}

/**
Expand All @@ -184,6 +258,8 @@ public static class ConfigurationBuilder {

private JsonProvider jsonProvider;
private MappingProvider mappingProvider;
private FunctionProvider functionProvider;
private final Map<String, Object> customFunctions = new LinkedHashMap<String, Object>();
private EnumSet<Option> options = EnumSet.noneOf(Option.class);
private Collection<EvaluationListener> evaluationListener = new ArrayList<EvaluationListener>();

Expand All @@ -197,6 +273,33 @@ public ConfigurationBuilder mappingProvider(MappingProvider provider) {
return this;
}

public ConfigurationBuilder functionProvider(FunctionProvider provider) {
this.functionProvider = provider;
return this;
}

public ConfigurationBuilder registerFunction(String name, Class<? extends PathFunction> functionClass) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Function name cannot be null or empty");
}
if (functionClass == null) {
throw new IllegalArgumentException("Function class cannot be null");
}
this.customFunctions.put(name, functionClass);
return this;
}

public ConfigurationBuilder registerFunction(String name, PathFunction function) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Function name cannot be null or empty");
}
if (function == null) {
throw new IllegalArgumentException("Function instance cannot be null");
}
this.customFunctions.put(name, function);
return this;
}

public ConfigurationBuilder options(Option... flags) {
if(flags.length > 0) {
this.options.addAll(asList(flags));
Expand Down Expand Up @@ -229,7 +332,31 @@ public Configuration build() {
mappingProvider = defaults.mappingProvider();
}
}
return new Configuration(jsonProvider, mappingProvider, options, evaluationListener);
if (functionProvider == null) {
final Defaults defaults = getEffectiveDefaults();
FunctionProvider defaultProvider = defaults.functionProvider();
if (!customFunctions.isEmpty()) {
DefaultFunctionProvider provider = new DefaultFunctionProvider(customFunctions);
functionProvider = provider;
} else {
functionProvider = defaultProvider != null ? defaultProvider : DefaultFunctionProvider.INSTANCE;
}
} else if (!customFunctions.isEmpty()) {
if (functionProvider instanceof DefaultFunctionProvider) {
DefaultFunctionProvider provider = new DefaultFunctionProvider(((DefaultFunctionProvider) functionProvider).getCustomFunctions());
for (Map.Entry<String, Object> entry : customFunctions.entrySet()) {
if (entry.getValue() instanceof Class) {
@SuppressWarnings("unchecked")
Class<? extends PathFunction> clazz = (Class<? extends PathFunction>) entry.getValue();
provider.register(entry.getKey(), clazz);
} else if (entry.getValue() instanceof PathFunction) {
provider.register(entry.getKey(), (PathFunction) entry.getValue());
}
}
functionProvider = provider;
}
}
return new Configuration(jsonProvider, mappingProvider, options, evaluationListener, functionProvider);
}
}

Expand All @@ -253,15 +380,25 @@ public interface Defaults {
*/
MappingProvider mappingProvider();

/**
* Returns the default {@link com.jayway.jsonpath.spi.function.FunctionProvider}
*
* @return default function provider
*/
default FunctionProvider functionProvider() {
return DefaultFunctionProvider.INSTANCE;
}

}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Configuration that = (Configuration) o;
return jsonProvider.getClass() == that.jsonProvider.getClass() &&
mappingProvider.getClass() == that.mappingProvider.getClass() &&
Objects.equals(options, that.options);
return Objects.equals(jsonProvider != null ? jsonProvider.getClass() : null, that.jsonProvider != null ? that.jsonProvider.getClass() : null) &&
Objects.equals(mappingProvider != null ? mappingProvider.getClass() : null, that.mappingProvider != null ? that.mappingProvider.getClass() : null) &&
Objects.equals(options, that.options) &&
Objects.equals(functionProvider, that.functionProvider);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.jayway.jsonpath.Configuration.Defaults;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.spi.function.DefaultFunctionProvider;
import com.jayway.jsonpath.spi.function.FunctionProvider;
import com.jayway.jsonpath.spi.json.JsonProvider;
import com.jayway.jsonpath.spi.json.JsonSmartJsonProvider;
import com.jayway.jsonpath.spi.mapper.JsonSmartMappingProvider;
Expand All @@ -15,6 +17,7 @@ public final class DefaultsImpl implements Defaults {
public static final DefaultsImpl INSTANCE = new DefaultsImpl();

private final MappingProvider mappingProvider = new JsonSmartMappingProvider();
private final FunctionProvider functionProvider = DefaultFunctionProvider.INSTANCE;

@Override
public JsonProvider jsonProvider() {
Expand All @@ -31,6 +34,11 @@ public MappingProvider mappingProvider() {
return mappingProvider;
}

@Override
public FunctionProvider functionProvider() {
return functionProvider;
}

private DefaultsImpl() {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,12 @@ private PathNode readPath() {
filter.setPosition(closingSquareBracketIndex + 1);
}
}
if (filter.inBounds() && filter.currentChar() == OPEN_PARENTHESIS) {
int closingParenIndex = filter.indexOfClosingBracket(filter.position(), true, false);
if (closingParenIndex != -1) {
filter.setPosition(closingParenIndex + 1);
}
}
boolean closingFunctionBracket = (filter.currentChar() == CLOSE_PARENTHESIS && currentCharIsClosingFunctionBracket(begin));
boolean closingLogicalBracket = (filter.currentChar() == CLOSE_PARENTHESIS && !closingFunctionBracket);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,12 @@ public String toString() {
public ValueNode evaluate(Predicate.PredicateContext ctx) {
if (isExistsCheck()) {
try {
Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build();
Configuration c = Configuration.builder()
.jsonProvider(ctx.configuration().jsonProvider())
.mappingProvider(ctx.configuration().mappingProvider())
.functionProvider(ctx.configuration().functionProvider())
.options(Option.REQUIRE_PROPERTIES)
.build();
Object result = path.evaluate(ctx.item(), ctx.root(), c).getValue(false);
return result == JsonProvider.UNDEFINED ? FALSE : TRUE;
} catch (PathNotFoundException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,11 @@
package com.jayway.jsonpath.internal.function;

import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.PathRef;

import java.util.List;

/**
* Defines the pattern by which a function can be executed over the result set in the particular path
* being grabbed. The Function's input is the content of the data from the json path selector and its output
* is defined via the functions behavior. Thus transformations in types can take place. Additionally, functions
* can accept multiple selectors in order to produce their output.
* being grabbed.
*
* Created by matt@mjgreenwood.net on 6/26/15.
* @deprecated Use {@link com.jayway.jsonpath.spi.function.PathFunction} instead.
*/
public interface PathFunction {

/**
* Invoke the function and output a JSON object (or scalar) value which will be the result of executing the path
*
* @param currentPath
* The current path location inclusive of the function name
* @param parent
* The path location above the current function
*
* @param model
* The JSON model as input to this particular function
*
* @param ctx
* Eval context, state bag used as the path is traversed, maintains the result of executing
*
* @param parameters
* @return result
*/
Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List<Parameter> parameters);
@Deprecated
public interface PathFunction extends com.jayway.jsonpath.spi.function.PathFunction {
}
Loading