Skip to content

Commit d624fc6

Browse files
authored
Fix NPE when deserializing @JsonAnySetter field in Throwable (#4325)
1 parent 6b738ac commit d624fc6

File tree

3 files changed

+69
-16
lines changed

3 files changed

+69
-16
lines changed

release-notes/VERSION-2.x

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ Project: jackson-databind
1414
#4303: `ObjectReader` is not serializable if it's configured for polymorphism
1515
(reported by @asardaes)
1616
(fix contributed by Joo-Hyuk K)
17+
#4316: NPE when deserializing `JsonAnySetter` in `Throwable`
18+
(reported by @jpraet)
19+
(fix contributed by Joo-Hyuk K)
1720
1821
2.16.1 (24-Dec-2023)
1922

src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,8 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t
129129
// should ideally mangle property names. But for now let's cheat; works
130130
// for case-changing although not for kebab/snake cases and "localizedMessage"
131131
if (PROP_NAME_MESSAGE.equalsIgnoreCase(propName)) {
132-
if (hasStringCreator) {
133-
throwable = (Throwable) _valueInstantiator.createFromString(ctxt, p.getValueAsString());
134-
continue;
135-
}
136-
// fall through
132+
throwable = _instantiate(ctxt, hasStringCreator, p.getValueAsString());
133+
continue;
137134
}
138135

139136
// Things marked as ignorable should not be passed to any setter
@@ -161,22 +158,13 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t
161158
p.skipChildren();
162159
continue;
163160
}
161+
164162
// Unknown: let's call handler method
165163
handleUnknownProperty(p, ctxt, throwable, propName);
166164
}
167165
// Sanity check: did we find "message"?
168166
if (throwable == null) {
169-
/* 15-Oct-2010, tatu: Can't assume missing message is an error, since it may be
170-
* suppressed during serialization.
171-
*
172-
* Should probably allow use of default constructor, too...
173-
*/
174-
//throw new XxxException("No 'message' property found: could not deserialize "+_beanType);
175-
if (hasStringCreator) {
176-
throwable = (Throwable) _valueInstantiator.createFromString(ctxt, null);
177-
} else {
178-
throwable = (Throwable) _valueInstantiator.createUsingDefault(ctxt);
179-
}
167+
throwable = _instantiate(ctxt, hasStringCreator, null);
180168
}
181169

182170
// any pending values?
@@ -196,4 +184,35 @@ public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) t
196184

197185
return throwable;
198186
}
187+
188+
/*
189+
/**********************************************************
190+
/* Internal helper methods
191+
/**********************************************************
192+
*/
193+
194+
/**
195+
* Helper method to initialize Throwable
196+
*
197+
* @since 2.17
198+
*/
199+
private Throwable _instantiate(DeserializationContext ctxt, boolean hasStringCreator, String valueAsString)
200+
throws IOException
201+
{
202+
/* 15-Oct-2010, tatu: Can't assume missing message is an error, since it may be
203+
* suppressed during serialization.
204+
*
205+
* Should probably allow use of default constructor, too...
206+
*/
207+
//throw new XxxException("No 'message' property found: could not deserialize "+_beanType);
208+
if (hasStringCreator) {
209+
if (valueAsString != null) {
210+
return (Throwable) _valueInstantiator.createFromString(ctxt, valueAsString);
211+
} else {
212+
return (Throwable) _valueInstantiator.createFromString(ctxt, null);
213+
}
214+
} else {
215+
return (Throwable) _valueInstantiator.createUsingDefault(ctxt);
216+
}
217+
}
199218
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.fasterxml.jackson.databind.exc;
2+
3+
import java.util.*;
4+
5+
import com.fasterxml.jackson.annotation.JsonAnyGetter;
6+
import com.fasterxml.jackson.annotation.JsonAnySetter;
7+
import com.fasterxml.jackson.databind.*;
8+
9+
public class ExceptionWithAnySetter4316Test extends BaseMapTest
10+
{
11+
static class Problem extends Exception {
12+
private static final long serialVersionUID = 1L;
13+
14+
@JsonAnySetter
15+
@JsonAnyGetter
16+
Map<String, Object> additionalProperties = new HashMap<>();
17+
}
18+
19+
private final ObjectMapper MAPPER = newJsonMapper();
20+
21+
// [databind#4316]
22+
public void testWithAnySetter() throws Exception
23+
{
24+
Problem problem = new Problem();
25+
problem.additionalProperties.put("key", "value");
26+
String json = MAPPER.writeValueAsString(problem);
27+
Problem result = MAPPER.readValue(json, Problem.class);
28+
assertEquals(Collections.singletonMap("key", "value"),
29+
result.additionalProperties);
30+
}
31+
}

0 commit comments

Comments
 (0)