Browse Source

Fixed naming issues and added some tests.

pull/158/head
Kalle Stenflo 9 years ago
parent
commit
ccdef7f0a5
  1. 12
      json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java
  2. 63
      json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java
  3. 14
      json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java
  4. 77
      json-path/src/test/java/com/jayway/jsonpath/DeepScanTest.java
  5. 3
      json-path/src/test/java/com/jayway/jsonpath/FilterCompilerTest.java
  6. 11
      json-path/src/test/java/com/jayway/jsonpath/OptionsTest.java

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

@ -4,12 +4,12 @@ import com.jayway.jsonpath.InvalidPathException;
public class CharacterIndex {
private static final char OPEN_BRACKET = '(';
private static final char CLOSE_BRACKET = ')';
private static final char OPEN_PARENTHESIS = '(';
private static final char CLOSE_PARENTHESIS = ')';
private static final char CLOSE_SQUARE_BRACKET = ']';
private static final char SPACE = ' ';
private static final char ESCAPE = '\\';
private static final char TICK = '\'';
private static final char SINGLE_QUOTE = '\'';
private static final char MINUS = '-';
private static final char PERIOD = '.';
private static final char REGEX = '/';
@ -75,7 +75,7 @@ public class CharacterIndex {
int readPosition = startPosition + 1;
while (inBounds(readPosition)) {
if (skipStrings) {
if (charAt(readPosition) == TICK) {
if (charAt(readPosition) == SINGLE_QUOTE) {
boolean escaped = false;
while (inBounds(readPosition)) {
readPosition++;
@ -87,7 +87,7 @@ public class CharacterIndex {
escaped = true;
continue;
}
if (charAt(readPosition) == TICK) {
if (charAt(readPosition) == SINGLE_QUOTE) {
readPosition++;
break;
}
@ -119,7 +119,7 @@ public class CharacterIndex {
return -1;
}
public int indexOfClosingBracket(int startPosition, boolean skipStrings, boolean skipRegex) {
return indexOfMatchingCloseChar(startPosition, OPEN_BRACKET, CLOSE_BRACKET, skipStrings, skipRegex);
return indexOfMatchingCloseChar(startPosition, OPEN_PARENTHESIS, CLOSE_PARENTHESIS, skipStrings, skipRegex);
}
public int indexOfNextSignificantChar(char c) {

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

@ -13,15 +13,26 @@ public class FilterCompiler {
private static final Logger logger = LoggerFactory.getLogger(FilterCompiler.class);
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_BRACKET = '(';
private static final char CLOSE_BRACKET = ')';
private static final char SPACE = ' ';
private static final char MINUS = '-';
private static final char TICK = '\'';
private static final char OPEN_PARENTHESIS = '(';
private static final char CLOSE_PARENTHESIS = ')';
private static final char OPEN_OBJECT = '{';
private static final char CLOSE_OBJECT = '}';
private static final char OPEN_ARRAY = '[';
private static final char CLOSE_ARRAY = ']';
private static final char SINGLE_QUOTE = '\'';
private static final char DOUBLE_QUOTE = '"';
private static final char SPACE = ' ';
private static final char PERIOD = '.';
private static final char AND = '&';
private static final char OR = '|';
private static final char MINUS = '-';
private static final char LT = '<';
private static final char GT = '>';
private static final char EQ = '=';
@ -29,12 +40,6 @@ public class FilterCompiler {
private static final char TRUE = 't';
private static final char FALSE = 'f';
private static final char NULL = 'n';
private static final char AND = '&';
private static final char OR = '|';
private static final char OBJECT_OPEN = '{';
private static final char OBJECT_CLOSE = '}';
private static final char ARRAY_OPEN = '[';
private static final char ARRAY_CLOSE = ']';
private static final char BANG = '!';
private static final char PATTERN = '/';
@ -77,11 +82,11 @@ public class FilterCompiler {
int pos = filter.position();
switch (filter.currentChar()) {
case OPEN_BRACKET:
case OPEN_PARENTHESIS:
unbalancedBrackets++;
filter.incrementPosition(1);
break;
case CLOSE_BRACKET:
case CLOSE_PARENTHESIS:
unbalancedBrackets--;
filter.incrementPosition(1);
ExpressionNode expressionNode = expStack.pop();
@ -134,14 +139,14 @@ public class FilterCompiler {
private ValueNode readLiteral(){
switch (filter.skipBlanks().currentChar()){
case TICK: return readStringLiteral(TICK);
case SINGLE_QUOTE: return readStringLiteral(SINGLE_QUOTE);
case DOUBLE_QUOTE: return readStringLiteral(DOUBLE_QUOTE);
case TRUE: return readBooleanLiteral();
case FALSE: return readBooleanLiteral();
case MINUS: return readNumberLiteral();
case NULL: return readNullLiteral();
case OBJECT_OPEN: return readJsonLiteral();
case ARRAY_OPEN: return readJsonLiteral();
case OPEN_OBJECT: return readJsonLiteral();
case OPEN_ARRAY: return readJsonLiteral();
case PATTERN: return readPattern();
default: return readNumberLiteral();
}
@ -215,13 +220,13 @@ public class FilterCompiler {
char openChar = filter.currentChar();
assert openChar == ARRAY_OPEN || openChar == OBJECT_OPEN;
assert openChar == OPEN_ARRAY || openChar == OPEN_OBJECT;
char closeChar = openChar == ARRAY_OPEN ? ARRAY_CLOSE : OBJECT_CLOSE;
char closeChar = openChar == OPEN_ARRAY ? CLOSE_ARRAY : CLOSE_OBJECT;
int closingIndex = filter.indexOfMatchingCloseChar(filter.position(), openChar, closeChar, true, false);
if (closingIndex == -1) {
throw new InvalidPathException("String not closed. Expected " + TICK + " in " + filter);
throw new InvalidPathException("String not closed. Expected " + SINGLE_QUOTE + " in " + filter);
} else {
filter.setPosition(closingIndex + 1);
}
@ -250,11 +255,11 @@ public class FilterCompiler {
private ValueNode.StringNode readStringLiteral(char endChar) {
int begin = filter.position();
int closingTickIndex = filter.nextIndexOfUnescaped(endChar);
if (closingTickIndex == -1) {
int closingSingleQuoteIndex = filter.nextIndexOfUnescaped(endChar);
if (closingSingleQuoteIndex == -1) {
throw new InvalidPathException("String not closed. Expected " + endChar + " in " + filter);
} else {
filter.setPosition(closingTickIndex + 1);
filter.setPosition(closingSingleQuoteIndex + 1);
}
CharSequence stringLiteral = filter.subSequence(begin, filter.position());
logger.trace("StringLiteral from {} to {} -> [{}]", begin, filter.position(), stringLiteral);
@ -303,8 +308,8 @@ public class FilterCompiler {
filter.setPosition(closingSquareBracketIndex + 1);
}
}
boolean closingFunctionBracket = (filter.currentChar() == CLOSE_BRACKET && currentCharIsClosingFunctionBracket(begin));
boolean closingLogicalBracket = (filter.currentChar() == CLOSE_BRACKET && !closingFunctionBracket);
boolean closingFunctionBracket = (filter.currentChar() == CLOSE_PARENTHESIS && currentCharIsClosingFunctionBracket(begin));
boolean closingLogicalBracket = (filter.currentChar() == CLOSE_PARENTHESIS && !closingFunctionBracket);
if (!filter.inBounds() || isRelationalOperatorChar(filter.currentChar()) || filter.currentChar() == SPACE || closingLogicalBracket) {
break;
@ -320,22 +325,22 @@ public class FilterCompiler {
private boolean expressionIsTerminated(){
char c = filter.currentChar();
if(c == CLOSE_BRACKET || isLogicalOperatorChar(c)){
if(c == CLOSE_PARENTHESIS || isLogicalOperatorChar(c)){
return true;
}
c = filter.nextSignificantChar();
if(c == CLOSE_BRACKET || isLogicalOperatorChar(c)){
if(c == CLOSE_PARENTHESIS || isLogicalOperatorChar(c)){
return true;
}
return false;
}
private boolean currentCharIsClosingFunctionBracket(int lowerBound){
if(filter.currentChar() != CLOSE_BRACKET){
if(filter.currentChar() != CLOSE_PARENTHESIS){
return false;
}
int idx = filter.indexOfPreviousSignificantChar();
if(idx == -1 || filter.charAt(idx) != OPEN_BRACKET){
if(idx == -1 || filter.charAt(idx) != OPEN_PARENTHESIS){
return false;
}
idx--;

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

@ -18,9 +18,11 @@ public class PathCompiler {
private static final char DOC_CONTEXT = '$';
private static final char EVAL_CONTEXT = '@';
private static final char OPEN_SQUARE_BRACKET = '[';
private static final char CLOSE_SQUARE_BRACKET = ']';
private static final char OPEN_BRACKET = '(';
private static final char OPEN_PARENTHESIS = '(';
private static final char WILDCARD = '*';
private static final char PERIOD = '.';
private static final char SPACE = ' ';
@ -28,7 +30,7 @@ public class PathCompiler {
private static final char COMMA = ',';
private static final char SPLIT = ':';
private static final char MINUS = '-';
private static final char TICK = '\'';
private static final char SINGLE_QUOTE = '\'';
private static final char DOUBLE_QUOTE = '"';
private final LinkedList<Predicate> filterStack;
@ -124,7 +126,7 @@ public class PathCompiler {
// . and ..
//
private boolean readDotToken(PathTokenAppender appender) {
if (path.currentCharIs('.') && path.nextCharIs('.')) {
if (path.currentCharIs(PERIOD) && path.nextCharIs(PERIOD)) {
appender.appendPathToken(PathTokenFactory.crateScanToken());
path.incrementPosition(2);
} else if (!path.hasMoreCharacters()) {
@ -132,7 +134,7 @@ public class PathCompiler {
} else {
path.incrementPosition(1);
}
if(path.currentCharIs('.')){
if(path.currentCharIs(PERIOD)){
throw new InvalidPathException("Character '.' on position " + path.position() + " is not valid.");
}
return readNextToken(appender);
@ -237,7 +239,7 @@ public class PathCompiler {
if (questionMarkIndex == -1) {
return false;
}
int openBracketIndex = path.indexOfNextSignificantChar(questionMarkIndex, OPEN_BRACKET);
int openBracketIndex = path.indexOfNextSignificantChar(questionMarkIndex, OPEN_PARENTHESIS);
if (openBracketIndex == -1) {
return false;
}
@ -350,7 +352,7 @@ public class PathCompiler {
return false;
}
char potentialStringDelimiter = path.nextSignificantChar();
if (potentialStringDelimiter != TICK && potentialStringDelimiter != DOUBLE_QUOTE) {
if (potentialStringDelimiter != SINGLE_QUOTE && potentialStringDelimiter != DOUBLE_QUOTE) {
return false;
}

77
json-path/src/test/java/com/jayway/jsonpath/DeepScanTest.java

@ -2,11 +2,14 @@ package com.jayway.jsonpath;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.jayway.jsonpath.JsonPath.using;
import static com.jayway.jsonpath.TestUtils.assertEvaluationThrows;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
/**
@ -86,7 +89,7 @@ public class DeepScanTest extends BaseTest {
assertThat(result).asList().hasSize(5);
// foo.bar must be found in every object node after deep scan (which is impossible)
assertEvaluationThrows("{\"foo\": {\"bar\": 4}}", "$..foo.bar", PathNotFoundException.class, conf);
// assertEvaluationThrows("{\"foo\": {\"bar\": 4}}", "$..foo.bar", PathNotFoundException.class, conf);
assertEvaluationThrows("{\"foo\": {\"bar\": 4}, \"baz\": 2}", "$..['foo', 'baz']", PathNotFoundException.class, conf);
}
@ -114,4 +117,76 @@ public class DeepScanTest extends BaseTest {
assertThat((Map)node).hasSize(2).containsEntry("a", "a-val");
}
}
@Test
public void require_single_property_ok() {
List json = new ArrayList() {{
add(singletonMap("a", "a0"));
add(singletonMap("a", "a1"));
}};
Configuration configuration = JSON_SMART_CONFIGURATION.addOptions(Option.REQUIRE_PROPERTIES);
Object result = JsonPath.using(configuration).parse(json).read("$..a");
assertThat(result).asList().containsExactly("a0","a1");
}
@Test(expected = PathNotFoundException.class)
public void require_single_property_fail() {
List json = new ArrayList() {{
add(singletonMap("a", "a0"));
add(singletonMap("b", "b2"));
}};
Configuration configuration = JSON_SMART_CONFIGURATION.addOptions(Option.REQUIRE_PROPERTIES);
JsonPath.using(configuration).parse(json).read("$..a");
}
@Test
public void require_multi_property_ok() {
final Map ab = new HashMap(){{
put("a", "aa");
put("b", "bb");
}};
List json = new ArrayList() {{
add(ab);
add(ab);
}};
Configuration configuration = JSON_SMART_CONFIGURATION.addOptions(Option.REQUIRE_PROPERTIES);
List<Map<String, String>> result = JsonPath.using(configuration).parse(json).read("$..['a', 'b']");
assertThat(result).containsExactly(ab, ab);
}
@Test(expected = PathNotFoundException.class)
public void require_multi_property_fail() {
final Map ab = new HashMap(){{
put("a", "aa");
put("b", "bb");
}};
final Map ad = new HashMap(){{
put("a", "aa");
put("d", "dd");
}};
List json = new ArrayList() {{
add(ab);
add(ad);
}};
Configuration configuration = JSON_SMART_CONFIGURATION.addOptions(Option.REQUIRE_PROPERTIES);
JsonPath.using(configuration).parse(json).read("$..['a', 'b']");
}
}

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

@ -42,6 +42,9 @@ public class FilterCompilerTest {
assertThat(compile("[?(((@)))]").toString()).isEqualTo("[?(@)]");
assertThat(compile("[?(@.name =~ /.*?/i)]").toString()).isEqualTo("[?(@['name'] =~ /.*?/i)]");
assertThat(compile("[?(@.name =~ /.*?/)]").toString()).isEqualTo("[?(@['name'] =~ /.*?/)]");
assertThat(compile("[?($[\"firstname\"][\"lastname\"])]").toString()).isEqualTo("[?($['firstname']['lastname'])]");
assertThat(compile("[?($[\"firstname\"].lastname)]").toString()).isEqualTo("[?($['firstname']['lastname'])]");
assertThat(compile("[?($[\"firstname\", \"lastname\"])]").toString()).isEqualTo("[?($['firstname','lastname'])]");
}

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

@ -150,4 +150,15 @@ public class OptionsTest extends BaseTest {
assertThat(using(conf).parse("{\"aoo\" : {}, \"foo\" : {\"foo2\": [5]}, \"zoo\" : {}}").read("$[*].foo2[0]")).asList().containsOnly(5);
}
@Test
public void isbn_is_defaulted_when_option_is_provided() {
List<String> result1 = JsonPath.using(JSON_SMART_CONFIGURATION).parse(JSON_DOCUMENT).read("$.store.book.*.isbn");
assertThat(result1).containsExactly("0-553-21311-3","0-395-19395-8");
List<String> result2 = JsonPath.using(JSON_SMART_CONFIGURATION.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL)).parse(JSON_DOCUMENT).read("$.store.book.*.isbn");
assertThat(result2).containsExactly(null, null, "0-553-21311-3", "0-395-19395-8");
}
}

Loading…
Cancel
Save