Skip to content

Commit e33acf4

Browse files
committed
Merge branch '2.11'
2 parents d77b5cd + a28cded commit e33acf4

File tree

3 files changed

+86
-21
lines changed

3 files changed

+86
-21
lines changed

release-notes/VERSION-2.x

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ Project: jackson-databind
2424
#2522: `DeserializationContext.handleMissingInstantiator()` throws `MismatchedInputException`
2525
for non-static inner classes
2626
#2525: Incorrect `JsonStreamContext` for `TokenBuffer` and `TreeTraversingParser`
27+
#2527: Add `AnnotationIntrospector.findRenameByField()` to support Kotlin's "is-getter"
28+
naming convention
2729
#2555: Use `@JsonProperty(index)` for sorting properties on serialization
2830
#2565: Java 8 `Optional` not working with `@JsonUnwrapped` on unwrappable type
2931
(reported by Haowei W)

src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,22 @@ public class POJOPropertiesCollector
8181
protected LinkedHashMap<String, POJOPropertyBuilder> _properties;
8282

8383
protected LinkedList<POJOPropertyBuilder> _creatorProperties;
84+
85+
/**
86+
* A set of "field renamings" that have been discovered, indicating
87+
* intended renaming of other accesors: key is the implicit original
88+
* name and value intended name to use instead.
89+
*<p>
90+
* Note that these renamings are applied earlier than "regular" (explicit)
91+
* renamings and affect implicit name: their effect may be changed by
92+
* further renaming based on explicit indicators.
93+
* The main use case is to effectively relink accessors based on fields
94+
* discovered, and used to sort of correct otherwise missing linkage between
95+
* fields and other accessors.
96+
*
97+
* @since 2.11
98+
*/
99+
protected Map<PropertyName, PropertyName> _fieldRenameMappings;
84100

85101
protected LinkedList<AnnotatedMember> _anyGetters;
86102

@@ -280,7 +296,7 @@ protected void collectAll()
280296
LinkedHashMap<String, POJOPropertyBuilder> props = new LinkedHashMap<String, POJOPropertyBuilder>();
281297

282298
// First: gather basic data
283-
_addFields(props);
299+
_addFields(props); // note: populates _fieldRenameMappings
284300
_addMethods(props);
285301
// 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static
286302
// inner classes, see [databind#1502]
@@ -289,9 +305,6 @@ protected void collectAll()
289305
}
290306
_addInjectables(props);
291307

292-
// 27-Dec-2019, tatu: [databind#2527] initial re-linking by Field needs to
293-
// be applied before other processing
294-
295308
// Remove ignored properties, first; this MUST precede annotation merging
296309
// since logic relies on knowing exactly which accessor has which annotation
297310
_removeUnwantedProperties(props);
@@ -368,15 +381,20 @@ protected void _addFields(Map<String, POJOPropertyBuilder> props)
368381
if (implName == null) {
369382
implName = f.getName();
370383
}
384+
final PropertyName implNameP = _propNameFromSimple(implName);
371385

372386
// [databind#2527: Field-based renaming can be applied early (here),
373387
// or at a later point, but probably must be done before pruning
374388
// final fields. So let's do it early here
375-
final PropertyName rename = ai.findRenameByField(_config, f, _propNameFromSimple(implName));
376-
if (rename != null) {
389+
final PropertyName rename = ai.findRenameByField(_config, f, implNameP);
390+
if ((rename != null) && !rename.equals(implNameP)) {
391+
if (_fieldRenameMappings == null) {
392+
_fieldRenameMappings = new HashMap<>();
393+
}
394+
_fieldRenameMappings.put(rename, implNameP);
377395
// todo
378396
}
379-
397+
380398
PropertyName pn;
381399

382400
if (_forSerialization) {
@@ -479,9 +497,12 @@ protected void _addCreatorParam(Map<String, POJOPropertyBuilder> props,
479497
pn = PropertyName.construct(impl);
480498
}
481499

500+
// 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
501+
impl = _checkRenameByField(impl);
502+
482503
// shouldn't need to worry about @JsonIgnore, since creators only added
483504
// if so annotated
484-
505+
485506
/* 13-May-2015, tatu: We should try to start with implicit name, similar to how
486507
* fields and methods work; but unlike those, we don't necessarily have
487508
* implicit name to use (pre-Java8 at least). So:
@@ -499,11 +520,11 @@ protected void _addMethods(Map<String, POJOPropertyBuilder> props)
499520
{
500521
final AnnotationIntrospector ai = _annotationIntrospector;
501522
for (AnnotatedMethod m : _classDef.memberMethods()) {
502-
/* For methods, handling differs between getters and setters; and
503-
* we will also only consider entries that either follow the bean
504-
* naming convention or are explicitly marked: just being visible
505-
* is not enough (unlike with fields)
506-
*/
523+
// For methods, handling differs between getters and setters; and
524+
// we will also only consider entries that either follow the bean
525+
// naming convention or are explicitly marked: just being visible
526+
// is not enough (unlike with fields)
527+
507528
int argCount = m.getParameterCount();
508529
if (argCount == 0) { // getters (including 'any getter')
509530
_addGetterMethod(props, m, ai);
@@ -584,6 +605,8 @@ protected void _addGetterMethod(Map<String, POJOPropertyBuilder> props,
584605
}
585606
visible = true;
586607
}
608+
// 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
609+
implName = _checkRenameByField(implName);
587610
boolean ignore = ai.hasIgnoreMarker(_config, m);
588611
_property(props, implName).addGetter(m, pn, nameExplicit, visible, ignore);
589612
}
@@ -621,6 +644,8 @@ protected void _addSetterMethod(Map<String, POJOPropertyBuilder> props,
621644
}
622645
visible = true;
623646
}
647+
// 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
648+
implName = _checkRenameByField(implName);
624649
boolean ignore = (ai == null) ? false : ai.hasIgnoreMarker(_config, m);
625650
_property(props, implName).addSetter(m, pn, nameExplicit, visible, ignore);
626651
}
@@ -666,6 +691,18 @@ private PropertyName _propNameFromSimple(String simpleName) {
666691
return PropertyName.construct(simpleName, null);
667692
}
668693

