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
A two-bound slice [from:to] never normalizes a negative from. The per-index handler downstream does normalize negatives, so the slice wraps to the end of the array and then continues from the start, returning out-of-order and duplicated elements. Single-bound slices ([from:] and [:to]) normalize correctly; only the two-bound form breaks.
Steps to reproduce
import com.jayway.jsonpath.*;
String json = "[0,1,2,3,4]";
net.minidev.json.JSONArray r = JsonPath.read(json, "$[-2:4]");
System.out.println(r);
Observed output
$[-2:4] => [3,4,0,1,2,3]
$[-2:] => [3,4] (single-bound, correct)
$[:4] => [0,1,2,3] (single-bound, correct)
$[-3:2] => [2,3,4,0,1] (two-bound, also wraps)
Expected
[3]. Standard slice semantics select index 3 only for [-2:4].
Actual
[3,4,0,1,2,3]. The negative from wraps to the end (3,4), then the loop continues 0,1,2,3, duplicating 3.
Root cause
json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceToken.java, sliceBetween (line 67):
67: private void sliceBetween(...) {
68: int length = ctx.jsonProvider().length(model);
69: int from = operation.from();
70: int to = operation.to();
71:
72: to = Math.min(length, to); // only `to` is handled
73:
74: if (from >= to || length == 0) { return; }
...
80: for (int i = from; i < to; i++) { // i starts negative
81: handleArrayIndex(i, currentPath, model, ctx); // maps i<0 to length+i
82: }
83: }
from is never normalized or clamped. sliceFrom (lines 48–55) does from = length + from; from = Math.max(0, from);, but sliceBetween skips that. With a negative from, the loop counter i starts negative and handleArrayIndex (PathToken.java:137) maps each negative i to length + i, emitting the tail elements before the loop reaches index 0.
Suggested fix
Normalize and clamp from before the guard, mirroring sliceFrom:
if (from < 0) {
from = length + from;
}
from = Math.max(0, from);
placed before the from >= to check.
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.
Version / environment
master@ HEAD (line numbers verified against source); latest release json-path 3.0.0 hits the same code path.Description
A two-bound slice
[from:to]never normalizes a negativefrom. The per-index handler downstream does normalize negatives, so the slice wraps to the end of the array and then continues from the start, returning out-of-order and duplicated elements. Single-bound slices ([from:]and[:to]) normalize correctly; only the two-bound form breaks.Steps to reproduce
Observed output
Expected
[3]. Standard slice semantics select index 3 only for[-2:4].Actual
[3,4,0,1,2,3]. The negativefromwraps to the end (3,4), then the loop continues0,1,2,3, duplicating3.Root cause
json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceToken.java,sliceBetween(line 67):fromis never normalized or clamped.sliceFrom(lines 48–55) doesfrom = length + from; from = Math.max(0, from);, butsliceBetweenskips that. With a negativefrom, the loop counteristarts negative andhandleArrayIndex(PathToken.java:137) maps each negativeitolength + i, emitting the tail elements before the loop reaches index 0.Suggested fix
Normalize and clamp
frombefore the guard, mirroringsliceFrom:placed before the
from >= tocheck.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.