Skip to content

Commit

Permalink
Fix #2527
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Dec 28, 2019
1 parent e1de974 commit a28cded
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 22 deletions.
2 changes: 2 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Project: jackson-databind
#2522: `DeserializationContext.handleMissingInstantiator()` throws `MismatchedInputException`
for non-static inner classes
#2525: Incorrect `JsonStreamContext` for `TokenBuffer` and `TreeTraversingParser`
#2527: Add `AnnotationIntrospector.findRenameByField()` to support Kotlin's "is-getter"
naming convention
#2555: Use `@JsonProperty(index)` for sorting properties on serialization
#2565: Java 8 `Optional` not working with `@JsonUnwrapped` on unwrappable type
(reported by Haowei W)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,22 @@ public class POJOPropertiesCollector
protected LinkedHashMap<String, POJOPropertyBuilder> _properties;

protected LinkedList<POJOPropertyBuilder> _creatorProperties;

/**
* A set of "field renamings" that have been discovered, indicating
* intended renaming of other accesors: key is the implicit original
* name and value intended name to use instead.
*<p>
* Note that these renamings are applied earlier than "regular" (explicit)
* renamings and affect implicit name: their effect may be changed by
* further renaming based on explicit indicators.
* The main use case is to effectively relink accessors based on fields
* discovered, and used to sort of correct otherwise missing linkage between
* fields and other accessors.
*
* @since 2.11
*/
protected Map<PropertyName, PropertyName> _fieldRenameMappings;

protected LinkedList<AnnotatedMember> _anyGetters;

Expand Down Expand Up @@ -305,7 +321,7 @@ protected void collectAll()
LinkedHashMap<String, POJOPropertyBuilder> props = new LinkedHashMap<String, POJOPropertyBuilder>();

// First: gather basic data
_addFields(props);
_addFields(props); // note: populates _fieldRenameMappings
_addMethods(props);
// 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static
// inner classes, see [databind#1502]
Expand All @@ -314,9 +330,6 @@ protected void collectAll()
}
_addInjectables(props);

// 27-Dec-2019, tatu: [databind#2527] initial re-linking by Field needs to
// be applied before other processing

// Remove ignored properties, first; this MUST precede annotation merging
// since logic relies on knowing exactly which accessor has which annotation
_removeUnwantedProperties(props);
Expand Down Expand Up @@ -397,15 +410,20 @@ protected void _addFields(Map<String, POJOPropertyBuilder> props)
if (implName == null) {
implName = f.getName();
}
final PropertyName implNameP = _propNameFromSimple(implName);