694+
private String _checkRenameByField(String implName) {
695+
if (_fieldRenameMappings != null) {
696+
PropertyName p = _fieldRenameMappings.get(_propNameFromSimple(implName));
697+
if (p != null) {
698+
implName = p.getSimpleName();
699+
return implName;
700+
701+
}
702+
}
703+
return implName;
704+
}
705+
669706
/*
670707
/**********************************************************************
671708
/* Internal methods; removing ignored properties

src/test/java/com/fasterxml/jackson/failing/IsGetterRenaming2527Test.java

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.Collections;
44
import java.util.Map;
55

6+
import com.fasterxml.jackson.annotation.JsonProperty;
67
import com.fasterxml.jackson.databind.*;
78
import com.fasterxml.jackson.databind.cfg.MapperConfig;
89
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
@@ -24,18 +25,28 @@ public POJO2527(boolean b) {
2425
public void setEnabled(boolean b) { isEnabled = b; }
2526
}
2627

27-
static class POJO2527b {
28+
static class POJO2527PublicField {
2829
public boolean isEnabled;
2930

30-
protected POJO2527b() { }
31-
public POJO2527b(boolean b) {
31+
protected POJO2527PublicField() { }
32+
public POJO2527PublicField(boolean b) {
3233
isEnabled = b;
3334
}
3435

3536
public boolean getEnabled() { return isEnabled; }
3637
public void setEnabled(boolean b) { isEnabled = b; }
3738
}
38-
39+
40+
static class POJO2527Creator {
41+
private final boolean isEnabled;
42+
43+
public POJO2527Creator(@JsonProperty("enabled") boolean b) {
44+
isEnabled = b;
45+
}
46+
47+
public boolean getEnabled() { return isEnabled; }
48+
}
49+
3950
@SuppressWarnings("serial")
4051
static class MyIntrospector extends JacksonAnnotationIntrospector
4152
{
@@ -55,7 +66,9 @@ public PropertyName findRenameByField(MapperConfig<?> config,
5566
}
5667
}
5768

58-
private final ObjectMapper MAPPER = newJsonMapper();
69+
private final ObjectMapper MAPPER = jsonMapperBuilder()
70+
.annotationIntrospector(new MyIntrospector())
71+
.build();
5972

6073
public void testIsPropertiesStdKotlin() throws Exception
6174
{
@@ -70,16 +83,29 @@ public void testIsPropertiesStdKotlin() throws Exception
7083
assertEquals(input.isEnabled, output.isEnabled);
7184
}
7285

73-
public void testIsPropertiesAlt() throws Exception
86+
public void testIsPropertiesWithPublicField() throws Exception
87+
{
88+
POJO2527PublicField input = new POJO2527PublicField(true);
89+
final String json = MAPPER.writeValueAsString(input);
90+
91+
Map<?, ?> props = MAPPER.readValue(json, Map.class);
92+
assertEquals(Collections.singletonMap("isEnabled", Boolean.TRUE),
93+
props);
94+
95+
POJO2527PublicField output = MAPPER.readValue(json, POJO2527PublicField.class);
96+
assertEquals(input.isEnabled, output.isEnabled);
97+
}
98+
99+
public void testIsPropertiesViaCreator() throws Exception
74100
{
75-
POJO2527b input = new POJO2527b(true);
101+
POJO2527Creator input = new POJO2527Creator(true);
76102
final String json = MAPPER.writeValueAsString(input);
77103

78104
Map<?, ?> props = MAPPER.readValue(json, Map.class);
79105
assertEquals(Collections.singletonMap("isEnabled", Boolean.TRUE),
80106
props);
81107

82-
POJO2527b output = MAPPER.readValue(json, POJO2527b.class);
108+
POJO2527Creator output = MAPPER.readValue(json, POJO2527Creator.class);
83109
assertEquals(input.isEnabled, output.isEnabled);
84110
}
85111
}

0 commit comments

Comments
 (0)