Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prioritize constructor parameter over field if both are annotated with @JsonAnySetter, to fix #4634 #4641

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -666,12 +666,7 @@ private SettableAnyProperty _resolveAnySetter(DeserializationContext ctxt,
BeanDescription beanDesc, SettableBeanProperty[] creatorProps)
throws JsonMappingException
{
// Find the regular method/field level any-setter
AnnotatedMember anySetter = beanDesc.findAnySetterAccessor();
if (anySetter != null) {
return constructAnySetter(ctxt, beanDesc, anySetter);
}
// else look for any-setter via @JsonCreator
// Look for any-setter via @JsonCreator
if (creatorProps != null) {
for (SettableBeanProperty prop : creatorProps) {
AnnotatedMember member = prop.getMember();
Expand All @@ -680,6 +675,11 @@ private SettableAnyProperty _resolveAnySetter(DeserializationContext ctxt,
}
}
}
// else find the regular method/field level any-setter
AnnotatedMember anySetter = beanDesc.findAnySetterAccessor();
if (anySetter != null) {
return constructAnySetter(ctxt, beanDesc, anySetter);
}
// not found, that's fine, too
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.junit.jupiter.api.Test;

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

// [databind#562] Allow @JsonAnySetter on Creator constructors
Expand All @@ -36,13 +37,29 @@ public POJO562(@JsonProperty("a") String a,
}
}

static class POJO562WithAnnotationOnBothCtorParamAndField
{
String a;
@JsonAnySetter
Map<String,Object> stuffFromField;
Map<String,Object> stuffFromConstructor;

@JsonCreator
public POJO562WithAnnotationOnBothCtorParamAndField(@JsonProperty("a") String a,
@JsonAnySetter Map<String, Object> leftovers
) {
this.a = a;
stuffFromConstructor = leftovers;
}
}

static class POJO562WithField
{
String a;
Map<String,Object> stuff;

public String b;

@JsonCreator
public POJO562WithField(@JsonProperty("a") String a,
@JsonAnySetter Map<String, Object> leftovers
Expand Down Expand Up @@ -115,12 +132,32 @@ public void mapAnySetterViaCreator562() throws Exception

assertEquals("value", pojo.a);
assertEquals(expected, pojo.stuff);

// Should also initialize any-setter-Map even if no contents
pojo = MAPPER.readValue(a2q("{'a':'value2'}"), POJO562.class);
assertEquals("value2", pojo.a);
assertEquals(new HashMap<>(), pojo.stuff);
}

// [databind#4634]
@Test
public void mapAnySetterViaCreatorWhenBothCreatorAndFieldAreAnnotated() throws Exception
{
Map<String, Object> expected = new HashMap<>();
expected.put("b", Integer.valueOf(42));
expected.put("c", Integer.valueOf(111));

POJO562WithAnnotationOnBothCtorParamAndField pojo = MAPPER.readValue(a2q(
"{'a':'value', 'b':42, 'c': 111}"
),
POJO562WithAnnotationOnBothCtorParamAndField.class);

assertEquals("value", pojo.a);
assertEquals(expected, pojo.stuffFromConstructor);
// In an ideal world, maybe exception should be thrown for annotating both field + constructor parameter,
// but that scenario is possible in this imperfect world e.g. annotating `@JsonAnySetter` on a Record component
// will cause that annotation to be (auto)propagated to both the field & constructor parameter (& accessor method)
assertNull(pojo.stuffFromField);
}

// Creator and non-Creator props AND any-setter ought to be fine too
Expand Down