Skip to content

Commit 77b3648

Browse files
authored
Fix #4603 (Keep stacktrace when re-throwing exception with JsonMappingException) (#5054)
1 parent d4977d3 commit 77b3648

File tree

5 files changed

+109
-5
lines changed

5 files changed

+109
-5
lines changed

release-notes/VERSION

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ Versions: 3.x (for earlier see VERSION-2.x)
77

88
3.0.0-rc3 (not yet released)
99

10+
#4603: Keep full stacktrace when re-throwing exception with
11+
`DatabindException` during deserialization
12+
(fixed by Joo-Hyuk K)
1013
#5065: Change default for `DateTimeFeature.ONE_BASED_MONTHS` to `true` in 3.0
1114
#5066: Move date/time `SerializationFeature`s into `DateTimeFeature` (3.0)
1215
#5067: Move date/time `DeserializationFeature`s into `DateTimeFeature` (3.0)

src/main/java/tools/jackson/databind/deser/SettableAnyProperty.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ public void set(DeserializationContext ctxt, Object instance, Object propName, O
251251
}
252252
}
253253

254-
protected abstract void _set(DeserializationContext ctxt, Object instance, Object propName, Object value) throws Exception;
254+
protected abstract void _set(DeserializationContext ctxt, Object instance, Object propName, Object value)
255+
throws Exception;
255256

256257
/*
257258
/**********************************************************************

src/main/java/tools/jackson/databind/deser/SettableBeanProperty.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package tools.jackson.databind.deser;
22

33
import java.lang.annotation.Annotation;
4+
import java.lang.reflect.InvocationTargetException;
45

56
import tools.jackson.core.*;
67
import tools.jackson.core.util.InternCache;
@@ -636,9 +637,18 @@ protected void _throwAsJacksonE(JsonParser p, Exception e) throws JacksonExcepti
636637
{
637638
ClassUtil.throwIfRTE(e);
638639
ClassUtil.throwIfJacksonE(e);
639-
// let's wrap the innermost problem
640-
Throwable th = ClassUtil.getRootCause(e);
641-
throw DatabindException.from(p, ClassUtil.exceptionMessage(th), th);
640+
641+
// 10-Apr-2025: [databind#4603] no more unwrapping, retain exception
642+
// Throwable th = ClassUtil.getRootCause(e);
643+
// ... except for InvocationTargetException which we still unwrap as it
644+
// adds no value
645+
if (e instanceof InvocationTargetException ite) {
646+
Throwable t = ite.getTargetException();
647+
ClassUtil.throwIfRTE(t);
648+
ClassUtil.throwIfJacksonE(t);
649+
throw DatabindException.from(p, ClassUtil.exceptionMessage(t), t);
650+
}
651+
throw DatabindException.from(p, ClassUtil.exceptionMessage(e), e);
642652
}
643653

644654
// 10-Oct-2015, tatu: _Should_ be deprecated, too, but its remaining
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package tools.jackson.databind.exc;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
import tools.jackson.core.JacksonException;
9+
import tools.jackson.databind.*;
10+
import tools.jackson.databind.testutil.DatabindTestUtil;
11+
12+
import com.fasterxml.jackson.annotation.*;
13+
14+
import static org.junit.jupiter.api.Assertions.*;
15+
16+
// [databind#4603]: Stop unwrapping root cause
17+
public class UnwrapRootCause4603Test
18+
extends DatabindTestUtil
19+
{
20+
@SuppressWarnings("serial")
21+
static class CustomException extends RuntimeException {
22+
public CustomException(String message) {
23+
super(message);
24+
}
25+
}
26+
27+
static class Feature1347DeserBean
28+
{
29+
int value;
30+
31+
public void setValue(int x) {
32+
throw new CustomException("setValue, fail on purpose");
33+
}
34+
}
35+
36+
static class AnySetterBean
37+
{
38+
protected Map<String, Integer> props = new HashMap<String, Integer>();
39+
40+
@JsonAnySetter
41+
public void prop(String name, Integer value) {
42+
throw new CustomException("@JsonAnySetter, fail on purpose");
43+
}
44+
}
45+
46+
static class Baz {
47+
private String qux;
48+
49+
@JsonCreator
50+
public Baz(@JsonProperty("qux") String qux) {
51+
this.qux = qux;
52+
}
53+
54+
public String getQux() {
55+
return qux;
56+
}
57+
}
58+
59+
private static final String JSON = a2q("{'value':3}");
60+
61+
private final ObjectMapper MAPPER = newJsonMapper();
62+
63+
// Whether disabled or enabled, should get ArithmeticException
64+
@Test
65+
public void testExceptionWrappingConfiguration()
66+
throws Exception
67+
{
68+
JacksonException disabledResult = _tryDeserializeWith(MAPPER);
69+
// !!! TODO: ought to be DatabindException
70+
assertInstanceOf(JacksonException.class, disabledResult);
71+
// We are throwing exception inside a setter, but InvocationTargetException
72+
// will still be unwrapped
73+
assertInstanceOf(CustomException.class, disabledResult.getCause());
74+
}
75+
76+
private JacksonException _tryDeserializeWith(ObjectMapper mapper) {
77+
return assertThrows(JacksonException.class,
78+
() -> mapper.readValue(JSON, Feature1347DeserBean.class)
79+
);
80+
}
81+
82+
@Test
83+
public void testWithAnySetter()
84+
throws Exception
85+
{
86+
DatabindException result = assertThrows(DatabindException.class,
87+
() -> MAPPER.readValue(a2q("{'a':3}"), AnySetterBean.class));
88+
assertInstanceOf(CustomException.class, result.getCause());
89+
}
90+
}

src/test/java/tools/jackson/databind/introspect/TestAutoDetect.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ public void testVisibilityConfigOverridesForDeser() throws Exception
182182
/*Feature1347DeserBean bean =*/
183183
MAPPER.readValue(JSON, Feature1347DeserBean.class);
184184
fail("Should not pass");
185-
} catch (DatabindException e) { // should probably be something more specific but...
185+
} catch (JacksonException e) { // should probably be something more specific but...
186186
verifyException(e, "Should NOT get called");
187187
}
188188

0 commit comments

Comments
 (0)