From ea206498cca9045eadb6d6027d7c48bba30a222a Mon Sep 17 00:00:00 2001 From: Kalle Stenflo Date: Wed, 25 Nov 2015 14:54:29 +0100 Subject: [PATCH] Bracket properties can contain path chars #154 --- .../jsonpath/internal/CharacterIndex.java | 18 ++++++++++++++++++ .../internal/filter/FilterCompiler.java | 3 ++- .../jsonpath/internal/path/PathCompiler.java | 4 ++-- .../internal/path/PathTokenFactory.java | 8 ++++---- .../internal/path/PropertyPathToken.java | 6 ++++-- .../jayway/jsonpath/FilterCompilerTest.java | 14 ++++++++++---- .../com/jayway/jsonpath/PathCompilerTest.java | 2 +- .../jsonpath/internal/path/PathTokenTest.java | 2 +- 8 files changed, 42 insertions(+), 15 deletions(-) diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java b/json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java index 83d3d9bf..f296ad91 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java @@ -10,6 +10,7 @@ public class CharacterIndex { private static final char SPACE = ' '; private static final char ESCAPE = '\\'; private static final char SINGLE_QUOTE = '\''; + private static final char DOUBLE_QUOTE = '"'; private static final char MINUS = '-'; private static final char PERIOD = '.'; private static final char REGEX = '/'; @@ -92,6 +93,23 @@ public class CharacterIndex { break; } } + } else if (charAt(readPosition) == DOUBLE_QUOTE) { + boolean escaped = false; + while (inBounds(readPosition)) { + readPosition++; + if (escaped) { + escaped = false; + continue; + } + if (charAt(readPosition) == ESCAPE) { + escaped = true; + continue; + } + if (charAt(readPosition) == DOUBLE_QUOTE) { + readPosition++; + break; + } + } } } if (skipRegex) { diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java index e1b33988..43bedddf 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java @@ -16,6 +16,7 @@ public class FilterCompiler { 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_PARENTHESIS = '('; private static final char CLOSE_PARENTHESIS = ')'; private static final char OPEN_OBJECT = '{'; @@ -301,7 +302,7 @@ public class FilterCompiler { filter.incrementPosition(1); //skip $ and @ while (filter.inBounds()) { if (filter.currentChar() == OPEN_SQUARE_BRACKET) { - int closingSquareBracketIndex = filter.indexOfClosingSquareBracket(filter.position()); + int closingSquareBracketIndex = filter.indexOfMatchingCloseChar(filter.position(), OPEN_SQUARE_BRACKET, CLOSE_SQUARE_BRACKET, true, false); if (closingSquareBracketIndex == -1) { throw new InvalidPathException("Square brackets does not match in filter " + filter); } else { diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java index b1760589..52c4a32a 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java @@ -172,7 +172,7 @@ public class PathCompiler { if(property.endsWith("()")){ appender.appendPathToken(PathTokenFactory.createFunctionPathToken(property)); } else { - appender.appendPathToken(PathTokenFactory.createSinglePropertyPathToken(property)); + appender.appendPathToken(PathTokenFactory.createSinglePropertyPathToken(property, SINGLE_QUOTE)); } return path.currentIsTail() || readNextToken(appender); @@ -385,7 +385,7 @@ public class PathCompiler { path.setPosition(endBracketIndex); - appender.appendPathToken(PathTokenFactory.createPropertyPathToken(properties)); + appender.appendPathToken(PathTokenFactory.createPropertyPathToken(properties, potentialStringDelimiter)); return path.currentIsTail() || readNextToken(appender); } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenFactory.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenFactory.java index fa11c45a..e242253d 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenFactory.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenFactory.java @@ -13,12 +13,12 @@ public class PathTokenFactory { return new RootPathToken(token); } - public static PathToken createSinglePropertyPathToken(String property) { - return new PropertyPathToken(singletonList(property)); + public static PathToken createSinglePropertyPathToken(String property, char stringDelimiter) { + return new PropertyPathToken(singletonList(property), stringDelimiter); } - public static PathToken createPropertyPathToken(List properties) { - return new PropertyPathToken(properties); + public static PathToken createPropertyPathToken(List properties, char stringDelimiter) { + return new PropertyPathToken(properties, stringDelimiter); } public static PathToken createSliceArrayPathToken(final ArraySliceOperation arraySliceOperation) { diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PropertyPathToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PropertyPathToken.java index 044fc1cf..74d280e4 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PropertyPathToken.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PropertyPathToken.java @@ -30,12 +30,14 @@ import static com.jayway.jsonpath.internal.Utils.onlyOneIsTrueNonThrow; class PropertyPathToken extends PathToken { private final List properties; + private final String stringDelimiter; - public PropertyPathToken(List properties) { + public PropertyPathToken(List properties, char stringDelimiter) { if (properties.isEmpty()) { throw new InvalidPathException("Empty properties"); } this.properties = properties; + this.stringDelimiter = Character.toString(stringDelimiter); } public List getProperties() { @@ -97,7 +99,7 @@ class PropertyPathToken extends PathToken { public String getPathFragment() { return new StringBuilder() .append("[") - .append(Utils.join(",", "'", properties)) + .append(Utils.join(",", stringDelimiter, properties)) .append("]").toString(); } } diff --git a/json-path/src/test/java/com/jayway/jsonpath/FilterCompilerTest.java b/json-path/src/test/java/com/jayway/jsonpath/FilterCompilerTest.java index b1b6039b..bb6bd33c 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/FilterCompilerTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/FilterCompilerTest.java @@ -42,18 +42,24 @@ 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'])]"); + assertThat(compile("[?($[\"firstname\"][\"lastname\"])]").toString()).isEqualTo("[?($[\"firstname\"][\"lastname\"])]"); + assertThat(compile("[?($[\"firstname\"].lastname)]").toString()).isEqualTo("[?($[\"firstname\"]['lastname'])]"); + assertThat(compile("[?($[\"firstname\", \"lastname\"])]").toString()).isEqualTo("[?($[\"firstname\",\"lastname\"])]"); } @Test - public void string_quoute_style_is_serialized() { + public void string_quote_style_is_serialized() { assertThat(compile("[?('apa' == 'apa')]").toString()).isEqualTo("[?('apa' == 'apa')]"); assertThat(compile("[?('apa' == \"apa\")]").toString()).isEqualTo("[?('apa' == \"apa\")]"); } + @Test + public void string_can_contain_path_chars() { + assertThat(compile("[?(@[')]@$)]'] == ')]@$)]')]").toString()).isEqualTo("[?(@[')]@$)]'] == ')]@$)]')]"); + assertThat(compile("[?(@[\")]@$)]\"] == \")]@$)]\")]").toString()).isEqualTo("[?(@[\")]@$)]\"] == \")]@$)]\")]"); + } + @Test public void invalid_filters_does_not_compile() { diff --git a/json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java b/json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java index a0d477fe..01acd726 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java @@ -59,7 +59,7 @@ public class PathCompilerTest { assertThat(compile("$['1prop']").toString()).isEqualTo("$['1prop']"); assertThat(compile("$['@prop']").toString()).isEqualTo("$['@prop']"); assertThat(compile("$[ '@prop' ]").toString()).isEqualTo("$['@prop']"); - assertThat(compile("$[\"prop\"]").toString()).isEqualTo("$['prop']"); + assertThat(compile("$[\"prop\"]").toString()).isEqualTo("$[\"prop\"]"); } @Test diff --git a/json-path/src/test/java/com/jayway/jsonpath/internal/path/PathTokenTest.java b/json-path/src/test/java/com/jayway/jsonpath/internal/path/PathTokenTest.java index 2ecb1d4a..9d87a7a3 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/internal/path/PathTokenTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/internal/path/PathTokenTest.java @@ -33,7 +33,7 @@ public class PathTokenTest extends BaseTest { private PathToken makePPT(final String ... properties) { - return new PropertyPathToken(Arrays.asList(properties)); + return new PropertyPathToken(Arrays.asList(properties), '\''); } private PathToken makePathReturningTail(final PathToken ... tokens) {