DeserializationProblemHandler.handleUnexpectedToken() no longer invoked for array-like types (original) (raw)
Describe the bug
Before 2.12.x DeserializationProblemHandler::handleUnexpectedToken is invoked when trying to deserialize something with a structurally incompatible type, like deserializing a string from a START_OBJECT. In 2.12.x this no longer happens if the targeted type is an array-like type, such as an Iterable or a Collection. Instead the following exception is thrown: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `java.util.ArrayList` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('prop'), it appears that DeserializationProblemHandler::handleInstantiationProblem is invoked instead.
Version information
2.12.x
2.13.x
To Reproduce
pom.xml
4.0.0 org.example jackson-mve 1.0-SNAPSHOT jar UTF-8 1.8 1.8 <!-- This and 2.13.0 exhibit the issue -->
<jackson.version>2.12.0</jackson.version>
<!-- This is the last version where the test passes -->
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
</plugin>
</plugins>
</build>
Test sample
package org.example.mve;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Objects;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.node.ObjectNode;
class ExceptionMappingProblemHandlerTest {
private static final String PROP = "prop";
private static final ObjectMapper MAPPER;
static {
MAPPER = new ObjectMapper();
MAPPER.addHandler(new ExceptionMappingProblemHandler());
}
@Test
void testHandleUnexpectedToken() {
ObjectNode input = MAPPER.createObjectNode();
input.set(PROP, MAPPER.createArrayNode());
assertThrows(
JsonStructuralMismatch.class,
() -> MAPPER.treeToValue(input, StringProperty.class)
);
}
@Test
void testHandleUnexpectedTokenArray() {
ObjectNode input = MAPPER.createObjectNode();
input.put(PROP, "prop");
assertThrows(
JsonStructuralMismatch.class,
() -> MAPPER.treeToValue(input, Array.class)
);
}
static class Array {
private final Collection<String> prop;
private Array(Collection<String> prop) {
this.prop = prop;
}
@JsonCreator
static Array create(@JsonProperty(PROP) Iterable<String> prop) {
ArrayList<String> list = new ArrayList<>();
prop.forEach(list::add);
return new Array(list);
}
@JsonProperty(PROP)
public Iterable<String> getProp() {
return prop;
}
}
static class StringProperty {
private final String prop;
private StringProperty(String prop) {
this.prop = Objects.requireNonNull(prop, "prop must not be null");
}
@JsonCreator
static StringProperty create(@JsonProperty(PROP) String prop) {
return new StringProperty(prop);
}
@JsonProperty(PROP)
public String getProp() {
return prop;
}
}
public static final class ExceptionMappingProblemHandler extends DeserializationProblemHandler {
@Override
public Object handleUnexpectedToken(
DeserializationContext ctx,
Class<?> targetType,
JsonToken token,
JsonParser parser,
String failureMsg
) throws IOException {
throw new JsonStructuralMismatch(parser, "Some text here");
}
@Override
public Object handleUnexpectedToken(
DeserializationContext ctxt,
JavaType targetType,
JsonToken t,
JsonParser parser,
String failureMsg
) throws IOException {
throw new JsonStructuralMismatch(parser, "Some text here");
}
}
public static class JsonStructuralMismatch extends MismatchedInputException {
JsonStructuralMismatch(JsonParser parser, String description) {
super(parser, description);
}
}}
Expected behavior
In the above test case, a JsonStructuralMismatch should be thrown for the array case as well.