diff --git a/LICENSE b/LICENSE
index d6456956..9972f34b 100644
--- a/LICENSE
+++ b/LICENSE
@@ -187,7 +187,7 @@
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-   Copyright [yyyy] [name of copyright owner]
+   Copyright 2017 Jayway
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index c146999c..e0953842 100644
--- a/README.md
+++ b/README.md
@@ -155,6 +155,7 @@ Given the json
 | <a href="http://jsonpath.herokuapp.com/?path=$.store.*" target="_blank">$.store.*</a>                  | All things, both books and bicycles  |
 | <a href="http://jsonpath.herokuapp.com/?path=$.store..price" target="_blank">$.store..price</a>             | The price of everything         |
 | <a href="http://jsonpath.herokuapp.com/?path=$..book[2]" target="_blank">$..book[2]</a>                 | The third book                      |
+| <a href="http://jsonpath.herokuapp.com/?path=$..book[2]" target="_blank">$..book[-2]</a>                 | The second to last book            |
 | <a href="http://jsonpath.herokuapp.com/?path=$..book[0,1]" target="_blank">$..book[0,1]</a>               | The first two books               |
 | <a href="http://jsonpath.herokuapp.com/?path=$..book[:2]" target="_blank">$..book[:2]</a>                | All books from index 0 (inclusive) until index 2 (exclusive) |
 | <a href="http://jsonpath.herokuapp.com/?path=$..book[1:2]" target="_blank">$..book[1:2]</a>                | All books from index 1 (inclusive) until index 2 (exclusive) |
@@ -256,6 +257,8 @@ List<Map<String, Object>> books =  JsonPath.parse(json)
 You can use `&&` and `||` to combine multiple predicates `[?(@.price < 10 && @.category == 'fiction')]` , 
 `[?(@.category == 'reference' || @.price > 10)]`.
  
+You can use `!` to negate a predicate `[?(!(@.price < 10 && @.category == 'fiction'))]`.
+
 ###Filter Predicates
  
 Predicates can be built using the Filter API as shown below:
