Skip to content

Commit

Permalink
Prioritize constructor parameter over field if both are annotated wit…
Browse files Browse the repository at this point in the history
…h `@JsonAnySetter`, to fix FasterXML#4634.
  • Loading branch information
yihtserns committed Jul 24, 2024
1 parent 3ed7f45 commit 800f844
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 8 deletions.
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

0 comments on commit 800f844

Please sign in to comment.