#1078·JsonPath

Single-quoted string arguments to path functions are dropped or corrupted; only double quotes work

Author: haskiindahouseCreated Jul 21, 2026Updated Jul 21, 2026

Version / environment

  • json-path master @ HEAD (line numbers verified against source); latest release json-path 3.0.0 hits the same code path.
  • JVM: any (Java 8+). Default JSON provider.

Description

JsonPath accepts single-quoted string literals in filter predicates, e.g. $[?(@.name == 'foo')]. Inside function argument lists the compiler recognizes only double quotes. A single-quoted string argument never gets typed as a JSON string parameter: as a standalone argument it yields an empty result, and when mixed with path arguments the quote characters leak into the output.

Steps to reproduce

java
import com.jayway.jsonpath.*;

// Standalone string arg: double quotes work, single quotes vanish
JsonPath.read("{}", "$.concat(\"hello\")"); // "hello"
JsonPath.read("{}", "$.concat('hello')");   // ""  (argument dropped)

// Mixed with path args on {"x":"P","y":"Q"}: the single quote leaks in
String doc = "{\"x\":\"P\",\"y\":\"Q\"}";
JsonPath.read(doc, "$.concat($.x,\"-\",$.y)"); // "P-Q"   (correct)
JsonPath.read(doc, "$.concat($.x,'-',$.y)");   // "P-'Q"  (corrupted)

Observed output

$.concat("hello")        => [hello]
$.concat('hello')        => []
$.concat($.x,"-",$.y)    => P-Q
$.concat($.x,'-',$.y)    => P-'Q

Expected

Single-quoted string arguments behave like double-quoted ones, matching filter-predicate string handling: 'hello' is the string hello, and '-' is the string -.

Actual

A single-quoted standalone argument produces an empty string. A single-quoted argument alongside path arguments keeps its quote characters, yielding P-'Q instead of P-Q.

Root cause

json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java, the function-parameter reader.

Parameter-type detection (line 308) recognizes only DOUBLE_QUOTE, so a leading single quote never sets the parameter type to JSON:

java
308:  if (c == OPEN_BRACE || isDigit(c) || DOUBLE_QUOTE == c || MINUS == c) {
309:      type = ParamType.JSON;
310:  }

The quote-grouping switch (lines 317–324) adjusts groupQuote for DOUBLE_QUOTE alone:

java
317:  case DOUBLE_QUOTE:
318:      if (priorChar != '\\' && groupQuote > 0) { groupQuote--; }
319:      else { groupQuote++; }
320:      break;

SINGLE_QUOTE is defined at line 42 ('\'') and used elsewhere in the compiler, but neither the type check nor the grouping switch references it.

Suggested fix

Handle SINGLE_QUOTE alongside DOUBLE_QUOTE in both places:

  • add || SINGLE_QUOTE == c to the type check at line 308, and
  • add a case SINGLE_QUOTE: to the grouping switch that adjusts groupQuote the same way the DOUBLE_QUOTE case does.

Found via property-based & differential bug-hunting, part of an effort to scale PBT (DepTyCheck-based) testing across the OSS ecosystem.

If this is intended / by-design: I'm really sorry, please just close it — no need to flag or ban me. I'm trying to scale property-based testing across the whole ecosystem and my publishing agents may have gotten this one wrong. I read every issue and follow up on each.