diff --git a/build.gradle b/build.gradle
index 31f6bbdf..899486e2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -12,12 +12,12 @@ buildscript {
 ext {
     libs = [
             slf4jApi: 'org.slf4j:slf4j-api:1.7.25',
-            jsonSmart: 'net.minidev:json-smart:2.2.1',
+            jsonSmart: 'net.minidev:json-smart:2.3',
             jacksonDatabind: 'com.fasterxml.jackson.core:jackson-databind:2.6.3',
             gson: 'com.google.code.gson:gson:2.3.1',
             jettison: 'org.codehaus.jettison:jettison:1.3.7',
             jsonOrg: 'org.json:json:20140107',
-            tapestryJson: 'org.apache.tapestry:tapestry-json:5.4.0',
+            tapestryJson: 'org.apache.tapestry:tapestry-json:5.4.1',
             hamcrestCore: 'org.hamcrest:hamcrest-core:1.3',
             hamcrestLibrary: 'org.hamcrest:hamcrest-library:1.3',
 
@@ -73,7 +73,7 @@ subprojects {
 }
 
 task wrapper(type: Wrapper) {
-    gradleVersion = '2.11'
+    gradleVersion = '3.4.1'
 }
 
 //Task used by Heroku for staging
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 5ccda13e..17279fa9 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ec299545..10eb72ab 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Fri Feb 12 10:36:51 CET 2016
+#Fri Mar 17 10:32:10 CET 2017
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip
diff --git a/gradlew b/gradlew
index 9d82f789..4453ccea 100755
--- a/gradlew
+++ b/gradlew
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
 
 ##############################################################################
 ##
@@ -6,12 +6,30 @@
 ##
 ##############################################################################
 
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
 
 APP_NAME="Gradle"
 APP_BASE_NAME=`basename "$0"`
 
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 MAX_FD="maximum"
 
@@ -30,6 +48,7 @@ die ( ) {
 cygwin=false
 msys=false
 darwin=false
+nonstop=false
 case "`uname`" in
   CYGWIN* )
     cygwin=true
@@ -40,26 +59,11 @@ case "`uname`" in
   MINGW* )
     msys=true
     ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
 esac
 
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-        PRG="$link"
-    else
-        PRG=`dirname "$PRG"`"/$link"
-    fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
 CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 
 # Determine the Java command to use to start the JVM.
@@ -85,7 +89,7 @@ location of your Java installation."
 fi
 
 # Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
     MAX_FD_LIMIT=`ulimit -H -n`
     if [ $? -eq 0 ] ; then
         if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -150,11 +154,19 @@ if $cygwin ; then
     esac
 fi
 
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
-    JVM_OPTS=("$@")
+# Escape application args
+save ( ) {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
 }
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
 
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index 72d362da..e95643d6 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -8,14 +8,14 @@
 @rem Set local scope for the variables with windows NT shell
 if "%OS%"=="Windows_NT" setlocal
 
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
 set DIRNAME=%~dp0
 if "%DIRNAME%" == "" set DIRNAME=.
 set APP_BASE_NAME=%~n0
 set APP_HOME=%DIRNAME%
 
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
 @rem Find java.exe
 if defined JAVA_HOME goto findJavaFromJavaHome
 
@@ -49,7 +49,6 @@ goto fail
 @rem Get command-line arguments, handling Windows variants
 
 if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
 
 :win9xME_args
 @rem Slurp the command line arguments.
@@ -60,11 +59,6 @@ set _SKIP=2
 if "x%~1" == "x" goto execute
 
 set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
 
 :execute
 @rem Setup the command line
diff --git a/json-path-assert/build.gradle b/json-path-assert/build.gradle
index 0a47bd0c..f63e70bb 100644
--- a/json-path-assert/build.gradle
+++ b/json-path-assert/build.gradle
@@ -1,7 +1,5 @@
 apply from: "$rootDir/gradle/publishMaven.gradle"
 
-displayName = "JsonPath Assert"
-
 description = "Assertions on Json using JsonPath"
 
 jar {
diff --git a/json-path-web-test/build.gradle b/json-path-web-test/build.gradle
index 7c185eb4..83eb94fc 100644
--- a/json-path-web-test/build.gradle
+++ b/json-path-web-test/build.gradle
@@ -1,18 +1,18 @@
 apply plugin: 'com.github.johnrengelman.shadow'
 apply plugin: 'application'
 
-displayName = "JsonPath Test Bench"
-
 description = "Web app that compares different JsonPath implementations."
 
 mainClassName = 'com.jayway.jsonpath.web.boot.Main'
 
-task createBuildInfoFile << {
-    def buildInfoFile = new File("$buildDir/classes/main/build-info.properties")
-    Properties props = new Properties()
-    props.setProperty('version', project.version.toString())
-    props.setProperty('timestamp', project.buildTimestamp)
-    props.store(buildInfoFile.newWriter(), null)
+task createBuildInfoFile {
+    doLast {
+	    def buildInfoFile = new File("$buildDir/classes/main/build-info.properties")
+	    Properties props = new Properties()
+	    props.setProperty('version', project.version.toString())
+	    props.setProperty('timestamp', project.buildTimestamp)
+	    props.store(buildInfoFile.newWriter(), null)
+    }
 }
 
 jar {
diff --git a/json-path/build.gradle b/json-path/build.gradle
index fa79910b..3aec3088 100644
--- a/json-path/build.gradle
+++ b/json-path/build.gradle
@@ -1,15 +1,12 @@
 apply from: "$rootDir/gradle/publishMaven.gradle"
 
-
-displayName = "Json Path"
-
 description = "Java port of Stefan Goessner JsonPath."
 
 jar {
     baseName 'json-path'
     manifest {
         attributes 'Implementation-Title': 'json-path', 'Implementation-Version': version
-        instruction 'Import-Package', 'org.json.*;resolution:=optional', 'com.google.gson.*;resolution:=optional', 'com.fasterxml.jackson.*;resolution:=optional', 'net.minidev.json.*;resolution:=optional', 'org.apache.tapestry5.json.*;resolution:=optional', '*'
+        instruction 'Import-Package', 'org.json.*;resolution:=optional', 'com.google.gson.*;resolution:=optional', 'com.fasterxml.jackson.*;resolution:=optional', 'net.minidev.json.*;resolution:=optional', 'org.apache.tapestry5.json.*;resolution:=optional', 'org.codehaus.jettison.*;resolution:=optional', '*'
     }
 }
 
@@ -39,7 +36,7 @@ task distZip(type: Zip, dependsOn: assemble) {
     }
     from(project.configurations.compile) {
         into 'lib'
-        exclude { it.file.name.contains('gson') || it.file.name.contains('jackson') || it.file.name.contains('json-2') || it.file.name.contains('jettison') }
+        exclude { it.file.name.contains('gson') || it.file.name.contains('jackson') || it.file.name.contains('json-2') || it.file.name.contains('jettison') || it.file.name.contains('tapestry') }
     }
     from(project.configurations.compile) {
         into 'lib-optional/jackson'
@@ -58,7 +55,7 @@ task distZip(type: Zip, dependsOn: assemble) {
         include { it.file.name.contains('json-2') }
     }
     from(project.configurations.compile) {
-        into 'lib-optional/jsonOrg'
+        into 'lib-optional/tapestry'
         include { it.file.name.contains('tapestry') }
     }
 }
@@ -79,7 +76,7 @@ task distTar(type: Tar, dependsOn: assemble) {
     }
     from(project.configurations.compile) {
         into 'lib'
-        exclude { it.file.name.contains('gson') || it.file.name.contains('jackson') || it.file.name.contains('json-2') || it.file.name.contains('jettison') }
+        exclude { it.file.name.contains('gson') || it.file.name.contains('jackson') || it.file.name.contains('json-2') || it.file.name.contains('jettison') || it.file.name.contains('tapestry') }
     }
     from(project.configurations.compile) {
         into 'lib-optional/jackson'
@@ -98,7 +95,7 @@ task distTar(type: Tar, dependsOn: assemble) {
         include { it.file.name.contains('json-2') }
     }
     from(project.configurations.compile) {
-        into 'lib-optional/jsonOrg'
+        into 'lib-optional/tapestry'
         include { it.file.name.contains('tapestry') }
     }
 }
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 b93811c3..1aa28679 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
@@ -17,14 +17,16 @@ public class CharacterIndex {
 
     private final CharSequence charSequence;
     private int position;
+    private int endPosition;
 
     public CharacterIndex(CharSequence charSequence) {
         this.charSequence = charSequence;
         this.position = 0;
+        this.endPosition = charSequence.length() - 1;
     }
 
     public int length() {
-        return charSequence.length();
+        return endPosition + 1;
     }
 
     public char charAt(int idx) {
@@ -39,6 +41,10 @@ public class CharacterIndex {
         return (charSequence.charAt(position) == c);
     }
 
+    public boolean lastCharIs(char c) {
+      return charSequence.charAt(endPosition) == c;
+    }
+
     public boolean nextCharIs(char c) {
         return inBounds(position + 1) && (charSequence.charAt(position + 1) == c);
     }
@@ -47,12 +53,21 @@ public class CharacterIndex {
         return setPosition(position + charCount);
     }
 
+    public int decrementEndPosition(int charCount) {
+        return setEndPosition(endPosition - charCount);
+    }
+
     public int setPosition(int newPosition) {
         //position = min(newPosition, charSequence.length() - 1);
         position = newPosition;
         return position;
     }
 
+    private int setEndPosition(int newPosition) {
+        endPosition = newPosition;
+        return endPosition;
+    }
+
     public int position(){
         return position;
     }
@@ -244,7 +259,7 @@ public class CharacterIndex {
     }
 
     public boolean currentIsTail() {
-        return position >= charSequence.length()-1;
+        return position >= endPosition;
     }
 
     public boolean hasMoreCharacters() {
@@ -252,7 +267,7 @@ public class CharacterIndex {
     }
 
     public boolean inBounds(int idx) {
-        return (idx >= 0) && (idx < charSequence.length());
+        return (idx >= 0) && (idx <= endPosition);
     }
     public boolean inBounds() {
         return inBounds(position);
@@ -281,9 +296,22 @@ public class CharacterIndex {
     }
 
     public CharacterIndex skipBlanks() {
-        while (inBounds() && currentChar() == SPACE){
+        while (inBounds() && position < endPosition  && currentChar() == SPACE){
             incrementPosition(1);
         }
         return this;
     }
+
+    private CharacterIndex skipBlanksAtEnd() {
+        while (inBounds() && position < endPosition && lastCharIs(SPACE)){
+            decrementEndPosition(1);
+        }
+        return this;
+    }
+
+    public CharacterIndex trim() {
+        skipBlanks();
+        skipBlanksAtEnd();
+        return this;
+    }
 }
\ No newline at end of file
diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/JsonContext.java b/json-path/src/main/java/com/jayway/jsonpath/internal/JsonContext.java
index 17ce9bce..50b84a08 100644
--- a/json-path/src/main/java/com/jayway/jsonpath/internal/JsonContext.java
+++ b/json-path/src/main/java/com/jayway/jsonpath/internal/JsonContext.java
@@ -289,7 +289,7 @@ public class JsonContext implements ParseContext, DocumentContext {
         return this;
     }
 
-    private final class LimitingEvaluationListener implements EvaluationListener {
+    private final static class LimitingEvaluationListener implements EvaluationListener {
         final int limit;
 
         private LimitingEvaluationListener(int limit) {
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 7126b278..67b512e4 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
@@ -54,20 +54,22 @@ public class FilterCompiler {
     }
 
     private FilterCompiler(String filterString) {
-        filterString = filterString.trim();
-        if (!filterString.startsWith("[") || !filterString.endsWith("]")) {
+        filter = new CharacterIndex(filterString);
+        filter.trim();
+        if (!filter.currentCharIs('[') || !filter.lastCharIs(']')) {
             throw new InvalidPathException("Filter must start with '[' and end with ']'. " + filterString);
         }
-        filterString = filterString.substring(1, filterString.length() - 1).trim();
-        if (!filterString.startsWith("?")) {
+        filter.incrementPosition(1);
+        filter.decrementEndPosition(1);
+        filter.trim();
+        if (!filter.currentCharIs('?')) {
             throw new InvalidPathException("Filter must start with '[?' and end with ']'. " + filterString);
         }
-        filterString = filterString.substring(1).trim();
-        if (!filterString.startsWith("(") || !filterString.endsWith(")")) {
+        filter.incrementPosition(1);
+        filter.trim();
+        if (!filter.currentCharIs('(') || !filter.lastCharIs(')')) {
             throw new InvalidPathException("Filter must start with '[?(' and end with ')]'. " + filterString);
         }
-
-        filter = new CharacterIndex(filterString);
     }
 
     public Predicate compile() {
@@ -120,7 +122,7 @@ public class FilterCompiler {
     /*
      *  LogicalOR               = LogicalAND { '||' LogicalAND }
      *  LogicalAND              = LogicalANDOperand { '&&' LogicalANDOperand }
-     *  LogicalANDOperand       = RelationalExpression | '(' LogicalOR ')'
+     *  LogicalANDOperand       = RelationalExpression | '(' LogicalOR ')' | '!' LogicalANDOperand
      *  RelationalExpression    = Value [ RelationalOperator Value ]
      */
 
@@ -164,6 +166,19 @@ public class FilterCompiler {
     }
 
     private ExpressionNode readLogicalANDOperand() {
+        int savepoint = filter.skipBlanks().position();
+        if (filter.skipBlanks().currentCharIs(NOT)) {
+            filter.readSignificantChar(NOT);
+            switch (filter.skipBlanks().currentChar()) {
+                case DOC_CONTEXT:
+                case EVAL_CONTEXT:
+                    filter.setPosition(savepoint);
+                    break;
+            default:
+                final ExpressionNode op = readLogicalANDOperand();
+                return LogicalExpressionNode.createLogicalNot(op);
+            }
+        }
         if (filter.skipBlanks().currentCharIs(OPEN_PARENTHESIS)) {
             filter.readSignificantChar(OPEN_PARENTHESIS);
             final ExpressionNode op = readLogicalOR();
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 3a5d9605..57cb3884 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
@@ -10,6 +10,10 @@ public class LogicalExpressionNode extends ExpressionNode {
     protected List<ExpressionNode> chain = new ArrayList<ExpressionNode>();
     private final LogicalOperator operator;
 
+    public static ExpressionNode createLogicalNot(ExpressionNode op) {
+       return new LogicalExpressionNode(op, LogicalOperator.NOT, null);
+    }
+
     public static LogicalExpressionNode createLogicalOr(ExpressionNode left,ExpressionNode right){
         return new LogicalExpressionNode(left, LogicalOperator.OR, right);
     }
@@ -68,13 +72,17 @@ public class LogicalExpressionNode extends ExpressionNode {
                 }
             }
             return false;
-        } else {
+        } else if (operator == LogicalOperator.AND) {
             for (ExpressionNode expression : chain) {
                 if(!expression.apply(ctx)){
                     return false;
                 }
             }
             return true;
+        } else {
+            ExpressionNode expression = chain.get(0);
+            return !expression.apply(ctx);
         }
     }
+
 }
diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/LogicalOperator.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/LogicalOperator.java
index 376e2ada..809a8560 100644
--- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/LogicalOperator.java
+++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/LogicalOperator.java
@@ -5,6 +5,7 @@ import com.jayway.jsonpath.InvalidPathException;
 public enum LogicalOperator {
 
     AND("&&"),
+    NOT("!"),
     OR("||");
 
     private final String operatorString;
@@ -24,6 +25,7 @@ public enum LogicalOperator {
 
     public static LogicalOperator fromString(String operatorString){
         if(AND.operatorString.equals(operatorString)) return AND;
+        else if(NOT.operatorString.equals(operatorString)) return NOT;
         else if(OR.operatorString.equals(operatorString)) return OR;
         else throw new InvalidPathException("Failed to parse operator " + operatorString);
     }
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 7506358a..3f3914e4 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
@@ -154,7 +154,7 @@ public abstract class ValueNode {
         if ((c0 == '[' && c1 == ']') || (c0 == '{' && c1 == '}')){
             try {
                 Configuration.defaultConfiguration().jsonProvider().parse(str);
-                return false;
+                return true;
             } catch(Exception e){
                 return false;
             }
@@ -805,9 +805,9 @@ public abstract class ValueNode {
         }
 
         public ValueNode evaluate(Predicate.PredicateContext ctx) {
-            Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build();
             if (isExistsCheck()) {
                 try {
+                    Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build();
                     Object result = path.evaluate(ctx.item(), ctx.root(), c).getValue(false);
                     return result == JsonProvider.UNDEFINED ? ValueNode.FALSE : ValueNode.TRUE;
                 } catch (PathNotFoundException e) {
@@ -842,4 +842,4 @@ public abstract class ValueNode {
 
 
     }
-}
\ No newline at end of file
+}
diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayIndexOperation.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayIndexOperation.java
index 8fde93a9..cbfa0253 100644
--- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayIndexOperation.java
+++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayIndexOperation.java
@@ -42,7 +42,7 @@ public class ArrayIndexOperation {
         //check valid chars
         for (int i = 0; i < operation.length(); i++) {
             char c = operation.charAt(i);
-            if (!isDigit(c) && c != ',' && c != ' ') {
+            if (!isDigit(c) && c != ',' && c != ' ' && c != '-') {
                 throw new InvalidPathException("Failed to parse ArrayIndexOperation: " + operation);
             }
         }
diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/EvaluationContextImpl.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/EvaluationContextImpl.java
index 3814419e..3fc7e487 100644
--- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/EvaluationContextImpl.java
+++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/EvaluationContextImpl.java
@@ -163,7 +163,7 @@ public class EvaluationContextImpl implements EvaluationContext {
         return res;
     }
 
-    private class FoundResultImpl implements EvaluationListener.FoundResult {
+    private static class FoundResultImpl implements EvaluationListener.FoundResult {
 
         private final int index;
         private final String path;
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 f83e953a..36d6b822 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
@@ -45,9 +45,13 @@ public class PathCompiler {
     private final LinkedList<Predicate> filterStack;
     private final CharacterIndex path;
 
-    private PathCompiler(String path, LinkedList<Predicate> filterStack) {
+    private PathCompiler(String path, LinkedList<Predicate> filterStack){
+        this(new CharacterIndex(path), filterStack);
+    }
+
+    private PathCompiler(CharacterIndex path, LinkedList<Predicate> filterStack){
         this.filterStack = filterStack;
-        this.path = new CharacterIndex(path);
+        this.path = path;
     }
 
     private Path compile() {
@@ -57,16 +61,18 @@ public class PathCompiler {
 
     public static Path compile(String path, final Predicate... filters) {
         try {
-            path = path.trim();
+            CharacterIndex ci = new CharacterIndex(path);
+            ci.trim();
 
-            if(!(path.charAt(0) == DOC_CONTEXT)  && !(path.charAt(0) == EVAL_CONTEXT)){
-                path = "$." + path;
+            if(!( ci.charAt(0) == DOC_CONTEXT)  && !( ci.charAt(0) == EVAL_CONTEXT)){
+                ci = new CharacterIndex("$." + path);
+                ci.trim();
             }
-            if(path.endsWith(".")){
+            if(ci.lastCharIs('.')){
                 fail("Path must not end with a '.' or '..'");
             }
-            LinkedList filterStack = new LinkedList<Predicate>(asList(filters));
-            Path p = new PathCompiler(path.trim(), filterStack).compile();
+            LinkedList<Predicate> filterStack = new LinkedList<Predicate>(asList(filters));
+            Path p = new PathCompiler(ci, filterStack).compile();
             return p;
         } catch (Exception e) {
             InvalidPathException ipe;
@@ -103,7 +109,6 @@ public class PathCompiler {
         }
 
         RootPathToken pathToken = PathTokenFactory.createRootPathToken(path.currentChar());
-        PathTokenAppender appender = pathToken.getPathTokenAppender();
 
         if (path.currentIsTail()) {
             return pathToken;
@@ -115,6 +120,7 @@ public class PathCompiler {
             fail("Illegal character at position " + path.position() + " expected '.' or '[");
         }
 
+        PathTokenAppender appender = pathToken.getPathTokenAppender();
         readNextToken(appender);
 
         return pathToken;
@@ -261,7 +267,6 @@ public class PathCompiler {
      *      an array.
      */
     private List<Parameter> parseFunctionParameters(String funcName) {
-        PathToken currentToken;
         ParamType type = null;
 
         // Parenthesis starts at 1 since we're marking the start of a function call, the close paren will denote the
diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathToken.java
index dc22dda1..e4d23604 100644
--- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathToken.java
+++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathToken.java
@@ -125,8 +125,9 @@ public abstract class PathToken {
     protected void handleArrayIndex(int index, String currentPath, Object model, EvaluationContextImpl ctx) {
         String evalPath = Utils.concat(currentPath, "[", String.valueOf(index), "]");
         PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, index) : PathRef.NO_OP;
+        int effectiveIndex = index < 0 ? ctx.jsonProvider().length(model) + index : index;
         try {
-            Object evalHit = ctx.jsonProvider().getArrayIndex(model, index);
+            Object evalHit = ctx.jsonProvider().getArrayIndex(model, effectiveIndex);
             if (isLeaf()) {
                 ctx.addResult(evalPath, pathRef, evalHit);
             } else {
diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/json/GsonJsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/json/GsonJsonProvider.java
index 7a2636a8..0828b295 100644
--- a/json-path/src/main/java/com/jayway/jsonpath/spi/json/GsonJsonProvider.java
+++ b/json-path/src/main/java/com/jayway/jsonpath/spi/json/GsonJsonProvider.java
@@ -14,28 +14,25 @@
  */
 package com.jayway.jsonpath.spi.json;
 
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.UnsupportedEncodingException;
-
-import java.math.BigDecimal;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
 import com.google.gson.Gson;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
 import com.google.gson.JsonPrimitive;
-import com.google.gson.internal.LazilyParsedNumber;
-
 import com.jayway.jsonpath.InvalidJsonException;
 import com.jayway.jsonpath.JsonPathException;
 
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
 public class GsonJsonProvider extends AbstractJsonProvider {
 
     private static final JsonParser PARSER = new JsonParser();
@@ -86,25 +83,38 @@ public class GsonJsonProvider extends AbstractJsonProvider {
         return o;
     }
 
+    private static boolean isPrimitiveNumber(final Number n) {
+        return n instanceof Integer ||
+                n instanceof Double ||
+                n instanceof Long ||
+                n instanceof BigDecimal ||
+                n instanceof BigInteger;
+    }
+
     private static Number unwrapNumber(final Number n) {
         Number unwrapped;
 
-        if (n instanceof LazilyParsedNumber) {
-            LazilyParsedNumber lpn = (LazilyParsedNumber) n;
-            BigDecimal bigDecimal = new BigDecimal(lpn.toString());
+        if (!isPrimitiveNumber(n)) {
+            BigDecimal bigDecimal = new BigDecimal(n.toString());
             if (bigDecimal.scale() <= 0) {
                 if (bigDecimal.compareTo(new BigDecimal(Integer.MAX_VALUE)) <= 0) {
                     unwrapped = bigDecimal.intValue();
-                } else {
+                } else if (bigDecimal.compareTo(new BigDecimal(Long.MAX_VALUE)) <= 0){
                     unwrapped = bigDecimal.longValue();
+                } else {
+                    unwrapped = bigDecimal;
                 }
             } else {
-                unwrapped = bigDecimal.doubleValue();
+                final double doubleValue = bigDecimal.doubleValue();
+                if (BigDecimal.valueOf(doubleValue).compareTo(bigDecimal) != 0) {
+                    unwrapped = bigDecimal;
+                } else {
+                    unwrapped = doubleValue;
+                }
             }
         } else {
             unwrapped = n;
         }
-
         return unwrapped;
     }
 
diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonOrgJsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonOrgJsonProvider.java
index a5b77e88..0c5f169e 100644
--- a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonOrgJsonProvider.java
+++ b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonOrgJsonProvider.java
@@ -1,6 +1,6 @@
 package com.jayway.jsonpath.spi.json;
 
-import com.google.gson.JsonObject;
+import org.json.JSONObject;
 import com.jayway.jsonpath.InvalidJsonException;
 import com.jayway.jsonpath.JsonPathException;
 import org.json.JSONArray;
@@ -62,7 +62,7 @@ public class JsonOrgJsonProvider extends AbstractJsonProvider {
 
     @Override
     public Object createMap() {
-        return new JsonObject();
+        return new JSONObject();
     }
 
     @Override
@@ -96,8 +96,8 @@ public class JsonOrgJsonProvider extends AbstractJsonProvider {
     public Object getMapValue(Object obj, String key) {
         try {
             JSONObject jsonObject = toJsonObject(obj);
-            Object o = jsonObject.get(key);
-            if (!jsonObject.has(key)) {
+            Object o = jsonObject.opt(key);
+            if (o == null) {
                 return UNDEFINED;
             } else {
                 return unwrap(o);
diff --git a/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java b/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java
index 58493a35..a9c02e76 100644
--- a/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java
+++ b/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java
@@ -89,6 +89,13 @@ public class FilterTest extends BaseTest {
         assertThat(filter(where("string-key").eq(null)).apply(createPredicateContext(json))).isEqualTo(false);
     }
 
+    @Test
+    public void arr_eq_evals() {
+        assertThat(filter(where("arr-empty").eq("[]")).apply(createPredicateContext(json))).isEqualTo(true);
+        assertThat(filter(where("int-arr").eq("[0,1,2,3,4]")).apply(createPredicateContext(json))).isEqualTo(true);
+        assertThat(filter(where("int-arr").eq("[0,1,2,3]")).apply(createPredicateContext(json))).isEqualTo(false);
+        assertThat(filter(where("int-arr").eq("[0,1,2,3,4,5]")).apply(createPredicateContext(json))).isEqualTo(false);
+    }
     //----------------------------------------------------------------------------
     //
     // NE
diff --git a/json-path/src/test/java/com/jayway/jsonpath/GsonJsonProviderTest.java b/json-path/src/test/java/com/jayway/jsonpath/GsonJsonProviderTest.java
index 8f3ec333..1fc85bd9 100644
--- a/json-path/src/test/java/com/jayway/jsonpath/GsonJsonProviderTest.java
+++ b/json-path/src/test/java/com/jayway/jsonpath/GsonJsonProviderTest.java
@@ -7,6 +7,8 @@ import com.jayway.jsonpath.spi.mapper.MappingException;
 import org.junit.Test;
 
 import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.List;
 
 import static com.jayway.jsonpath.JsonPath.using;
@@ -69,6 +71,65 @@ public class GsonJsonProviderTest extends BaseTest {
         assertThat(val).isEqualTo(node.getAsLong());
     }
 
+    @Test
+    public void doubles_are_unwrapped() {
+        final String json = "{double-property = 56.78}";
+
+        JsonElement node =  using(GSON_CONFIGURATION).parse(json).read("$.double-property");
+        Double val =  using(GSON_CONFIGURATION).parse(json).read("$.double-property", Double.class);
+
+        assertThat(val).isEqualTo(56.78);
+        assertThat(val).isEqualTo(node.getAsDouble());
+    }
+
+    @Test
+    public void bigdecimals_are_unwrapped() {
+        final BigDecimal bd = BigDecimal.valueOf(Long.MAX_VALUE).add(BigDecimal.valueOf(10.5));
+        final String json = "{bd-property = " + bd.toString() + "}";
+
+        JsonElement node =  using(GSON_CONFIGURATION).parse(json).read("$.bd-property");
+        BigDecimal val =  using(GSON_CONFIGURATION).parse(json).read("$.bd-property", BigDecimal.class);
+
+        assertThat(val).isEqualTo(bd);
+        assertThat(val).isEqualTo(node.getAsBigDecimal());
+    }
+
+    @Test
+    public void small_bigdecimals_are_unwrapped() {
+        final BigDecimal bd = BigDecimal.valueOf(10.5);
+        final String json = "{bd-property = " + bd.toString() + "}";
+
+        JsonElement node =  using(GSON_CONFIGURATION).parse(json).read("$.bd-property");
+        BigDecimal val =  using(GSON_CONFIGURATION).parse(json).read("$.bd-property", BigDecimal.class);
+
+        assertThat(val).isEqualTo(bd);
+        assertThat(val).isEqualTo(node.getAsBigDecimal());
+    }
+
+    @Test
+    public void bigintegers_are_unwrapped() {
+        final BigInteger bi = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.TEN);
+        final String json = "{bi-property = " + bi.toString() + "}";
+
+        JsonElement node =  using(GSON_CONFIGURATION).parse(json).read("$.bi-property");
+        BigInteger val =  using(GSON_CONFIGURATION).parse(json).read("$.bi-property", BigInteger.class);
+
+        assertThat(val).isEqualTo(bi);
+        assertThat(val).isEqualTo(node.getAsBigInteger());
+    }
+
+    @Test
+    public void small_bigintegers_are_unwrapped() {
+        final BigInteger bi = BigInteger.valueOf(Long.MAX_VALUE);
+        final String json = "{bi-property = " + bi.toString() + "}";
+
+        JsonElement node =  using(GSON_CONFIGURATION).parse(json).read("$.bi-property");
+        BigInteger val =  using(GSON_CONFIGURATION).parse(json).read("$.bi-property", BigInteger.class);
+
+        assertThat(val).isEqualTo(bi);
+        assertThat(val).isEqualTo(node.getAsBigInteger());
+    }
+
     @Test
     public void int_to_long_mapping() {
         assertThat(using(GSON_CONFIGURATION).parse("{\"val\": 1}").read("val", Long.class)).isEqualTo(1L);
diff --git a/json-path/src/test/java/com/jayway/jsonpath/JsonOrgJsonProviderTest.java b/json-path/src/test/java/com/jayway/jsonpath/JsonOrgJsonProviderTest.java
index d24e30d1..59ba665c 100644
--- a/json-path/src/test/java/com/jayway/jsonpath/JsonOrgJsonProviderTest.java
+++ b/json-path/src/test/java/com/jayway/jsonpath/JsonOrgJsonProviderTest.java
@@ -44,4 +44,12 @@ public class JsonOrgJsonProviderTest extends BaseTest {
 
         assertThat(books.size()).isEqualTo(4);
     }
+
+    @Test
+    public void read_books_with_isbn() {
+
+        JSONArray books = using(JSON_ORG_CONFIGURATION).parse(JSON_DOCUMENT).read("$..book[?(@.isbn)]");
+
+        assertThat(books.length()).isEqualTo(2);
+    }
 }
diff --git a/json-path/src/test/java/com/jayway/jsonpath/ProviderInTest.java b/json-path/src/test/java/com/jayway/jsonpath/ProviderInTest.java
new file mode 100644
index 00000000..0508539c
--- /dev/null
+++ b/json-path/src/test/java/com/jayway/jsonpath/ProviderInTest.java
@@ -0,0 +1,128 @@
+package com.jayway.jsonpath;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.gson.JsonArray;
+import com.jayway.jsonpath.spi.json.GsonJsonProvider;
+import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider;
+import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
+import com.jayway.jsonpath.spi.json.JsonOrgJsonProvider;
+import com.jayway.jsonpath.spi.json.JsonSmartJsonProvider;
+import com.jayway.jsonpath.spi.mapper.GsonMappingProvider;
+import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
+import com.jayway.jsonpath.spi.mapper.JsonOrgMappingProvider;
+import com.jayway.jsonpath.spi.mapper.JsonSmartMappingProvider;
+import org.assertj.core.util.Lists;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class ProviderInTest {
+    private final String JSON = "[{\"foo\": \"bar\"}, {\"foo\": \"baz\"}]";
+    private final String EQUALS_FILTER = "$.[?(@.foo == %s)].foo";
+    private final String IN_FILTER = "$.[?(@.foo in [%s])].foo";
+    private final String DOUBLE_QUOTES = "\"bar\"";
+    private final String DOUBLE_QUOTES_EQUALS_FILTER = String.format(EQUALS_FILTER, DOUBLE_QUOTES);
+    private final String DOUBLE_QUOTES_IN_FILTER = String.format(IN_FILTER, DOUBLE_QUOTES);
+    private final String SINGLE_QUOTES = "'bar'";
+    private final String SINGLE_QUOTES_EQUALS_FILTER = String.format(EQUALS_FILTER, SINGLE_QUOTES);
+    private final String SINGLE_QUOTES_IN_FILTER = String.format(IN_FILTER, SINGLE_QUOTES);
+
+
+    @Rule
+    public final ExpectedException exception = ExpectedException.none();
+
+    @Test
+    public void testJsonPathQuotesJackson() throws Exception {
+        final Configuration jackson = Configuration.builder().jsonProvider(new JacksonJsonProvider()).mappingProvider(new JacksonMappingProvider()).build();
+        final DocumentContext ctx = JsonPath.using(jackson).parse(JSON);
+
+        final List<String> doubleQuoteEqualsResult = ctx.read(DOUBLE_QUOTES_EQUALS_FILTER);
+        assertEquals(Lists.newArrayList("bar"), doubleQuoteEqualsResult);
+
+        final List<String> singleQuoteEqualsResult = ctx.read(SINGLE_QUOTES_EQUALS_FILTER);
+        assertEquals(doubleQuoteEqualsResult, singleQuoteEqualsResult);
+
+        final List<String> doubleQuoteInResult = ctx.read(DOUBLE_QUOTES_IN_FILTER);
+        assertEquals(doubleQuoteInResult, doubleQuoteEqualsResult);
+
+        exception.expect(InvalidJsonException.class);
+        ctx.read(SINGLE_QUOTES_IN_FILTER);
+    }
+
+
+    @Test
+    public void testJsonPathQuotesJacksonJsonNode() throws Exception {
+        final Configuration jacksonJsonNode = Configuration.builder().jsonProvider(new JacksonJsonNodeJsonProvider()).mappingProvider(new JacksonMappingProvider()).build();
+        final DocumentContext ctx = JsonPath.using(jacksonJsonNode).parse(JSON);
+
+        final ArrayNode doubleQuoteEqualsResult = ctx.read(DOUBLE_QUOTES_EQUALS_FILTER);
+        assertEquals("bar", doubleQuoteEqualsResult.get(0).asText());
+
+        final ArrayNode singleQuoteEqualsResult = ctx.read(SINGLE_QUOTES_EQUALS_FILTER);
+        assertEquals(doubleQuoteEqualsResult, singleQuoteEqualsResult);
+
+        final ArrayNode doubleQuoteInResult = ctx.read(DOUBLE_QUOTES_IN_FILTER);
+        assertEquals(doubleQuoteInResult, doubleQuoteEqualsResult);
+
+        exception.expect(InvalidJsonException.class);
+        ctx.read(SINGLE_QUOTES_IN_FILTER);
+    }
+
+    @Test
+    public void testJsonPathQuotesGson() throws Exception {
+        final Configuration gson = Configuration.builder().jsonProvider(new GsonJsonProvider()).mappingProvider(new GsonMappingProvider()).build();
+        final DocumentContext ctx = JsonPath.using(gson).parse(JSON);
+
+        final JsonArray doubleQuoteEqualsResult = ctx.read(DOUBLE_QUOTES_EQUALS_FILTER);
+        assertEquals("bar", doubleQuoteEqualsResult.get(0).getAsString());
+
+        final JsonArray singleQuoteEqualsResult = ctx.read(SINGLE_QUOTES_EQUALS_FILTER);
+        assertEquals(doubleQuoteEqualsResult, singleQuoteEqualsResult);
+
+        final JsonArray doubleQuoteInResult = ctx.read(DOUBLE_QUOTES_IN_FILTER);
+        assertEquals(doubleQuoteInResult, doubleQuoteEqualsResult);
+
+        final JsonArray singleQuoteInResult = ctx.read(SINGLE_QUOTES_IN_FILTER);
+        assertEquals(doubleQuoteInResult, singleQuoteInResult);
+    }
+
+    @Test
+    public void testJsonPathQuotesJsonOrg() throws Exception {
+        final Configuration jsonOrg = Configuration.builder().jsonProvider(new JsonOrgJsonProvider()).mappingProvider(new JsonOrgMappingProvider()).build();
+        final DocumentContext ctx = JsonPath.using(jsonOrg).parse(JSON);
+
+        final org.json.JSONArray doubleQuoteEqualsResult = ctx.read(DOUBLE_QUOTES_EQUALS_FILTER);
+        assertEquals("bar", doubleQuoteEqualsResult.get(0));
+
+        final org.json.JSONArray singleQuoteEqualsResult = ctx.read(SINGLE_QUOTES_EQUALS_FILTER);
+        assertEquals(doubleQuoteEqualsResult.get(0), singleQuoteEqualsResult.get(0));
+
+        final org.json.JSONArray doubleQuoteInResult = ctx.read(DOUBLE_QUOTES_IN_FILTER);
+        assertEquals(doubleQuoteInResult.get(0), doubleQuoteEqualsResult.get(0));
+
+        final org.json.JSONArray singleQuoteInResult = ctx.read(SINGLE_QUOTES_IN_FILTER);
+        assertEquals(doubleQuoteInResult.get(0), singleQuoteInResult.get(0));
+    }
+
+    @Test
+    public void testJsonPathQuotesJsonSmart() throws Exception {
+        final Configuration jsonSmart = Configuration.builder().jsonProvider(new JsonSmartJsonProvider()).mappingProvider(new JsonSmartMappingProvider()).build();
+        final DocumentContext ctx = JsonPath.using(jsonSmart).parse(JSON);
+
+        final net.minidev.json.JSONArray doubleQuoteEqualsResult = ctx.read(DOUBLE_QUOTES_EQUALS_FILTER);
+        assertEquals("bar", doubleQuoteEqualsResult.get(0));
+
+        final net.minidev.json.JSONArray singleQuoteEqualsResult = ctx.read(SINGLE_QUOTES_EQUALS_FILTER);
+        assertEquals(doubleQuoteEqualsResult, singleQuoteEqualsResult);
+
+        final net.minidev.json.JSONArray doubleQuoteInResult = ctx.read(DOUBLE_QUOTES_IN_FILTER);
+        assertEquals(doubleQuoteInResult, doubleQuoteEqualsResult);
+
+        final net.minidev.json.JSONArray singleQuoteInResult = ctx.read(SINGLE_QUOTES_IN_FILTER);
+        assertEquals(doubleQuoteInResult, singleQuoteInResult);
+    }
+}
\ No newline at end of file
diff --git a/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java b/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java
index 93d3f8b2..04ffa00f 100644
--- a/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java
+++ b/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java
@@ -999,4 +999,24 @@ public class IssuesTest extends BaseTest {
         assertThat(objectNode.get("can delete").isNull());
         assertThat(objectNode.get("can't delete").isNull());
     }
+
+    @Test
+    public void issue_309(){
+
+        String json = "{\n" +
+                "\"jsonArr\": [\n" +
+                "   {\n" +
+                "       \"name\":\"nOne\"\n" +
+                "   },\n" +
+                "   {\n" +
+                "       \"name\":\"nTwo\"\n" +
+                "   }\n" +
+                "   ]\n" +
+                "}";
+
+        DocumentContext doc = JsonPath.parse(json).set("$.jsonArr[1].name", "Jayway");
+
+        assertThat(doc.read("$.jsonArr[0].name")).isEqualTo("nOne");
+        assertThat(doc.read("$.jsonArr[1].name")).isEqualTo("Jayway");
+    }
 }
diff --git a/json-path/src/test/java/com/jayway/jsonpath/old/internal/ArrayIndexFilterTest.java b/json-path/src/test/java/com/jayway/jsonpath/old/internal/ArrayIndexFilterTest.java
index 434775af..eaada166 100644
--- a/json-path/src/test/java/com/jayway/jsonpath/old/internal/ArrayIndexFilterTest.java
+++ b/json-path/src/test/java/com/jayway/jsonpath/old/internal/ArrayIndexFilterTest.java
@@ -1,6 +1,9 @@
 package com.jayway.jsonpath.old.internal;
 
 import com.jayway.jsonpath.JsonPath;
+
+import org.junit.Assert;
+
 import org.hamcrest.Matchers;
 import org.junit.Test;
 
@@ -43,4 +46,10 @@ public class ArrayIndexFilterTest {
         assertThat(result, Matchers.contains(1, 3, 5));
     }
 
+    @Test
+    public void can_access_items_from_end_with_negative_index() {
+        int result = JsonPath.parse(JSON).read("$[-3]");
+        Assert.assertEquals(8, result);
+    }
+
 }