Browse Source

Removed % prefix from path functions.

pull/183/merge
Kalle Stenflo 9 years ago
parent
commit
33f365ef20
  1. 14
      README.md
  2. 5
      json-path/src/main/java/com/jayway/jsonpath/JsonPath.java
  3. 45
      json-path/src/main/java/com/jayway/jsonpath/internal/PathCompiler.java
  4. 7
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java
  5. 12
      json-path/src/main/java/com/jayway/jsonpath/internal/token/FunctionPathToken.java
  6. 6
      json-path/src/test/java/com/jayway/jsonpath/FilterCompilerTest.java
  7. 9
      json-path/src/test/java/com/jayway/jsonpath/FilterTest.java
  8. 10
      json-path/src/test/java/com/jayway/jsonpath/functions/JSONEntityFunctionTest.java
  9. 12
      json-path/src/test/java/com/jayway/jsonpath/functions/NumericFunctionTest.java

14
README.md

@ -62,7 +62,7 @@ Operators
| `[?(<expression>)]` | Filter expression. Expression must evaluate to a boolean value. | | `[?(<expression>)]` | Filter expression. Expression must evaluate to a boolean value. |
<!--- <!---
Functions (not released yet) Functions
--------- ---------
Functions can be invoked at the tail end of a path - the input to a function is the output of the path expression. Functions can be invoked at the tail end of a path - the input to a function is the output of the path expression.
@ -70,11 +70,11 @@ The function output is dictated by the function itself.
| Function | Description | Output | | Function | Description | Output |
| :------------------------ | :----------------------------------------------------------------- |-----------| | :------------------------ | :----------------------------------------------------------------- |-----------|
| %min() | Provides the min value of an array of numbers | Double | | min() | Provides the min value of an array of numbers | Double |
| %max() | Provides the max value of an array of numbers | Double | | max() | Provides the max value of an array of numbers | Double |
| %avg() | Provides the average value of an array of numbers | Double | | avg() | Provides the average value of an array of numbers | Double |
| %stddev() | Provides the standard deviation value of an array of numbers | Double | | stddev() | Provides the standard deviation value of an array of numbers | Double |
| %length() | Provides the length of an array | Integer | | length() | Provides the length of an array | Integer |
--> -->
Path Examples Path Examples
@ -141,7 +141,7 @@ Given the json
| <a href="http://jsonpath.herokuapp.com/?path=$..book[?(@.author =~ /.*REES/i)]" target="_blank">$..book[?(@.author =~ /.*REES/i)]</a> | All books matching regex (ignore case) | | <a href="http://jsonpath.herokuapp.com/?path=$..book[?(@.author =~ /.*REES/i)]" target="_blank">$..book[?(@.author =~ /.*REES/i)]</a> | All books matching regex (ignore case) |
| <a href="http://jsonpath.herokuapp.com/?path=$..*" target="_blank">$..*</a> | Give me every thing | <a href="http://jsonpath.herokuapp.com/?path=$..*" target="_blank">$..*</a> | Give me every thing
<!--- <!---
| <a href="http://jsonpath.herokuapp.com/?path=$..book.%length()" target="_blank">$..book.%length()</a> | The number of books | | <a href="http://jsonpath.herokuapp.com/?path=$..book.length()" target="_blank">$..book.%length()</a> | The number of books |
--> -->
Reading a Document Reading a Document
------------------ ------------------

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

