From 7d187c97e061bd4cd1417e265bc7dd657885d21e Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Wed, 11 Dec 2024 08:55:52 +0900 Subject: [PATCH] fix subclassed exception deserialization (#4847) --- release-notes/VERSION-2.x | 6 +++ .../deser/BeanDeserializerFactory.java | 35 +++++++------ ...assedThrowableDeserialization4827Test.java | 49 +++++++++++++++++++ 3 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/databind/exc/SubclassedThrowableDeserialization4827Test.java diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index e2155a6d2c..d2c00e55bc 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -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 diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java index ab02dfee97..257d8110ac 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java @@ -438,20 +438,27 @@ public JsonDeserializer 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? diff --git a/src/test/java/com/fasterxml/jackson/databind/exc/SubclassedThrowableDeserialization4827Test.java b/src/test/java/com/fasterxml/jackson/databind/exc/SubclassedThrowableDeserialization4827Test.java new file mode 100644 index 0000000000..c3bc887a58 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/exc/SubclassedThrowableDeserialization4827Test.java @@ -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()); + } +}