XmlPath: a negative list index on a hyphenated element name throws a misleading parameter "..." was used but not defined error
Description
XmlPath auto-escapes hyphenated element names so they need no manual quoting (the feature that closed #74/#105/#564), and it supports negative from-the-end list indexes such as records.car[-1]. Each works on its own. Combining them throws.
XML: <root><some-list>one</some-list><some-list>two</some-list><some-list>three</some-list></root>
getString("root.some-list[-1]") -> IllegalArgumentException:
The parameter "list" was used but not defined.
Define parameters using the XmlPath.params(...) function (should be "three")
getString("root.some-list[-2]") -> same exception (should be "two")The caller never used a parameter and never called params(...). The identifier list is the tail of the element name some-list; the MissingPropertyException handler turns it into a red herring about params(...).
rest-assured version
- Reproduced on released
io.rest-assured:xml-path:5.5.6(Maven Central) and onmasterHEADeeed4998(xml-path 6.0.2-SNAPSHOT). Present in both. - Java 24 (Temurin 24.0.1).
Steps to reproduce
import io.restassured.path.xml.XmlPath;
public class Repro {
static String xml = "<root><some-list>one</some-list><some-list>two</some-list><some-list>three</some-list></root>";
static String get(String p) {
try { return new XmlPath(xml).getString(p); }
catch (Throwable t) { return "EXC " + t.getClass().getSimpleName() + ": " + t.getMessage(); }
}
public static void main(String[] a) {
System.out.println("some-list[-1] = " + get("root.some-list[-1]")); // EXC (should be three)
System.out.println("some-list[-2] = " + get("root.some-list[-2]")); // EXC (should be two)
System.out.println("some-list[0] = " + get("root.some-list[0]")); // one (positive: OK)
System.out.println("'some-list'[-1] = " + get("root.'some-list'[-1]")); // three (manual-quote workaround)
}
}Output. Each feature works in isolation; the hyphen plus negative-index intersection throws:
some-list[-1] = EXC IllegalArgumentException: The parameter "list" was used but not defined. ...
some-list[-2] = EXC IllegalArgumentException: The parameter "list" was used but not defined. ...
some-list[0] = one
'some-list'[-1] = threeControl with a non-hyphenated name: <root><item>one</item>...<item>three</item></root> gives root.item[-1] = "three", so the negative index itself works. Workaround: quote the name by hand, root.'some-list'[-1].
Expected
root.some-list[-1] = "three", root.some-list[-2] = "two" (matching /root/some-list[last()] / [last()-1] from a standard XPath oracle, and consistent with both features being documented and tested).
Actual
IllegalArgumentException: The parameter "list" was used but not defined. A documented feature combination throws, and the message blames a params(...) parameter the caller never used.
Root cause
rest-assured-common/src/main/groovy/io/restassured/internal/common/assertion/AssertionSupport.groovy:76, the hyphen() escaper's guard:
boolean shouldEscape(String pathFragment) {
!pathFragment.startsWith("'") && !pathFragment.endsWith("'") && pathFragment.contains('-') &&
!pathFragment.contains('[-') && // <-- this clause
!containsAny(pathFragment, [closureStartFragment, closureEndFragment, listGetterFragment])
}For the fragment some-list[-1]: it contains -, so the escaper would rewrite it to 'some-list'[-1], except it also contains the substring [- (from the [-1] index), so shouldEscape returns false and escaping is skipped. The unescaped some-list[-1] reaches the Groovy GPath evaluator, which parses some-list as the subtraction some - list; list is an undefined binding, producing MissingPropertyException(property = "list"), which XMLAssertion.groovy reformats into the misleading params(...) message. The escaper's own escape() method splits before [ and would produce the correct 'some-list'[-1]; the shouldEscape gate alone opts out.
Suggested fix
The [- clause keeps JsonPath's bare leading negative index ([-1].email) out of the escaper, so keep it in spirit and narrow it to fire only when the hyphen sits in the element-name portion before [:
def nameHead = org.apache.commons.lang3.StringUtils.substringBefore(pathFragment, '[')
// ... && nameHead.contains('-') && !containsAny(...) // replaces `contains('-') && !contains('[-')`This escapes some-list[-1] to 'some-list'[-1] (returning three) while leaving bare [-1] and non-hyphen item[-1] to GPath's native negative-index handling. Add a regression test for a hyphenated name with a negative index.
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.
Source: rest-assured/rest-assured