@ -179,9 +179,10 @@ public class JsonPath {
throw new JsonPathException("Options " + AS_PATH_LIST + " and " + ALWAYS_RETURN_LIST + " are not allowed when using path functions!"); throw new JsonPathException("Options " + AS_PATH_LIST + " and " + ALWAYS_RETURN_LIST + " are not allowed when using path functions!");
} }
return path.evaluate(jsonObject, jsonObject, configuration).getValue(true); return path.evaluate(jsonObject, jsonObject, configuration).getValue(true);
}
if(optAsPathList){ } else if(optAsPathList){
return (T)path.evaluate(jsonObject, jsonObject, configuration).getPath(); return (T)path.evaluate(jsonObject, jsonObject, configuration).getPath();
} else { } else {
Object res = path.evaluate(jsonObject, jsonObject, configuration).getValue(false); Object res = path.evaluate(jsonObject, jsonObject, configuration).getValue(false);
if(optAlwaysReturnList && path.isDefinite()){ if(optAlwaysReturnList && path.isDefinite()){

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

@ -24,7 +24,6 @@ public class PathCompiler {
private static final char OPEN_SQUARE_BRACKET = '['; private static final char OPEN_SQUARE_BRACKET = '[';
private static final char CLOSE_SQUARE_BRACKET = ']'; private static final char CLOSE_SQUARE_BRACKET = ']';
private static final char OPEN_BRACKET = '('; private static final char OPEN_BRACKET = '(';
private static final char CLOSE_BRACKET = ')';
private static final char WILDCARD = '*'; private static final char WILDCARD = '*';
private static final char PERIOD = '.'; private static final char PERIOD = '.';
private static final char SPACE = ' '; private static final char SPACE = ' ';
@ -33,7 +32,6 @@ public class PathCompiler {
private static final char SPLIT = ':'; private static final char SPLIT = ':';
private static final char MINUS = '-'; private static final char MINUS = '-';
private static final char TICK = '\''; private static final char TICK = '\'';
private static final char FUNCTION = '%';
private final LinkedList<Predicate> filterStack; private final LinkedList<Predicate> filterStack;
private final CharacterIndex path; private final CharacterIndex path;
@ -118,42 +116,12 @@ public class PathCompiler {
case WILDCARD: case WILDCARD:
return readWildCardToken(appender) || return readWildCardToken(appender) ||
fail("Could not parse token starting at position " + path.position()); fail("Could not parse token starting at position " + path.position());
case FUNCTION:
return readFunctionToken(appender) ||
fail("Could not parse token starting at position " + path.position());
default: default:
return readPropertyToken(appender) || return readPropertyOrFunctionToken(appender) ||
fail("Could not parse token starting at position " + path.position()); fail("Could not parse token starting at position " + path.position());
} }
} }
//
// $function()
//
private boolean readFunctionToken(PathTokenAppender appender) {
if (path.currentCharIs(OPEN_SQUARE_BRACKET) || path.currentCharIs(WILDCARD) || path.currentCharIs(PERIOD) || path.currentCharIs(SPACE)) {
return false;
}
int startPosition = path.position();
int readPosition = startPosition;
int endPosition = 0;
while (path.inBounds(readPosition)) {
char c = path.charAt(readPosition);
if (c == OPEN_BRACKET && path.nextSignificantCharIs(readPosition, CLOSE_BRACKET)) {
endPosition = path.indexOfNextSignificantChar(readPosition, CLOSE_BRACKET);
break;
}
readPosition++;
}
path.setPosition(endPosition);
String function = path.subSequence(startPosition, endPosition + 1).toString();
appender.appendPathToken(PathTokenFactory.createFunctionPathToken(function));
return path.currentIsTail();
}
// //
// . and .. // . and ..
// //
@ -173,9 +141,9 @@ public class PathCompiler {
} }
// //
// fooBar // fooBar or fooBar()
// //
private boolean readPropertyToken(PathTokenAppender appender) { private boolean readPropertyOrFunctionToken(PathTokenAppender appender) {
if (path.currentCharIs(OPEN_SQUARE_BRACKET) || path.currentCharIs(WILDCARD) || path.currentCharIs(PERIOD) || path.currentCharIs(SPACE)) { if (path.currentCharIs(OPEN_SQUARE_BRACKET) || path.currentCharIs(WILDCARD) || path.currentCharIs(PERIOD) || path.currentCharIs(SPACE)) {
return false; return false;
} }
@ -201,8 +169,11 @@ public class PathCompiler {
path.setPosition(endPosition); path.setPosition(endPosition);
String property = path.subSequence(startPosition, endPosition).toString(); String property = path.subSequence(startPosition, endPosition).toString();
if(property.endsWith("()")){
appender.appendPathToken(PathTokenFactory.createSinglePropertyPathToken(property)); appender.appendPathToken(PathTokenFactory.createFunctionPathToken(property));
} else {
appender.appendPathToken(PathTokenFactory.createSinglePropertyPathToken(property));
}
return path.currentIsTail() || readNextToken(appender); return path.currentIsTail() || readNextToken(appender);
} }

7
json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java

@ -13,14 +13,14 @@ public class FilterCompiler {
private static final Logger logger = LoggerFactory.getLogger(FilterCompiler.class); private static final Logger logger = LoggerFactory.getLogger(FilterCompiler.class);
private static final char DOC_CONTEXT = '$'; private static final char DOC_CONTEXT = '$';
private static final char EVAL_CONTEXT = '@'; private static final char EVAL_CONTEXT = '@'; /**/
private static final char OPEN_SQUARE_BRACKET = '['; private static final char OPEN_SQUARE_BRACKET = '[';
private static final char OPEN_BRACKET = '('; private static final char OPEN_BRACKET = '(';
private static final char CLOSE_BRACKET = ')'; private static final char CLOSE_BRACKET = ')';
private static final char SPACE = ' '; private static final char SPACE = ' ';
private static final char MINUS = '-'; private static final char MINUS = '-';
private static final char TICK = '\''; private static final char TICK = '\'';
private static final char FUNCTION = '%'; private static final char PERIOD = '.';
private static final char LT = '<'; private static final char LT = '<';
private static final char GT = '>'; private static final char GT = '>';
private static final char EQ = '='; private static final char EQ = '=';
@ -338,7 +338,7 @@ public class FilterCompiler {
} }
idx--; idx--;
while(filter.inBounds(idx) && idx > lowerBound){ while(filter.inBounds(idx) && idx > lowerBound){
if(filter.charAt(idx) == FUNCTION){ if(filter.charAt(idx) == PERIOD){
return true; return true;
} }
idx--; idx--;
@ -349,6 +349,7 @@ public class FilterCompiler {
private boolean isLogicalOperatorChar(char c) { private boolean isLogicalOperatorChar(char c) {
return c == AND || c == OR; return c == AND || c == OR;
} }
private boolean isRelationalOperatorChar(char c) { private boolean isRelationalOperatorChar(char c) {
return c == LT || c == GT || c == EQ || c == TILDE || c == BANG; return c == LT || c == GT || c == EQ || c == TILDE || c == BANG;
} }

12
json-path/src/main/java/com/jayway/jsonpath/internal/token/FunctionPathToken.java

@ -4,9 +4,6 @@ import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.function.Function; import com.jayway.jsonpath.internal.function.Function;
import com.jayway.jsonpath.internal.function.FunctionFactory; import com.jayway.jsonpath.internal.function.FunctionFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* Token representing a Function call to one of the functions produced via the FunctionFactory * Token representing a Function call to one of the functions produced via the FunctionFactory
* *
@ -21,12 +18,9 @@ public class FunctionPathToken extends PathToken {
public FunctionPathToken(String pathFragment) { public FunctionPathToken(String pathFragment) {
this.pathFragment = pathFragment; this.pathFragment = pathFragment;
Matcher matcher = Pattern.compile(".*?\\%(\\w+)\\(.*?").matcher(pathFragment); if(pathFragment.endsWith("()")){
if (matcher.matches()) { functionName = pathFragment.substring(0, pathFragment.length()-2);
functionName = matcher.group(1); } else {
}
else {
// We'll end up throwing an error from the factory when we get that far
functionName = null; functionName = null;
} }
} }

6
json-path/src/test/java/com/jayway/jsonpath/FilterCompilerTest.java

@ -36,9 +36,9 @@ public class FilterCompilerTest {
assertThat(compile("[?(@.a IN {'foo':'bar'})]").toString()).isEqualTo("[?(@['a'] IN {'foo':'bar'})]"); assertThat(compile("[?(@.a IN {'foo':'bar'})]").toString()).isEqualTo("[?(@['a'] IN {'foo':'bar'})]");
assertThat(compile("[?(@.value<'7')]").toString()).isEqualTo("[?(@['value'] < '7')]"); assertThat(compile("[?(@.value<'7')]").toString()).isEqualTo("[?(@['value'] < '7')]");
assertThat(compile("[?(@.message == 'it\\\\')]").toString()).isEqualTo("[?(@['message'] == 'it\\\\')]"); assertThat(compile("[?(@.message == 'it\\\\')]").toString()).isEqualTo("[?(@['message'] == 'it\\\\')]");
assertThat(compile("[?(@.message.%min() > 10)]").toString()).isEqualTo("[?(@['message'].%min() > 10)]"); assertThat(compile("[?(@.message.min() > 10)]").toString()).isEqualTo("[?(@['message'].min() > 10)]");
assertThat(compile("[?(@.message.%min()==10)]").toString()).isEqualTo("[?(@['message'].%min() == 10)]"); assertThat(compile("[?(@.message.min()==10)]").toString()).isEqualTo("[?(@['message'].min() == 10)]");
assertThat(compile("[?(10 == @.message.%min())]").toString()).isEqualTo("[?(10 == @['message'].%min())]"); assertThat(compile("[?(10 == @.message.min())]").toString()).isEqualTo("[?(10 == @['message'].min())]");
assertThat(compile("[?(((@)))]").toString()).isEqualTo("[?(@)]"); assertThat(compile("[?(((@)))]").toString()).isEqualTo("[?(@)]");
assertThat(compile("[?(@.name =~ /.*?/i)]").toString()).isEqualTo("[?(@['name'] =~ /.*?/i)]"); assertThat(compile("[?(@.name =~ /.*?/i)]").toString()).isEqualTo("[?(@['name'] =~ /.*?/i)]");
assertThat(compile("[?(@.name =~ /.*?/)]").toString()).isEqualTo("[?(@['name'] =~ /.*?/)]"); assertThat(compile("[?(@.name =~ /.*?/)]").toString()).isEqualTo("[?(@['name'] =~ /.*?/)]");

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

@ -482,11 +482,8 @@ public class FilterTest extends BaseTest {
@Test @Test
public void inline_in_criteria_evalueates() { public void inline_in_criteria_evaluates() {
List list = JsonPath.read(JSON_DOCUMENT, "$.store.book[?(@.category in ['reference', 'fiction'])]");
Object read = JsonPath.read(JSON_DOCUMENT, "$.store.book[?(@.category in ['reference', 'fiction'])]"); assertThat(list).hasSize(4);
System.out.println(read);
} }
} }

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

@ -54,18 +54,18 @@ public class JSONEntityFunctionTest extends BaseFunctionTest {
@Test @Test
public void testLengthOfTextArray() { public void testLengthOfTextArray() {
// The length of JSONArray is an integer // The length of JSONArray is an integer
verifyFunction(conf, "$['text'].%length()", TEXT_SERIES, 6); verifyFunction(conf, "$['text'].length()", TEXT_SERIES, 6);
} }
@Test @Test
public void testLengthOfNumberArray() { public void testLengthOfNumberArray() {
// The length of JSONArray is an integer // The length of JSONArray is an integer
verifyFunction(conf, "$.numbers.%length()", NUMBER_SERIES, 10); verifyFunction(conf, "$.numbers.length()", NUMBER_SERIES, 10);
} }
@Test @Test
public void testLengthOfStructure() { public void testLengthOfStructure() {
verifyFunction(conf, "$.batches.%length()", BATCH_JSON, 2); verifyFunction(conf, "$.batches.length()", BATCH_JSON, 2);
} }
/** /**
@ -80,7 +80,7 @@ public class JSONEntityFunctionTest extends BaseFunctionTest {
*/ */
@Test @Test
public void testPredicateWithFunctionCallSingleMatch() { public void testPredicateWithFunctionCallSingleMatch() {
String path = "$.batches.results[?(@.values.%length() >= $.batches.minBatchSize)].values.%avg()"; String path = "$.batches.results[?(@.values.length() >= $.batches.minBatchSize)].values.avg()";
// Its an array because in some use-cases the min size might match more than one batch and thus we'll get // Its an array because in some use-cases the min size might match more than one batch and thus we'll get
// the average out for each collection // the average out for each collection
@ -91,7 +91,7 @@ public class JSONEntityFunctionTest extends BaseFunctionTest {
@Test @Test
public void testPredicateWithFunctionCallTwoMatches() { public void testPredicateWithFunctionCallTwoMatches() {
String path = "$.batches.results[?(@.values.%length() >= 3)].values.%avg()"; String path = "$.batches.results[?(@.values.length() >= 3)].values.avg()";
// Its an array because in some use-cases the min size might match more than one batch and thus we'll get // Its an array because in some use-cases the min size might match more than one batch and thus we'll get
// the average out for each collection // the average out for each collection

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

@ -42,27 +42,27 @@ public class NumericFunctionTest extends BaseFunctionTest {
@Test @Test
public void testAverageOfDoubles() { public void testAverageOfDoubles() {
verifyMathFunction(conf, "$.numbers.%avg()", 5.5); verifyMathFunction(conf, "$.numbers.avg()", 5.5);
} }
@Test @Test
public void testSumOfDouble() { public void testSumOfDouble() {
verifyMathFunction(conf, "$.numbers.%sum()", (10d * (10d + 1d)) / 2d); verifyMathFunction(conf, "$.numbers.sum()", (10d * (10d + 1d)) / 2d);
} }
@Test @Test
public void testMaxOfDouble() { public void testMaxOfDouble() {
verifyMathFunction(conf, "$.numbers.%max()", 10d); verifyMathFunction(conf, "$.numbers.max()", 10d);
} }
@Test @Test
public void testMinOfDouble() { public void testMinOfDouble() {
verifyMathFunction(conf, "$.numbers.%min()", 1d); verifyMathFunction(conf, "$.numbers.min()", 1d);
} }
@Test @Test
public void testStdDevOfDouble() { public void testStdDevOfDouble() {
verifyMathFunction(conf, "$.numbers.%stddev()", 2.8722813232690143d); verifyMathFunction(conf, "$.numbers.stddev()", 2.8722813232690143d);
} }
/** /**
@ -73,7 +73,7 @@ public class NumericFunctionTest extends BaseFunctionTest {
// public void testInvalidFunctionNameNegative() { // public void testInvalidFunctionNameNegative() {
// JSONArray numberSeries = new JSONArray(); // JSONArray numberSeries = new JSONArray();
// numberSeries.addAll(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); // numberSeries.addAll(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
// assertThat(using(conf).parse(NUMBER_SERIES).read("$.numbers.%foo()")).isEqualTo(numberSeries); // assertThat(using(conf).parse(NUMBER_SERIES).read("$.numbers.foo()")).isEqualTo(numberSeries);
// } // }
} }

Loading…
Cancel
Save