diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java index b72f7ab5af..ae6b3ad0cf 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java @@ -19,6 +19,7 @@ public class ThrowableDeserializer private static final long serialVersionUID = 1L; protected final static String PROP_NAME_MESSAGE = "message"; + protected final static String PROP_NAME_SUPPRESSED = "suppressed"; /* /************************************************************ @@ -82,6 +83,7 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t Object throwable = null; Object[] pending = null; + Throwable[] suppressed = null; int pendingIx = 0; for (; !p.hasToken(JsonToken.END_OBJECT); p.nextToken()) { @@ -120,6 +122,14 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t continue; } } + + // Maybe it's "suppressed"? + final boolean isSuppressed = PROP_NAME_SUPPRESSED.equals(propName); + if (isSuppressed) { + suppressed = p.readValueAs(Throwable[].class); + continue; + } + // Things marked as ignorable should not be passed to any setter if ((_ignorableProps != null) && _ignorableProps.contains(propName)) { p.skipChildren(); @@ -156,6 +166,14 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t } } } + + if (suppressed != null && throwable instanceof Throwable) { + Throwable t = (Throwable) throwable; + for (Throwable s : suppressed) { + t.addSuppressed(s); + } + } + return throwable; } } diff --git a/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java index b5db9255d3..4ea7793b3b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/exc/ExceptionDeserializationTest.java @@ -6,6 +6,9 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator; +import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; /** * Unit tests for verifying that simple exceptions can be deserialized. @@ -130,6 +133,47 @@ public void testExceptionCauseDeserialization() throws IOException _assertEquality(exp.getCause().getStackTrace(), act.getCause().getStackTrace()); } + + public void testSuppressedGenericThrowableDeserialization() throws IOException + { + ObjectMapper mapper = new ObjectMapper(); + + final IOException exp = new IOException("the outer exception"); + exp.addSuppressed(new Throwable("the suppressed exception")); + + final String value = mapper.writeValueAsString(exp); + final IOException act = mapper.readValue(value, IOException.class); + + assertNotNull(act.getSuppressed()); + assertEquals(1, act.getSuppressed().length); + assertEquals(exp.getSuppressed()[0].getMessage(), act.getSuppressed()[0].getMessage()); + _assertEquality(exp.getSuppressed()[0].getStackTrace(), act.getSuppressed()[0].getStackTrace()); + } + + public void testSuppressedTypedExceptionDeserialization() throws IOException + { + PolymorphicTypeValidator typeValidator = BasicPolymorphicTypeValidator.builder() + .allowIfSubTypeIsArray() + .allowIfSubType(Throwable.class) + .build(); + + ObjectMapper mapper = JsonMapper.builder() + .activateDefaultTyping(typeValidator, ObjectMapper.DefaultTyping.NON_FINAL) + .build(); + + final IOException exp = new IOException("the outer exception"); + exp.addSuppressed(new IllegalArgumentException("the suppressed exception")); + + final String value = mapper.writeValueAsString(exp); + final IOException act = mapper.readValue(value, IOException.class); + + assertNotNull(act.getSuppressed()); + assertEquals(1, act.getSuppressed().length); + assertEquals(IllegalArgumentException.class, act.getSuppressed()[0].getClass()); + assertEquals(exp.getSuppressed()[0].getMessage(), act.getSuppressed()[0].getMessage()); + _assertEquality(exp.getSuppressed()[0].getStackTrace(), act.getSuppressed()[0].getStackTrace()); + } + private void _assertEquality(StackTraceElement[] exp, StackTraceElement[] act) { assertEquals(exp.length, act.length); for (int i = 0; i < exp.length; i++) {