From a79ce09e93aac381528b03efcb9516f52b81ff22 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Sun, 29 Sep 2024 13:02:50 +0900 Subject: [PATCH] Add any-setter handling in `PropertyValueBuffer` post #562 (#4720) --- release-notes/CREDITS-2.x | 3 ++ release-notes/VERSION-2.x | 6 +++ .../deser/impl/PropertyValueBuffer.java | 50 ++++++++++++++++--- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 42dad748c5..6803c37e17 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -1834,3 +1834,6 @@ Rikkarth (rikkarth@github) * Contributed #4709: Add `JacksonCollectors` with `toArrayNode()` implementation (2.18.0) +Maxim Valeev (@MaximValeev) + * Reported #4508: Deserialized JsonAnySetter field in Kotlin data class is null + (2.18.1) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 8c74695818..c9097a2c05 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -4,6 +4,12 @@ Project: jackson-databind === Releases === ------------------------------------------------------------------------ +2.18.1 (WIP-2024) + +#4508: Deserialized JsonAnySetter field in Kotlin data class is null + (reported by @MaximValeev) + (fix by Joo-Hyuk K) + 2.18.0 (26-Sep-2024) #562: Allow `@JsonAnySetter` to flow through Creators diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java index 531c6ab24e..31d2b76bd3 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java @@ -135,10 +135,24 @@ public PropertyValueBuffer(JsonParser p, DeserializationContext ctxt, int paramC */ public final boolean hasParameter(SettableBeanProperty prop) { + final int ix = prop.getCreatorIndex(); + if (_paramsSeenBig == null) { - return ((_paramsSeen >> prop.getCreatorIndex()) & 1) == 1; + if (((_paramsSeen >> ix) & 1) == 1) { + return true; + } + } else { + if (_paramsSeenBig.get(ix)) { + return true; + } } - return _paramsSeenBig.get(prop.getCreatorIndex()); + // 28-Sep-2024 : [databind#4508] Support any-setter flowing through creator + if (_anyParamSetter != null) { + if (ix == _anyParamSetter.getParameterIndex()) { + return true; + } + } + return false; } /** @@ -160,6 +174,11 @@ public Object getParameter(SettableBeanProperty prop) } else { value = _creatorParameters[prop.getCreatorIndex()] = _findMissing(prop); } + // 28-Sep-2024 : [databind#4508] Support any-setter flowing through creator + if ((value == null) && (_anyParamSetter != null ) + && (prop.getCreatorIndex() == _anyParamSetter.getParameterIndex())) { + value = _createAndSetAnySetterValue(); + } if (value == null && _context.isEnabled(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES)) { return _context.reportInputMismatch(prop, "Null value for creator property '%s' (index %d); `DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES` enabled", @@ -198,11 +217,7 @@ public Object[] getParameters(SettableBeanProperty[] props) } // [databind#562] since 2.18 : Respect @JsonAnySetter in @JsonCreator if (_anyParamSetter != null) { - Object anySetterParameterObject = _anyParamSetter.createParameterObject(); - for (PropertyValue pv = _anyParamBuffered; pv != null; pv = pv.next) { - pv.setValue(anySetterParameterObject); - } - _creatorParameters[_anyParamSetter.getParameterIndex()] = anySetterParameterObject; + _creatorParameters[_anyParamSetter.getParameterIndex()] = _createAndSetAnySetterValue(); } if (_context.isEnabled(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES)) { for (int ix = 0; ix < props.length; ++ix) { @@ -217,6 +232,27 @@ public Object[] getParameters(SettableBeanProperty[] props) return _creatorParameters; } + /** + * Helper method called to create and set any values buffered for "any setter" + */ + private Object _createAndSetAnySetterValue() throws JsonMappingException + { + Object anySetterParameterObject = _anyParamSetter.createParameterObject(); + for (PropertyValue pv = _anyParamBuffered; pv != null; pv = pv.next) { + try { + pv.setValue(anySetterParameterObject); + + // Since one of callers only exposes JsonMappingException, but pv.setValue() + // nominally leaks IOException, need to do this unfortunate conversion + } catch (JsonMappingException e) { + throw e; + } catch (IOException e) { + throw JsonMappingException.fromUnexpectedIOE(e); + } + } + return anySetterParameterObject; + } + protected Object _findMissing(SettableBeanProperty prop) throws JsonMappingException { // 08-Jun-2024: [databind#562] AnySetters are bit special