`MapDeserializer` forcing `JsonMappingException` wrapping even if WRAP_EXCEPTIONS set to false
See original GitHub issueDescribe the bug I am upgrading a project from 1.9.13 to 2.12.1. The WRAP_EXCEPTION feature was added in since that as we have a custom exception now being wrapped.
The project adds a Deserializer and overrides deserialize where it throws a custom exception. We do not want this exception to be wrapped but it now gets wrapped as a JsonMappingException. Even setting WRAP_EXCEPTION to false it still wraps the exception.
The code calls wrapAndThrow and then wraps the exception ContainerDeserializerBase.
Then when handled as its already been wrapped by a JsonMappingException
it is a instance of IOException
so it will not unwrap the exception in the code BeanDeserializerBase.
Should we be able to get the underlying exception here or is this by design to force this wrapping?
Version information 2.12.1
To Reproduce
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
public class JacksonIssue {
private static final ObjectMapper OBJECT_MAPPER = getObjectMapper();
private static ObjectMapper getObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.WRAP_EXCEPTIONS, false);
mapper.configure(SerializationFeature.WRAP_EXCEPTIONS, false);
SimpleModule module = new SimpleModule("SimpleModule", new Version(1, 0, 0, ""));
module.addDeserializer(MyContainerModel.class, new JsonDeserializer<MyContainerModel>() {
@Override
public MyContainerModel deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
throw new CustomException("This gets wrapped");
}
});
mapper.addMixIn(MyJobModel.class, MyJsonJobModelMixIn.class);
mapper.addMixIn(MyContainerModel.class, MyJsonContainerModelMixIn.class);
mapper.registerModule(module);
return mapper;
}
@Test
void wrapExceptionIgnored() throws JsonProcessingException {
ObjectNode jobModelJson = buildJobModelJson();
ObjectNode containerModelJson = (ObjectNode) jobModelJson.get("containers").get("1");
containerModelJson.remove("processor-id");
String jsonString = new ObjectMapper().writeValueAsString(jobModelJson);
Assertions.assertThrows(CustomException.class, () -> {
OBJECT_MAPPER.readValue(jsonString, MyJobModel.class);
});
}
private static ObjectNode buildJobModelJson() {
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode model = objectMapper.createObjectNode();
model.put("system", "foo");
model.put("stream", "bar");
model.put("partition", 1);
ArrayNode containerModel1TaskTestSSPsJson = objectMapper.createArrayNode();
containerModel1TaskTestSSPsJson.add(model);
ObjectNode model2 = objectMapper.createObjectNode();
model2.put("task-name", "test");
model2.put("changelog-partition", 2);
model2.put("task-mode", "Active");
ObjectNode containerModel1TasksJson = objectMapper.createObjectNode();
containerModel1TasksJson.put("test", model);
ObjectNode containerModel1Json = objectMapper.createObjectNode();
containerModel1Json.put("processor-id", "1");
containerModel1Json.put("tasks", containerModel1TasksJson);
ObjectNode containersJson = objectMapper.createObjectNode();
containersJson.put("1", containerModel1Json);
ObjectNode jobModelJson = objectMapper.createObjectNode();
jobModelJson.put("containers", containersJson);
return jobModelJson;
}
public abstract class MyJsonContainerModelMixIn {
static final String PROCESSOR_ID_KEY = "processor-id";
static final String CONTAINER_ID_KEY = "container-id";
static final String TASKS_KEY = "tasks";
public MyJsonContainerModelMixIn() {
}
@JsonProperty("processor-id")
abstract String getId();
}
@JsonIgnoreProperties(
ignoreUnknown = true
)
public abstract class MyJsonJobModelMixIn {
@JsonCreator
public MyJsonJobModelMixIn(@JsonProperty("containers") Map<String, MyContainerModel> containers) {
}
@JsonProperty("containers")
abstract Map<String, MyContainerModel> getContainers();
}
public static class MyContainerModel {
private final String id;
public MyContainerModel(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
}
public static class MyJobModel {
private final Map<String, MyContainerModel> containers;
public int maxChangeLogStreamPartitions;
@JsonCreator
public MyJobModel(Map<String, MyContainerModel> containers) {
this.containers = Collections.unmodifiableMap(containers);
this.maxChangeLogStreamPartitions = 0;
}
public Map<String, MyContainerModel> getContainers() {
return this.containers;
}
}
}
public class CustomException extends RuntimeException {
private static final long serialVersionUID = 1L;
public CustomException() {
super();
}
public CustomException(String s, Throwable t) {
super(s, t);
}
public CustomException(String s) {
super(s);
}
public CustomException(Throwable t) {
super(t);
}
}
Expected behavior <jacksonissue.CustomException> but was: <com.fasterxml.jackson.databind.JsonMappingException>
Additional context This is due to trying to upgrade Apache Samza from a very old Jackson version where they throw an exception like the case above referenced here
Issue Analytics
- State:
- Created 3 years ago
- Comments:5 (3 by maintainers)
Top GitHub Comments
Was straight-forward to fix and I think likelihood of regression is low (affects regular Map and EnumMap deserializers), so including it in 2.12 branch for upcoming 2.12.2 patch.
I have tried to create a toy example which hits WrapsAndThrows in ContainerDeserializerBase.