// [databind#2527: Field-based renaming can be applied early (here),
// or at a later point, but probably must be done before pruning
// final fields. So let's do it early here
final PropertyName rename = ai.findRenameByField(_config, f, _propNameFromSimple(implName));
if (rename != null) {
final PropertyName rename = ai.findRenameByField(_config, f, implNameP);
if ((rename != null) && !rename.equals(implNameP)) {
if (_fieldRenameMappings == null) {
_fieldRenameMappings = new HashMap<>();
}
_fieldRenameMappings.put(rename, implNameP);
// todo
}

PropertyName pn;

if (_forSerialization) {
Expand Down Expand Up @@ -511,9 +529,12 @@ protected void _addCreatorParam(Map<String, POJOPropertyBuilder> props,
pn = PropertyName.construct(impl);
}

// 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
impl = _checkRenameByField(impl);

// shouldn't need to worry about @JsonIgnore, since creators only added
// if so annotated

/* 13-May-2015, tatu: We should try to start with implicit name, similar to how
* fields and methods work; but unlike those, we don't necessarily have
* implicit name to use (pre-Java8 at least). So:
Expand All @@ -531,11 +552,11 @@ protected void _addMethods(Map<String, POJOPropertyBuilder> props)
{
final AnnotationIntrospector ai = _annotationIntrospector;
for (AnnotatedMethod m : _classDef.memberMethods()) {
/* For methods, handling differs between getters and setters; and
* we will also only consider entries that either follow the bean
* naming convention or are explicitly marked: just being visible
* is not enough (unlike with fields)
*/
// For methods, handling differs between getters and setters; and
// we will also only consider entries that either follow the bean
// naming convention or are explicitly marked: just being visible
// is not enough (unlike with fields)

int argCount = m.getParameterCount();
if (argCount == 0) { // getters (including 'any getter')
_addGetterMethod(props, m, ai);
Expand Down Expand Up @@ -616,6 +637,8 @@ protected void _addGetterMethod(Map<String, POJOPropertyBuilder> props,
}
visible = true;
}
// 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
implName = _checkRenameByField(implName);
boolean ignore = ai.hasIgnoreMarker(m);
_property(props, implName).addGetter(m, pn, nameExplicit, visible, ignore);
}
Expand Down Expand Up @@ -653,6 +676,8 @@ protected void _addSetterMethod(Map<String, POJOPropertyBuilder> props,
}
visible = true;
}
// 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
implName = _checkRenameByField(implName);
boolean ignore = (ai == null) ? false : ai.hasIgnoreMarker(m);
_property(props, implName).addSetter(m, pn, nameExplicit, visible, ignore);
}
Expand Down Expand Up @@ -697,7 +722,20 @@ protected void _doAddInjectable(JacksonInject.Value injectable, AnnotatedMember
private PropertyName _propNameFromSimple(String simpleName) {
return PropertyName.construct(simpleName, null);
}


// @since 2.11
private String _checkRenameByField(String implName) {
if (_fieldRenameMappings != null) {
PropertyName p = _fieldRenameMappings.get(_propNameFromSimple(implName));
if (p != null) {
implName = p.getSimpleName();
return implName;

}
}
return implName;
}

/*
/**********************************************************
/* Internal methods; removing ignored properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Collections;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
Expand All @@ -24,18 +25,28 @@ public POJO2527(boolean b) {
public void setEnabled(boolean b) { isEnabled = b; }
}

static class POJO2527b {
static class POJO2527PublicField {
public boolean isEnabled;

protected POJO2527b() { }
public POJO2527b(boolean b) {
protected POJO2527PublicField() { }
public POJO2527PublicField(boolean b) {
isEnabled = b;
}

public boolean getEnabled() { return isEnabled; }
public void setEnabled(boolean b) { isEnabled = b; }
}


static class POJO2527Creator {
private final boolean isEnabled;

public POJO2527Creator(@JsonProperty("enabled") boolean b) {
isEnabled = b;
}

public boolean getEnabled() { return isEnabled; }
}

@SuppressWarnings("serial")
static class MyIntrospector extends JacksonAnnotationIntrospector
{
Expand All @@ -55,7 +66,9 @@ public PropertyName findRenameByField(MapperConfig<?> config,
}
}

private final ObjectMapper MAPPER = newJsonMapper();
private final ObjectMapper MAPPER = jsonMapperBuilder()
.annotationIntrospector(new MyIntrospector())
.build();

public void testIsPropertiesStdKotlin() throws Exception
{
Expand All @@ -70,16 +83,29 @@ public void testIsPropertiesStdKotlin() throws Exception
assertEquals(input.isEnabled, output.isEnabled);
}

public void testIsPropertiesAlt() throws Exception
public void testIsPropertiesWithPublicField() throws Exception
{
POJO2527PublicField input = new POJO2527PublicField(true);
final String json = MAPPER.writeValueAsString(input);

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

POJO2527PublicField output = MAPPER.readValue(json, POJO2527PublicField.class);
assertEquals(input.isEnabled, output.isEnabled);
}

public void testIsPropertiesViaCreator() throws Exception
{
POJO2527b input = new POJO2527b(true);
POJO2527Creator input = new POJO2527Creator(true);
final String json = MAPPER.writeValueAsString(input);

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

POJO2527b output = MAPPER.readValue(json, POJO2527b.class);
POJO2527Creator output = MAPPER.readValue(json, POJO2527Creator.class);
assertEquals(input.isEnabled, output.isEnabled);
}
}

0 comments on commit a28cded

Please sign in to comment.