Description
ProviderBase.readFrom() does not handle JSON array input when the entity type is MappingIterator. The method calls p.nextToken() which positions the parser at START_ARRAY, then passes it to reader.readValues(p). However, readValues(JsonParser) creates a MappingIterator with managedParser=false, which does not auto-skip START_ARRAY.
The ObjectReader.readValues(JsonParser) Javadoc explicitly states:
for wrapped sequences, parser MUST NOT point to the surrounding START_ARRAY but rather to the token following it.
This means JSON array input like [{"x":1}, {"x":2}] fails with a MismatchedInputException, while NDJSON input like {"x":1}\n{"x":2} works fine.
Steps to Reproduce
Create a Jakarta REST resource method with a MappingIterator parameter:
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public List<Point> process(MappingIterator<Point> points) {
List<Point> result = new ArrayList<>();
while (points.hasNext()) {
result.add(points.next());
}
return result;
}
POST a JSON array:
Content-Type: application/json
[{"x": 1, "y": 2}, {"x": 3, "y": 4}]
Expected: Successfully iterates over the two Point objects.
Actual: Throws MismatchedInputException: Cannot deserialize value of type Point from Array value (token START_ARRAY).
Root Cause
In ProviderBase.readFrom():
if (p == null || p.nextToken() == null) { // positions at START_ARRAY for array input
// ...
}
// ...
if (multiValued) {
return reader.readValues(p); // passes parser still at START_ARRAY
}
readValues(JsonParser) calls _newIterator(p, ctxt, deser, false) — the false means managedParser=false, so MappingIterator's constructor does not skip START_ARRAY:
if (managedParser && p.isExpectedStartArrayToken()) {
p.clearCurrentToken(); // only reached when managedParser=true
}
The existing test (testMappingIterator in SimpleEndpointTestBase) only sends NDJSON, so this path was never exercised.
Suggested Fix
Advance past START_ARRAY before calling readValues(p):
if (multiValued) {
if (p.currentToken() == JsonToken.START_ARRAY) {
p.nextToken();
}
return reader.readValues(p);
}
Description
ProviderBase.readFrom() does not handle JSON array input when the entity type is MappingIterator. The method calls p.nextToken() which positions the parser at START_ARRAY, then passes it to reader.readValues(p). However, readValues(JsonParser) creates a MappingIterator with managedParser=false, which does not auto-skip START_ARRAY.
The ObjectReader.readValues(JsonParser) Javadoc explicitly states:
This means JSON array input like
[{"x":1}, {"x":2}]fails with aMismatchedInputException, while NDJSON input like{"x":1}\n{"x":2}works fine.Steps to Reproduce
Create a Jakarta REST resource method with a
MappingIteratorparameter:Expected: Successfully iterates over the two Point objects.
Actual: Throws
MismatchedInputException: Cannot deserialize value of type Point from Array value (token START_ARRAY).Root Cause
In
ProviderBase.readFrom():readValues(JsonParser)calls_newIterator(p, ctxt, deser, false)— thefalsemeansmanagedParser=false, soMappingIterator's constructor does not skipSTART_ARRAY:The existing test (
testMappingIteratorinSimpleEndpointTestBase) only sends NDJSON, so this path was never exercised.Suggested Fix
Advance past
START_ARRAYbefore callingreadValues(p):