diff --git a/json-path/src/main/java/com/jayway/jsonpath/Filter.java b/json-path/src/main/java/com/jayway/jsonpath/Filter.java index 3bfb443e..c99786a5 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/Filter.java +++ b/json-path/src/main/java/com/jayway/jsonpath/Filter.java @@ -178,8 +178,7 @@ public abstract class Filter implements Predicate { public static Filter parse(String filter){ - Predicate f = FilterCompiler.compile(filter); - return new SingleFilter(f); + return FilterCompiler.compile(filter); } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java b/json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java index c9decdfb..37649552 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java @@ -15,54 +15,14 @@ package com.jayway.jsonpath.internal; import com.jayway.jsonpath.JsonPathException; -import com.jayway.jsonpath.ValueCompareException; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamClass; -import java.io.OutputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; +import java.io.StringWriter; import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; public final class Utils { - public static final String CR = System.getProperty("line.separator"); - private static final char BACKSLASH = '\\'; - - /** - * Creates a range of integers from start (inclusive) to end (exclusive) - * - * @param start - * @param end - * @return - */ - public static List createRange(int start, int end) { - if (end <= start) { - throw new IllegalArgumentException("Cannot create range from " + start + " to " + end + ", end must be greater than start."); - } - if (start == end - 1) { - return Collections.emptyList(); - } - List range = new ArrayList(end - start - 1); - for (int i = start; i < end; i++) { - range.add(i); - } - return range; - } - - public static Iterable reverse(final List list) { - return new ListReverser(list); - } - // accept a collection of objects, since all objects have toString() public static String join(String delimiter, String wrap, Iterable objs) { Iterator iter = objs.iterator(); @@ -133,81 +93,166 @@ public final class Utils { } } - //--------------------------------------------------------- - // - // Strings - // - //--------------------------------------------------------- - public static boolean isInt(String str) { + public static String escape(String str, boolean escapeSingleQuote) { if (str == null) { - return false; + return null; } - int sz = str.length(); - for (int i = 0; i < sz; i++) { - if (Character.isDigit(str.charAt(i)) == false) { - return false; + int len = str.length(); + StringWriter writer = new StringWriter(len * 2); + + for (int i = 0; i < len; i++) { + char ch = str.charAt(i); + + // handle unicode + if (ch > 0xfff) { + writer.write("\\u" + hex(ch)); + } else if (ch > 0xff) { + writer.write("\\u0" + hex(ch)); + } else if (ch > 0x7f) { + writer.write("\\u00" + hex(ch)); + } else if (ch < 32) { + switch (ch) { + case '\b': + writer.write('\\'); + writer.write('b'); + break; + case '\n': + writer.write('\\'); + writer.write('n'); + break; + case '\t': + writer.write('\\'); + writer.write('t'); + break; + case '\f': + writer.write('\\'); + writer.write('f'); + break; + case '\r': + writer.write('\\'); + writer.write('r'); + break; + default : + if (ch > 0xf) { + writer.write("\\u00" + hex(ch)); + } else { + writer.write("\\u000" + hex(ch)); + } + break; + } + } else { + switch (ch) { + case '\'': + if (escapeSingleQuote) { + writer.write('\\'); + } + writer.write('\''); + break; + case '"': + writer.write('\\'); + writer.write('"'); + break; + case '\\': + writer.write('\\'); + writer.write('\\'); + break; + case '/': + writer.write('\\'); + writer.write('/'); + break; + default : + writer.write(ch); + break; + } } } - return true; + return writer.toString(); } - public static boolean isNumeric(String str) { - if (str == null || str.trim().isEmpty()) { - return false; + public static String unescape(String str) { + if (str == null) { + return null; } - int sz = str.length(); - for (int i = 0; i < sz; i++) { - if (Character.isDigit(str.charAt(i)) == false && !(str.charAt(i) == '.')) { - return false; + int len = str.length(); + StringWriter writer = new StringWriter(len); + StringBuffer unicode = new StringBuffer(4); + boolean hadSlash = false; + boolean inUnicode = false; + for (int i = 0; i < len; i++) { + char ch = str.charAt(i); + if (inUnicode) { + unicode.append(ch); + if (unicode.length() == 4) { + try { + int value = Integer.parseInt(unicode.toString(), 16); + writer.write((char) value); + unicode.setLength(0); + inUnicode = false; + hadSlash = false; + } catch (NumberFormatException nfe) { + throw new JsonPathException("Unable to parse unicode value: " + unicode, nfe); + } + } + continue; } - } - return true; - } - - public static String unescape(String s) { - if (s.indexOf(BACKSLASH) == -1) { - return s; - } - StringBuilder sb = new StringBuilder(s.length()); - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == BACKSLASH) { - char c2 = s.charAt(++i); - switch (c2) { + if (hadSlash) { + hadSlash = false; + switch (ch) { case '\\': - c2 = '\\'; - break; - case 'b': - c2 = '\b'; + writer.write('\\'); break; - case 'f': - c2 = '\f'; + case '\'': + writer.write('\''); break; - case 'n': - c2 = '\n'; + case '\"': + writer.write('"'); break; case 'r': - c2 = '\r'; + writer.write('\r'); + break; + case 'f': + writer.write('\f'); break; case 't': - c2 = '\t'; + writer.write('\t'); + break; + case 'n': + writer.write('\n'); + break; + case 'b': + writer.write('\b'); break; case 'u': - try { - String hex = s.substring(i + 1, i + 5); - c2 = (char) Integer.parseInt(hex, 16); - i += 4; - } catch (Exception e) { - throw new ValueCompareException("\\u parse failed", e); - } + { + inUnicode = true; + break; + } + default : + writer.write(ch); break; } - sb.append(c2); - } else { - sb.append(c); + continue; + } else if (ch == '\\') { + hadSlash = true; + continue; } + writer.write(ch); } - return sb.toString(); + if (hadSlash) { + writer.write('\\'); + } + return writer.toString(); + } + + /** + * Returns an upper case hexadecimal String for the given + * character. + * + * @param ch The character to convert. + * @return An upper case hexadecimal String + */ + public static String hex(char ch) { + return Integer.toHexString(ch).toUpperCase(); } /** @@ -245,38 +290,7 @@ public final class Utils { return cs.toString().indexOf(searchChar.toString(), start); } - /** - *

Counts how many times the substring appears in the larger string.

- *

- *

A {@code null} or empty ("") String input returns {@code 0}.

- *

- *

-     * StringUtils.countMatches(null, *)       = 0
-     * StringUtils.countMatches("", *)         = 0
-     * StringUtils.countMatches("abba", null)  = 0
-     * StringUtils.countMatches("abba", "")    = 0
-     * StringUtils.countMatches("abba", "a")   = 2
-     * StringUtils.countMatches("abba", "ab")  = 1
-     * StringUtils.countMatches("abba", "xxx") = 0
-     * 
- * - * @param str the CharSequence to check, may be null - * @param sub the substring to count, may be null - * @return the number of occurrences, 0 if either CharSequence is {@code null} - * @since 3.0 Changed signature from countMatches(String, String) to countMatches(CharSequence, CharSequence) - */ - public static int countMatches(CharSequence str, CharSequence sub) { - if (isEmpty(str) || isEmpty(sub)) { - return 0; - } - int count = 0; - int idx = 0; - while ((idx = indexOf(str, sub, idx)) != -1) { - count++; - idx += sub.length(); - } - return count; - } + //--------------------------------------------------------- // @@ -384,200 +398,9 @@ public final class Utils { if (null == o) { return null; } - return o.toString(); } - //--------------------------------------------------------- - // - // Serialization - // - //--------------------------------------------------------- - - - /** - *

Serializes an {@code Object} to the specified stream.

- *

- *

The stream will be closed once the object is written. - * This avoids the need for a finally clause, and maybe also exception - * handling, in the application code.

- *

- *

The stream passed in is not buffered internally within this method. - * This is the responsibility of your application if desired.

- * - * @param obj the object to serialize to bytes, may be null - * @param outputStream the stream to write to, must not be null - * @throws IllegalArgumentException if {@code outputStream} is {@code null} - * @throws RuntimeException (runtime) if the serialization fails - */ - public static void serialize(Serializable obj, OutputStream outputStream) { - if (outputStream == null) { - throw new IllegalArgumentException("The OutputStream must not be null"); - } - ObjectOutputStream out = null; - try { - // stream closed in the finally - out = new ObjectOutputStream(outputStream); - out.writeObject(obj); - - } catch (IOException ex) { - throw new JsonPathException(ex); - } finally { - try { - if (out != null) { - out.close(); - } - } catch (IOException ex) { // NOPMD - // ignore close exception - } - } - } - - /** - *

Serializes an {@code Object} to a byte array for - * storage/serialization.

- * - * @param obj the object to serialize to bytes - * @return a byte[] with the converted Serializable - * @throws RuntimeException (runtime) if the serialization fails - */ - public static byte[] serialize(Serializable obj) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(512); - serialize(obj, baos); - return baos.toByteArray(); - } - - // Deserialize - //----------------------------------------------------------------------- - - /** - *

Deserializes an {@code Object} from the specified stream.

- *

- *

The stream will be closed once the object is written. This - * avoids the need for a finally clause, and maybe also exception - * handling, in the application code.

- *

- *

The stream passed in is not buffered internally within this method. - * This is the responsibility of your application if desired.

- * - * @param inputStream the serialized object input stream, must not be null - * @return the deserialized object - * @throws IllegalArgumentException if {@code inputStream} is {@code null} - * @throws RuntimeException (runtime) if the serialization fails - */ - public static Object deserialize(InputStream inputStream) { - if (inputStream == null) { - throw new IllegalArgumentException("The InputStream must not be null"); - } - ObjectInputStream in = null; - try { - // stream closed in the finally - in = new ObjectInputStream(inputStream); - return in.readObject(); - - } catch (ClassNotFoundException ex) { - throw new JsonPathException(ex); - } catch (IOException ex) { - throw new JsonPathException(ex); - } finally { - try { - if (in != null) { - in.close(); - } - } catch (IOException ex) { // NOPMD - // ignore close exception - } - } - } - - /** - *

Deserializes a single {@code Object} from an array of bytes.

- * - * @param objectData the serialized object, must not be null - * @return the deserialized object - * @throws IllegalArgumentException if {@code objectData} is {@code null} - * @throws RuntimeException (runtime) if the serialization fails - */ - public static Object deserialize(byte[] objectData) { - if (objectData == null) { - throw new IllegalArgumentException("The byte[] must not be null"); - } - ByteArrayInputStream bais = new ByteArrayInputStream(objectData); - return deserialize(bais); - } - - /** - *

Custom specialization of the standard JDK {@link java.io.ObjectInputStream} - * that uses a custom ClassLoader to resolve a class. - * If the specified ClassLoader is not able to resolve the class, - * the context classloader of the current thread will be used. - * This way, the standard deserialization work also in web-application - * containers and application servers, no matter in which of the - * ClassLoader the particular class that encapsulates - * serialization/deserialization lives.

- *

- *

For more in-depth information about the problem for which this - * class here is a workaround, see the JIRA issue LANG-626.

- */ - static class ClassLoaderAwareObjectInputStream extends ObjectInputStream { - private ClassLoader classLoader; - - /** - * Constructor. - * - * @param in The InputStream. - * @param classLoader classloader to use - * @throws IOException if an I/O error occurs while reading stream header. - * @see java.io.ObjectInputStream - */ - public ClassLoaderAwareObjectInputStream(InputStream in, ClassLoader classLoader) throws IOException { - super(in); - this.classLoader = classLoader; - } - - /** - * Overriden version that uses the parametrized ClassLoader or the ClassLoader - * of the current Thread to resolve the class. - * - * @param desc An instance of class ObjectStreamClass. - * @return A Class object corresponding to desc. - * @throws IOException Any of the usual Input/Output exceptions. - * @throws ClassNotFoundException If class of a serialized object cannot be found. - */ - @Override - protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { - String name = desc.getName(); - try { - return Class.forName(name, false, classLoader); - } catch (ClassNotFoundException ex) { - return Class.forName(name, false, Thread.currentThread().getContextClassLoader()); - } - } - - } - - private static class ListReverser implements Iterable { - private ListIterator listIterator; - - public ListReverser(List wrappedList) { - this.listIterator = wrappedList.listIterator(wrappedList.size()); - } - - public Iterator iterator() { - return new Iterator() { - public boolean hasNext() { - return listIterator.hasPrevious(); - } - public T next() { - return listIterator.previous(); - } - public void remove() { - listIterator.remove(); - } - }; - } - } - private Utils() { } } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ExpressionNode.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ExpressionNode.java index 99f9c994..f5871c52 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ExpressionNode.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ExpressionNode.java @@ -6,16 +6,16 @@ public abstract class ExpressionNode implements Predicate { public static ExpressionNode createExpressionNode(ExpressionNode right, LogicalOperator operator, ExpressionNode left){ if(operator == LogicalOperator.AND){ - if((left instanceof LogicalExpressionNode) && ((LogicalExpressionNode)left).getOperator() == LogicalOperator.AND ){ - LogicalExpressionNode len = (LogicalExpressionNode) left; - return len.append(right); + if((right instanceof LogicalExpressionNode) && ((LogicalExpressionNode)right).getOperator() == LogicalOperator.AND ){ + LogicalExpressionNode len = (LogicalExpressionNode) right; + return len.append(left); } else { return LogicalExpressionNode.createLogicalAnd(left, right); } } else { - if((left instanceof LogicalExpressionNode) && ((LogicalExpressionNode)left).getOperator() == LogicalOperator.OR ){ - LogicalExpressionNode len = (LogicalExpressionNode) left; - return len.append(right); + if((right instanceof LogicalExpressionNode) && ((LogicalExpressionNode)right).getOperator() == LogicalOperator.OR ){ + LogicalExpressionNode len = (LogicalExpressionNode) right; + return len.append(left); } else { return LogicalExpressionNode.createLogicalOr(left, right); } 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 c965ce58..c6a97f97 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 @@ -1,5 +1,6 @@ package com.jayway.jsonpath.internal.filter; +import com.jayway.jsonpath.Filter; import com.jayway.jsonpath.InvalidPathException; import com.jayway.jsonpath.Predicate; import com.jayway.jsonpath.internal.CharacterIndex; @@ -38,9 +39,9 @@ public class FilterCompiler { private CharacterIndex filter; - public static Predicate compile(String filterString) { + public static Filter compile(String filterString) { FilterCompiler compiler = new FilterCompiler(filterString); - return compiler.compile(); + return new CompiledFilter(compiler.compile()); } private FilterCompiler(String filterString) { @@ -78,9 +79,15 @@ public class FilterCompiler { case CLOSE_BRACKET: unbalancedBrackets--; filter.incrementPosition(1); - while(!opsStack.isEmpty()){ - expStack.push(ExpressionNode.createExpressionNode(expStack.pop(), opsStack.pop(), expStack.pop())); + ExpressionNode expressionNode = expStack.pop(); + if(!opsStack.isEmpty()){ + ExpressionNode right = expStack.pop(); + expressionNode = ExpressionNode.createExpressionNode(expressionNode, opsStack.pop(), right); + while(!opsStack.isEmpty()){ + expressionNode = ExpressionNode.createExpressionNode(expressionNode, opsStack.pop(), expStack.pop()); + } } + expStack.push(expressionNode); break; case BANG: filter.incrementPosition(1); @@ -336,4 +343,28 @@ public class FilterCompiler { private boolean isRelationalOperatorChar(char c) { return c == LT || c == GT || c == EQ || c == TILDE || c == BANG; } + + private static final class CompiledFilter extends Filter { + + private final Predicate predicate; + + private CompiledFilter(Predicate predicate) { + this.predicate = predicate; + } + + @Override + public boolean apply(Predicate.PredicateContext ctx) { + return predicate.apply(ctx); + } + + @Override + public String toString() { + String predicateString = predicate.toString(); + if(predicateString.startsWith("(")){ + return "[?" + predicateString + "]"; + } else { + return "[?(" + predicateString + ")]"; + } + } + } } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/LogicalExpressionNode.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/LogicalExpressionNode.java index 327f4f31..a16ac842 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/LogicalExpressionNode.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/LogicalExpressionNode.java @@ -36,7 +36,7 @@ public class LogicalExpressionNode extends ExpressionNode { } public LogicalExpressionNode append(ExpressionNode expressionNode) { - chain.add(expressionNode); + chain.add(0, expressionNode); return this; } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java index 9a091fcc..ddbb3796 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java @@ -415,7 +415,7 @@ public abstract class ValueNode { @Override public String toString() { - return "'" + string + "'"; + return "'" + Utils.escape(string, true) + "'"; } @Override 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 e50fc212..d689aebc 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/FilterCompilerTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/FilterCompilerTest.java @@ -1,54 +1,41 @@ package com.jayway.jsonpath; -import com.jayway.jsonpath.internal.filter.FilterCompiler; import org.junit.Test; +import static com.jayway.jsonpath.internal.filter.FilterCompiler.compile; +import static org.assertj.core.api.Assertions.assertThat; + public class FilterCompilerTest { + @Test public void filter_compiler_test() { - - FilterCompiler.compile("[?(@)]"); - - FilterCompiler.compile("[?($)]"); - - FilterCompiler.compile("[?(@.firstname)]"); - FilterCompiler.compile("[?(@.firstname)]"); - FilterCompiler.compile("[?($.firstname)]"); - FilterCompiler.compile("[?(@['firstname'])]"); - - - FilterCompiler.compile("[?($['firstname'].lastname)]"); - FilterCompiler.compile("[?($['firstname']['lastname'])]"); - - FilterCompiler.compile("[?($['firstname']['lastname'].*)]"); - - FilterCompiler.compile("[?($['firstname']['num_eq'] == 1)]"); - FilterCompiler.compile("[?($['firstname']['num_gt'] > 1.1)]"); - - FilterCompiler.compile("[?($['firstname']['num_lt'] < 11.11)]"); - - FilterCompiler.compile("[?($['firstname']['str_eq'] == 'hej')]"); - FilterCompiler.compile("[?($['firstname']['str_eq'] == '')]"); - - FilterCompiler.compile("[?($['firstname']['str_eq'] == null)]"); - FilterCompiler.compile("[?($['firstname']['str_eq'] == true)]"); - FilterCompiler.compile("[?($['firstname']['str_eq'] == false)]"); - - - FilterCompiler.compile("[?(@.firstname && @.lastname)]"); - FilterCompiler.compile("[?((@.firstname || @.lastname) && @.and)]"); - - FilterCompiler.compile("[?((@.a || @.b || @.c) && @.x)]"); - FilterCompiler.compile("[?((@.a && @.b && @.c) || @.x)]"); - FilterCompiler.compile("[?((@.a && @.b || @.c) || @.x)]"); - FilterCompiler.compile("[?((@.a && @.b) || (@.c && @.d))]"); - - - FilterCompiler.compile("[?(@.a IN [1,2,3])]"); - FilterCompiler.compile("[?(@.a IN {'foo':'bar'})]"); - FilterCompiler.compile("[?(@.value<'7')]"); - - FilterCompiler.compile("[?(@.message == 'it\\\\')]"); + assertThat(compile("[?(@)]").toString()).isEqualTo("[?(@)]"); + assertThat(compile("[?(@)]").toString()).isEqualTo("[?(@)]"); + assertThat(compile("[?(@.firstname)]").toString()).isEqualTo("[?(@['firstname'])]"); + assertThat(compile("[?($.firstname)]").toString()).isEqualTo("[?($['firstname'])]"); + assertThat(compile("[?(@['firstname'])]").toString()).isEqualTo("[?(@['firstname'])]"); + 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']['num_eq'] == 1)]").toString()).isEqualTo("[?($['firstname']['num_eq'] == 1)]"); + assertThat(compile("[?($['firstname']['num_gt'] > 1.1)]").toString()).isEqualTo("[?($['firstname']['num_gt'] > 1.1)]"); + assertThat(compile("[?($['firstname']['num_lt'] < 11.11)]").toString()).isEqualTo("[?($['firstname']['num_lt'] < 11.11)]"); + assertThat(compile("[?($['firstname']['str_eq'] == 'hej')]").toString()).isEqualTo("[?($['firstname']['str_eq'] == 'hej')]"); + assertThat(compile("[?($['firstname']['str_eq'] == '')]").toString()).isEqualTo("[?($['firstname']['str_eq'] == '')]"); + assertThat(compile("[?($['firstname']['str_eq'] == null)]").toString()).isEqualTo("[?($['firstname']['str_eq'] == null)]"); + assertThat(compile("[?($['firstname']['str_eq'] == true)]").toString()).isEqualTo("[?($['firstname']['str_eq'] == true)]"); + assertThat(compile("[?($['firstname']['str_eq'] == false)]").toString()).isEqualTo("[?($['firstname']['str_eq'] == false)]"); + assertThat(compile("[?(@.firstname && @.lastname)]").toString()).isEqualTo("[?(@['firstname'] && @['lastname'])]"); + assertThat(compile("[?((@.firstname || @.lastname) && @.and)]").toString()).isEqualTo("[?((@['firstname'] || @['lastname']) && @['and'])]"); + assertThat(compile("[?((@.a || @.b || @.c) && @.x)]").toString()).isEqualTo("[?((@['a'] || @['b'] || @['c']) && @['x'])]"); + assertThat(compile("[?((@.a && @.b && @.c) || @.x)]").toString()).isEqualTo("[?((@['a'] && @['b'] && @['c']) || @['x'])]"); + assertThat(compile("[?((@.a && @.b || @.c) || @.x)]").toString()).isEqualTo("[?((@['a'] && (@['b'] || @['c'])) || @['x'])]"); + assertThat(compile("[?((@.a && @.b) || (@.c && @.d))]").toString()).isEqualTo("[?((@['a'] && @['b']) || (@['c'] && @['d']))]"); + assertThat(compile("[?(@.a IN [1,2,3])]").toString()).isEqualTo("[?(@['a'] IN [1,2,3])]"); + assertThat(compile("[?(@.a IN {'foo':'bar'})]").toString()).isEqualTo("[?(@['a'] IN {'foo':'bar'})]"); + assertThat(compile("[?(@.value<'7')]").toString()).isEqualTo("[?(@['value'] < '7')]"); + assertThat(compile("[?(@.message == 'it\\\\')]").toString()).isEqualTo("[?(@['message'] == 'it\\\\')]"); } + } diff --git a/json-path/src/test/java/com/jayway/jsonpath/internal/UtilsTest.java b/json-path/src/test/java/com/jayway/jsonpath/internal/UtilsTest.java new file mode 100644 index 00000000..f24e66d5 --- /dev/null +++ b/json-path/src/test/java/com/jayway/jsonpath/internal/UtilsTest.java @@ -0,0 +1,17 @@ +package com.jayway.jsonpath.internal; + +import org.junit.Test; + +public class UtilsTest { + + + @Test + public void strings_can_be_escaped() { + + String str = "it\\\\"; + System.out.println(Utils.unescape(str)); + System.out.println(Utils.escape(str, true)); + + } + +}