Search before asking
- [x] I searched in the issues and found nothing similar.
Describe the bug
In Jackson 3.0.0, parsing a simple JSON Object, such as the following:
{
"key" : "value"
}
as a Map<String, String>
via parser.readValueAs(new TypeReference<Map<String, String>>() { })
fails with the following exception:
tools.jackson.databind.exc.MismatchedInputException: Unexpected end-of-input when trying read value of type `java.util.LinkedHashMap<java.lang.String,java.lang.String>`
at [Source: (String)"{
"key" : "value"
}
"; line: 1, column: 0]
at tools.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at tools.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1822)
at tools.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1618)
at tools.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1567)
at tools.jackson.databind.deser.jdk.MapDeserializer.deserialize(MapDeserializer.java:439)
at tools.jackson.databind.deser.jdk.MapDeserializer.deserialize(MapDeserializer.java:30)
at tools.jackson.databind.DeserializationContext._readValue(DeserializationContext.java:403)
at tools.jackson.databind.DeserializationContext.readValue(DeserializationContext.java:391)
at tools.jackson.databind.DeserializationContext.readValue(DeserializationContext.java:372)
at tools.jackson.core.base.ParserMinimalBase.readValueAs(ParserMinimalBase.java:815)
at Jackson3ParserTest.testJackson3Parser(Jackson3ParserTest.java:26)
It works with Jackson 2.20.0
Version Information
Does not work with Jackson 3.0.0 Works with Jackson 2.20.0
Reproduction
I have attached a simple project (jackson-parser-test.zip) with unit tests for Jackson 2 (works) and Jackson 3 (does not work).
The unit test that fails for Jackson 3 looks like this:
import org.junit.jupiter.api.Test;
import tools.jackson.core.JsonParser;
import tools.jackson.core.StreamReadFeature;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.json.JsonMapper;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class Jackson3ParserTest {
@Test
void testJackson3Parser() {
String jsonText = """
{
"key" : "value"
}
""";
JsonMapper jsonMapper = JsonMapper.builder()
.enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION)
.build();
try (JsonParser parser = jsonMapper.createParser(jsonText)) {
Map<String, String> result = parser.readValueAs(new TypeReference<Map<String, String>>() { });
assertEquals("value", result.get("key"));
}
}
}
Expected behavior
Expect the call to parser.readValueAs(new TypeReference<Map<String, String>>() { })
to not throw an exception, and return a Map<String, String>
object populated with the correct entries.
Additional context
No response
Comment From: philsttr
As a workaround for Jackson 3, if I change the code to explicitly advance the parser to the first token, it works ...
try (JsonParser parser = jsonMapper.createParser(jsonText)) {
// BEGIN workaround for https://github.com/FasterXML/jackson-databind/issues/5344
if (parser.currentToken() == null) {
parser.nextToken();
}
// END workaround
Map<String, String> result = parser.readValueAs(new TypeReference<Map<String, String>>() { });
assertEquals("value", result.get("key"));
}
In Jackson 2 this was not necessary, because Jackson 2 calls an _initForReading
method that advanced the parser to the first token automatically.
Comment From: philsttr
Another workaround is to call jsonMapper.readValue
instead...
try (JsonParser parser = jsonMapper.createParser(jsonText)) {
Map<String, String> result = jsonMapper.readValue(parser, new TypeReference<Map<String, String>>() { });
assertEquals("value", result.get("key"));
}
So it appears the bug only affects calls to parser.readValueAs(...)
Comment From: cowtowncoder
Hmmmh. This is a tricky one to reason about, I'll have to think about it. My first instinct is that it is a bug, but there may be some considerations on how changes affect other handling.
The reason for difference b/w JsonParser.readValueAs(type)
and ObjectMapper.readValue(JsonParser, type)
is that latter by-passes set up of DeserializationContext
whereas latter constructs a new one.
For what that is worth -- not something users are expected to know (or have to care about).
Comment From: cowtowncoder
This might be easiest to fix at JsonParser
level, actually, since ObjectReadContext.readValue(...)
expects properly positioned JsonParser
.
Will need to create matching issue on jackson-core
, set up test scaffolding (w/ test ObjectReadContext
).
Probably create tests on databind side too to verify functionality.
Comment From: cowtowncoder
Actually easy enough to address completely on databind side, see #5345
Comment From: philsttr
Thanks for the quick turn around, and congrats on the 3.0 release! The improvements are really nice.
Comment From: cowtowncoder
Thank you for reporting the issue!