-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Issue with deserialization when there are unexpected properties (due to null StreamReadConstraints
)
#3913
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
this is best moved to https://github.com/FasterXML/jackson-module-kotlin/ - noone in the team that maintains the core Jackson modules uses Kotlin |
I detected it in a kotlin project but had the impression that I was in the core stack all the way. Will try to reproduce with java class hierarchy. |
Same behaviour in java. I am not even attaching the Kotlin module, nor am I using fancy java 17 stuff like records or sealed interface. package jackson;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
class JacksonBugTest {
private ObjectMapper objectMapper() {
ObjectMapper om = new ObjectMapper();
om.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
return om;
}
@Test
void thisFails() {
String rawResponse = "{\"list\":[{\"type\":\"impl\",\"unmappedKey\":\"unusedValue\"}]}";
Assertions.assertThrows(JsonMappingException.class, () ->
objectMapper().readValue(rawResponse, MyResponse.class));
}
@Test
void isOkIfNoMissingFieldInJson() throws JsonProcessingException {
String rawResponse = "{\"list\":[{\"type\":\"impl\",\"missingInJson\":\"not missing\",\"unmappedKey\":\"unusedValue\"}]}";
MyResponse marshalled = objectMapper().readValue(rawResponse, MyResponse.class);
assertEquals("impl", marshalled.getList().get(0).getType());
assertEquals("not missing", marshalled.getList().get(0).getMissingInJson());
}
@Test
void isOkIfNoExtraFieldInJson() throws JsonProcessingException {
String rawResponse = "{\"list\":[{\"type\":\"impl\"}]}";
MyResponse marshalled = objectMapper().readValue(rawResponse, MyResponse.class);
assertEquals("impl", marshalled.getList().get(0).getType());
assertNull(marshalled.getList().get(0).getMissingInJson());
}
@Test
void isOkIfNoClassHierarchy() throws JsonProcessingException {
String rawResponse = "{\"list\":[{\"type\":\"impl\"}]}";
MyResponseWithoutHierarchy marshalled = objectMapper().readValue(rawResponse, MyResponseWithoutHierarchy.class);
assertEquals("impl", marshalled.getList().get(0).getType());
assertNull(marshalled.getList().get(0).getMissingInJson());
}
}
class MyResponse {
private List<Base> list;
public List<Base> getList() {
return list;
}
public void setList(List<Base> list) {
this.list = list;
}
}
class MyResponseWithoutHierarchy {
private List<Impl> list;
public List<Impl> getList() {
return list;
}
public void setList(List<Impl> list) {
this.list = list;
}
}
interface Base {
String getType();
String getMissingInJson();
@JsonCreator
static Base unmarshall(
@JsonProperty("missingInJson") String missingInJson,
@JsonProperty("type") String type
) {
return switch (type) {
case "impl" -> new Impl(type, missingInJson);
default -> null;
};
}
}
final class Impl implements Base {
private String type;
private String missingInJson;
public Impl() {
}
public Impl(String type, String missingInJson) {
this.type = type;
this.missingInJson = missingInJson;
}
@Override public String getType() {
return type;
}
@Override public String getMissingInJson() {
return missingInJson;
}
public void setType(String type) {
this.type = type;
}
public void setMissingInJson(String missingInJson) {
this.missingInJson = missingInJson;
}
} |
@cowtowncoder can you move this to jackson-databind? I have a PR there - #3911 @sbertault thanks for the Java test case |
StreamReadConstraints
)
Better fix for #922 - root cause of issue appears to be a bug specific to Jackson 2.15.0 at FasterXML/jackson-databind#3913
Hello,
We encountered a weird behaviour with jackson-core 2.15.0 that was not present in version 2.14.2 :
Disclaimer : this is a corner case (but still annoying ;))
When trying to unmarshall the raw json
{"list":[{"type":"impl","unmappedKey":"unusedValue"}]}
with "type" being the discriminator for a sealed class, I get this error
com.fasterxml.jackson.databind.JsonMappingException: Cannot invoke "com.fasterxml.jackson.core.JsonParser.streamReadConstraints()" because "p" is null (through reference chain: com.orange.ccmd.connector.MyResponse["list"]->java.util.ArrayList[0])
In the debugger, there is no trace of a variable
p
being null though.This happens only when there is all three conditions:
Here is a complete working unit test showing the bug, with the stacktrace :
Thanks for the invaluable work
The text was updated successfully, but these errors were encountered: