Skip to content

Commit

Permalink
fix subclassed exception deserialization (#4847)
Browse files Browse the repository at this point in the history
  • Loading branch information
JooHyukKim authored Dec 10, 2024
1 parent c1d3ea7 commit 7d187c9
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 14 deletions.
6 changes: 6 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ Project: jackson-databind
=== Releases ===
------------------------------------------------------------------------

2.18.3 (not yet released)

#4827: Subclassed Throwable deserialization fails since v2.18.0 - no creator
index for property 'cause'
(fix by Joo-Hyuk K)

2.18.2 (27-Nov-2024)

#4733: Wrong serialization of Type Ids for certain types of Enum values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,20 +438,27 @@ public JsonDeserializer<Object> buildThrowableDeserializer(DeserializationContex
}
AnnotatedMethod am = beanDesc.findMethod("initCause", INIT_CAUSE_PARAMS);
if (am != null) { // should never be null
// [databind#3497]: must consider possible PropertyNamingStrategy
String name = "cause";
PropertyNamingStrategy pts = config.getPropertyNamingStrategy();
if (pts != null) {
name = pts.nameForSetterMethod(config, am, "cause");
}
SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(ctxt.getConfig(), am,
new PropertyName(name));
SettableBeanProperty prop = constructSettableProperty(ctxt, beanDesc, propDef,
am.getParameterType(0));
if (prop != null) {
// 21-Aug-2011, tatus: We may actually have found 'cause' property
// to set... but let's replace it just in case, otherwise can end up with odd errors.
builder.addOrReplaceProperty(prop, true);
SettableBeanProperty causeCreatorProp = builder.findProperty(PropertyName.construct("cause"));
// [databind#4827] : Consider case where sub-classed `Exception` has `JsonCreator` with `cause` parameter
if (causeCreatorProp instanceof CreatorProperty) {
// Set fallback-setter as null, so `fixAccess()` does not happen during build
((CreatorProperty) causeCreatorProp).setFallbackSetter(null);
} else {
// [databind#3497]: must consider possible PropertyNamingStrategy
String name = "cause";
PropertyNamingStrategy pts = config.getPropertyNamingStrategy();
if (pts != null) {
name = pts.nameForSetterMethod(config, am, "cause");
}
SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(ctxt.getConfig(), am,
new PropertyName(name));
SettableBeanProperty prop = constructSettableProperty(ctxt, beanDesc, propDef,
am.getParameterType(0));
if (prop != null) {
// 21-Aug-2011, tatus: We may actually have found 'cause' property
// to set... but let's replace it just in case, otherwise can end up with odd errors.
builder.addOrReplaceProperty(prop, true);
}
}
}
// update builder now that all information is in?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.fasterxml.jackson.databind.exc;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;

import static org.junit.jupiter.api.Assertions.assertEquals;

// [databind#4827] Subclassed Throwable deserialization fails since v2.18.0
// no creator index for property 'cause'
public class SubclassedThrowableDeserialization4827Test
extends DatabindTestUtil
{

public static class SubclassedExceptionJava extends Exception {
@JsonCreator
public SubclassedExceptionJava(
@JsonProperty("message") String message,
@JsonProperty("cause") Throwable cause
) {
super(message, cause);
}
}

private final ObjectMapper MAPPER = jsonMapperBuilder()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.build();

@Test
public void testDeserialization()
throws Exception
{
// Given input
SubclassedExceptionJava input = new SubclassedExceptionJava(
"Test Message", new RuntimeException("test runtime cause"));

// When serialize, then deserialize, round-trip
String serialized = MAPPER.writeValueAsString(input);
SubclassedExceptionJava deserialized = MAPPER.readValue(serialized, SubclassedExceptionJava.class);

// Contents are same
assertEquals(input.getMessage(), deserialized.getMessage());
assertEquals(input.getCause().getMessage(), deserialized.getCause().getMessage());
}
}

0 comments on commit 7d187c9

Please sign in to comment.