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 { public class CharacterIndex {
private static final char OPEN_BRACKET = '('; private static final char OPEN_PARENTHESIS = '(';
private static final char CLOSE_BRACKET = ')'; private static final char CLOSE_PARENTHESIS = ')';
private static final char CLOSE_SQUARE_BRACKET = ']'; private static final char CLOSE_SQUARE_BRACKET = ']';
private static final char SPACE = ' '; private static final char SPACE = ' ';
private static final char ESCAPE = '\\'; 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 MINUS = '-';
private static final char PERIOD = '.'; private static final char PERIOD = '.';
private static final char REGEX = '/'; private static final char REGEX = '/';
@ -75,7 +75,7 @@ public class CharacterIndex {
int readPosition = startPosition + 1; int readPosition = startPosition + 1;
while (inBounds(readPosition)) { while (inBounds(readPosition)) {
if (skipStrings) { if (skipStrings) {
if (charAt(readPosition) == TICK) { if (charAt(readPosition) == SINGLE_QUOTE) {
boolean escaped = false; boolean escaped = false;
while (inBounds(readPosition)) { while (inBounds(readPosition)) {
readPosition++; readPosition++;
@ -87,7 +87,7 @@ public class CharacterIndex {
escaped = true; escaped = true;
continue; continue;
} }
if (charAt(readPosition) == TICK) { if (charAt(readPosition) == SINGLE_QUOTE) {
readPosition++; readPosition++;
break; break;
} }
@ -119,7 +119,7 @@ public class CharacterIndex {
return -1; return -1;
} }
public int indexOfClosingBracket(int startPosition, boolean skipStrings, boolean skipRegex) { 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) { 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 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_PARENTHESIS = '(';
private static final char CLOSE_BRACKET = ')'; private static final char CLOSE_PARENTHESIS = ')';
private static final char SPACE = ' '; private static final char OPEN_OBJECT = '{';
private static final char MINUS = '-'; private static final char CLOSE_OBJECT = '}';
private static final char TICK = '\''; 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 DOUBLE_QUOTE = '"';
private static final char SPACE = ' ';
private static final char PERIOD = '.'; 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 LT = '<';
private static final char GT = '>'; private static final char GT = '>';
private static final char EQ = '='; private static final char EQ = '=';
@ -29,12 +40,6 @@ public class FilterCompiler {
private static final char TRUE = 't'; private static final char TRUE = 't';
private static final char FALSE = 'f'; private static final char FALSE = 'f';
private static final char NULL = 'n'; 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 BANG = '!';
private static final char PATTERN = '/'; private static final char PATTERN = '/';
@ -77,11 +82,11 @@ public class FilterCompiler {
int pos = filter.position(); int pos = filter.position();
switch (filter.currentChar()) { switch (filter.currentChar()) {
case OPEN_BRACKET: case OPEN_PARENTHESIS:
unbalancedBrackets++; unbalancedBrackets++;
filter.incrementPosition(1); filter.incrementPosition(1);
break; break;
case CLOSE_BRACKET: case CLOSE_PARENTHESIS:
unbalancedBrackets--; unbalancedBrackets--;
filter.incrementPosition(1); filter.incrementPosition(1);
ExpressionNode expressionNode = expStack.pop(); ExpressionNode expressionNode = expStack.pop();
@ -134,14 +139,14 @@ public class FilterCompiler {
private ValueNode readLiteral(){ private ValueNode readLiteral(){
switch (filter.skipBlanks().currentChar()){ switch (filter.skipBlanks().currentChar()){
case TICK: return readStringLiteral(TICK); case SINGLE_QUOTE: return readStringLiteral(SINGLE_QUOTE);
case DOUBLE_QUOTE: return readStringLiteral(DOUBLE_QUOTE); case DOUBLE_QUOTE: return readStringLiteral(DOUBLE_QUOTE);
case TRUE: return readBooleanLiteral(); case TRUE: return readBooleanLiteral();
case FALSE: return readBooleanLiteral(); case FALSE: return readBooleanLiteral();
case MINUS: return readNumberLiteral(); case MINUS: return readNumberLiteral();
case NULL: return readNullLiteral(); case NULL: return readNullLiteral();
case OBJECT_OPEN: return readJsonLiteral(); case OPEN_OBJECT: return readJsonLiteral();
case ARRAY_OPEN: return readJsonLiteral(); case OPEN_ARRAY: return readJsonLiteral();
case PATTERN: return readPattern(); case PATTERN: return readPattern();
default: return readNumberLiteral(); default: return readNumberLiteral();
} }
@ -215,13 +220,13 @@ public class FilterCompiler {
char openChar = filter.currentChar(); 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); int closingIndex = filter.indexOfMatchingCloseChar(filter.position(), openChar, closeChar, true, false);
if (closingIndex == -1) { 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 { } else {
filter.setPosition(closingIndex + 1); filter.setPosition(closingIndex + 1);
} }
@ -250,11 +255,11 @@ public class FilterCompiler {
private ValueNode.StringNode readStringLiteral(char endChar) { private ValueNode.StringNode readStringLiteral(char endChar) {
int begin = filter.position(); int begin = filter.position();
int closingTickIndex = filter.nextIndexOfUnescaped(endChar); int closingSingleQuoteIndex = filter.nextIndexOfUnescaped(endChar);
if (closingTickIndex == -1) { if (closingSingleQuoteIndex == -1) {
throw new InvalidPathException("String not closed. Expected " + endChar + " in " + filter); throw new InvalidPathException("String not closed. Expected " + endChar + " in " + filter);
} else { } else {
filter.setPosition(closingTickIndex + 1); filter.setPosition(closingSingleQuoteIndex + 1);
} }
CharSequence stringLiteral = filter.subSequence(begin, filter.position()); CharSequence stringLiteral = filter.subSequence(begin, filter.position());
logger.trace("StringLiteral from {} to {} -> [{}]", begin, filter.position(), stringLiteral); logger.trace("StringLiteral from {} to {} -> [{}]", begin, filter.position(), stringLiteral);
@ -303,8 +308,8 @@ public class FilterCompiler {
filter.setPosition(closingSquareBracketIndex + 1); filter.setPosition(closingSquareBracketIndex + 1);
} }
} }
boolean closingFunctionBracket = (filter.currentChar() == CLOSE_BRACKET && currentCharIsClosingFunctionBracket(begin)); boolean closingFunctionBracket = (filter.currentChar() == CLOSE_PARENTHESIS && currentCharIsClosingFunctionBracket(begin));
boolean closingLogicalBracket = (filter.currentChar() == CLOSE_BRACKET && !closingFunctionBracket); boolean closingLogicalBracket = (filter.currentChar() == CLOSE_PARENTHESIS && !closingFunctionBracket);
if (!filter.inBounds() || isRelationalOperatorChar(filter.currentChar()) || filter.currentChar() == SPACE || closingLogicalBracket) { if (!filter.inBounds() || isRelationalOperatorChar(filter.currentChar()) || filter.currentChar() == SPACE || closingLogicalBracket) {
break; break;
@ -320,22 +325,22 @@ public class FilterCompiler {
private boolean expressionIsTerminated(){ private boolean expressionIsTerminated(){
char c = filter.currentChar(); char c = filter.currentChar();
if(c == CLOSE_BRACKET || isLogicalOperatorChar(c)){ if(c == CLOSE_PARENTHESIS || isLogicalOperatorChar(c)){
return true; return true;
} }
c = filter.nextSignificantChar(); c = filter.nextSignificantChar();
if(c == CLOSE_BRACKET || isLogicalOperatorChar(c)){ if(c == CLOSE_PARENTHESIS || isLogicalOperatorChar(c)){
return true; return true;
} }
return false; return false;
} }
private boolean currentCharIsClosingFunctionBracket(int lowerBound){ private boolean currentCharIsClosingFunctionBracket(int lowerBound){
if(filter.currentChar() != CLOSE_BRACKET){ if(filter.currentChar() != CLOSE_PARENTHESIS){
return false; return false;
} }
int idx = filter.indexOfPreviousSignificantChar(); int idx = filter.indexOfPreviousSignificantChar();
if(idx == -1 || filter.charAt(idx) != OPEN_BRACKET){ if(idx == -1 || filter.charAt(idx) != OPEN_PARENTHESIS){
return false; return false;
} }
idx--; 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 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 CLOSE_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 WILDCARD = '*';
private static final char PERIOD = '.'; private static final char PERIOD = '.';
private static final char SPACE = ' '; private static final char SPACE = ' ';
@ -28,7 +30,7 @@ public class PathCompiler {
private static final char COMMA = ','; private static final char COMMA = ',';
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 SINGLE_QUOTE = '\'';
private static final char DOUBLE_QUOTE = '"'; private static final char DOUBLE_QUOTE = '"';
private final LinkedList<Predicate> filterStack; private final LinkedList<Predicate> filterStack;
@ -124,7 +126,7 @@ public class PathCompiler {
// . and .. // . and ..
// //
private boolean readDotToken(PathTokenAppender appender) { private boolean readDotToken(PathTokenAppender appender) {
if (path.currentCharIs('.') && path.nextCharIs('.')) { if (path.currentCharIs(PERIOD) && path.nextCharIs(PERIOD)) {
appender.appendPathToken(PathTokenFactory.crateScanToken()); appender.appendPathToken(PathTokenFactory.crateScanToken());
path.incrementPosition(2); path.incrementPosition(2);
} else if (!path.hasMoreCharacters()) { } else if (!path.hasMoreCharacters()) {
@ -132,7 +134,7 @@ public class PathCompiler {
} else { } else {
path.incrementPosition(1); path.incrementPosition(1);
} }
if(path.currentCharIs('.')){ if(path.currentCharIs(PERIOD)){
throw new InvalidPathException("Character '.' on position " + path.position() + " is not valid."); throw new InvalidPathException("Character '.' on position " + path.position() + " is not valid.");
} }
return readNextToken(appender); return readNextToken(appender);
@ -237,7 +239,7 @@ public class PathCompiler {
if (questionMarkIndex == -1) { if (questionMarkIndex == -1) {
return false; return false;
} }
int openBracketIndex = path.indexOfNextSignificantChar(questionMarkIndex, OPEN_BRACKET); int openBracketIndex = path.indexOfNextSignificantChar(questionMarkIndex, OPEN_PARENTHESIS);
if (openBracketIndex == -1) { if (openBracketIndex == -1) {
return false; return false;
} }
@ -350,7 +352,7 @@ public class PathCompiler {
return false; return false;
} }
char potentialStringDelimiter = path.nextSignificantChar(); char potentialStringDelimiter = path.nextSignificantChar();
if (potentialStringDelimiter != TICK && potentialStringDelimiter != DOUBLE_QUOTE) { if (potentialStringDelimiter != SINGLE_QUOTE && potentialStringDelimiter != DOUBLE_QUOTE) {
return false; 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 org.junit.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static com.jayway.jsonpath.JsonPath.using; import static com.jayway.jsonpath.JsonPath.using;
import static com.jayway.jsonpath.TestUtils.assertEvaluationThrows; import static com.jayway.jsonpath.TestUtils.assertEvaluationThrows;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
@ -86,7 +89,7 @@ public class DeepScanTest extends BaseTest {
assertThat(result).asList().hasSize(5); assertThat(result).asList().hasSize(5);
// foo.bar must be found in every object node after deep scan (which is impossible) // 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); 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"); 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("[?(((@)))]").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'] =~ /.*?/)]");
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); 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