From 9f80462540bd0e0cce847a572dedd4040634ae4f Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 18 May 2023 16:44:56 -0700 Subject: [PATCH] Fix #3938: do not skip Method-based setters on Records (#3939) --- release-notes/VERSION-2.x | 4 ++ .../databind/deser/BeanDeserializer.java | 4 +- .../records/RecordFailingSetter3938Test.java | 42 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordFailingSetter3938Test.java diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 489349e707..f5efaf82c5 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -4,6 +4,10 @@ Project: jackson-databind === Releases === ------------------------------------------------------------------------ +2.15.2 (not yet released) + +#3938: Record setter not included from interface (2.15 regression) + 2.15.1 (16-May-2023) #3882: Error in creating nested `ArrayNode`s with `JsonNode.withArray()` diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java index f2487ff713..3371ed4f4c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java @@ -470,7 +470,9 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri // weren't removed (to help in creating constructor-backed PropertyCreator) // so they ended up in _beanProperties, unlike POJO (whose ignored // props are removed) - if ((prop != null) && !_beanType.isRecordType()) { + if ((prop != null) && + // [databind#3938]: except if it's MethodProperty + (!_beanType.isRecordType() || (prop instanceof MethodProperty))) { try { buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop)); } catch (UnresolvedForwardReference reference) { diff --git a/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordFailingSetter3938Test.java b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordFailingSetter3938Test.java new file mode 100644 index 0000000000..3fe8cfef18 --- /dev/null +++ b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordFailingSetter3938Test.java @@ -0,0 +1,42 @@ +package com.fasterxml.jackson.databind.records; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.*; + +public class RecordFailingSetter3938Test extends BaseMapTest +{ + private final static String ERROR_3938_PREFIX = "Non-null 'options' not allowed for "; + + // [databind#3938] + interface NoOptionsCommand { + @JsonProperty("options") + default void setOptions(JsonNode value) { + if (value.isNull()) { + return; + } + throw new IllegalArgumentException(ERROR_3938_PREFIX+getClass().getName()); + } + } + + public record Command3938(int id, String filter) implements NoOptionsCommand { } + + private final ObjectMapper MAPPER = newJsonMapper(); + + // [databind#3938]: Should detect and use setters too + public void testFailingSetter3939() throws Exception + { + final ObjectReader R = MAPPER.readerFor(Command3938.class); + + // First, missing value and `null` are fine, as long as we have all fields + assertNotNull(R.readValue(a2q("{'id':1, 'filter':'abc'}"))); + assertNotNull(R.readValue(a2q("{'id':2, 'filter':'abc', 'options':null}"))); + + // But then failure for non-empty Array (f.ex) + try { + R.readValue(a2q("{'id':2,'options':[123]}}")); + fail("Should not pass"); + } catch (DatabindException e) { + verifyException(e, ERROR_3938_PREFIX); + } + } +}