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

Ability to use JsonAnyGetter on fields #2881

Merged
merged 2 commits into from
Oct 16, 2020
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 @@ -466,18 +466,34 @@ public JsonInclude.Value findPropertyInclusion(JsonInclude.Value defValue) {
@Override
public AnnotatedMember findAnyGetter() throws IllegalArgumentException
{
AnnotatedMember anyGetter = (_propCollector == null) ? null
: _propCollector.getAnyGetter();
if (anyGetter != null) {
/* For now let's require a Map; in future can add support for other
* types like perhaps Iterable<Map.Entry>?
*/
Class<?> type = anyGetter.getRawType();
if (!Map.class.isAssignableFrom(type)) {
throw new IllegalArgumentException("Invalid 'any-getter' annotation on method "+anyGetter.getName()+"(): return type is not instance of java.util.Map");
if (_propCollector != null) {
AnnotatedMember anyGetter = _propCollector.getAnyGetterMethod();
if (anyGetter != null) {
// For now let's require a Map; in future can add support for other
// types like perhaps Iterable<Map.Entry>?
Class<?> type = anyGetter.getRawType();
if (!Map.class.isAssignableFrom(type)) {
throw new IllegalArgumentException(String.format(
"Invalid 'any-getter' annotation on method %s(): return type is not instance of java.util.Map",
anyGetter.getName()));
}
return anyGetter;
}

AnnotatedMember anyField = _propCollector.getAnyGetterField();
if (anyField != null) {
// For now let's require a Map; in future can add support for other
// types like perhaps Iterable<Map.Entry>?
Class<?> type = anyField.getRawType();
if (!Map.class.isAssignableFrom(type)) {
throw new IllegalArgumentException(String.format(
"Invalid 'any-getter' annotation on field '%s': type is not instance of java.util.Map",
anyField.getName()));
}
return anyField;
}
}
return anyGetter;
return null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ public class POJOPropertiesCollector

protected LinkedList<AnnotatedMember> _anyGetters;

/**
* @since 2.12
*/
protected LinkedList<AnnotatedMember> _anyGetterField;
cowtowncoder marked this conversation as resolved.
Show resolved Hide resolved

protected LinkedList<AnnotatedMethod> _anySetters;

protected LinkedList<AnnotatedMember> _anySetterField;
Expand Down Expand Up @@ -208,18 +213,36 @@ public AnnotatedMember getJsonValueAccessor()
return null;
}

public AnnotatedMember getAnyGetter()
/**
* @since 2.12
*/
public AnnotatedMember getAnyGetterField()
{
if (!_collected) {
collectAll();
}
if (_anyGetterField != null) {
if (_anyGetterField.size() > 1) {
reportProblem("Multiple 'any-getter' fields defined (%s vs %s)",
_anyGetterField.get(0), _anyGetterField.get(1));
}
return _anyGetterField.getFirst();
}
return null;
}

public AnnotatedMember getAnyGetterMethod()
{
if (!_collected) {
collectAll();
}
if (_anyGetters != null) {
if (_anyGetters.size() > 1) {
reportProblem("Multiple 'any-getters' defined (%s vs %s)",
reportProblem("Multiple 'any-getter' methods defined (%s vs %s)",
_anyGetters.get(0), _anyGetters.get(1));
}
return _anyGetters.getFirst();
}
}
return null;
}

Expand Down Expand Up @@ -392,12 +415,25 @@ protected void _addFields(Map<String, POJOPropertyBuilder> props)
_jsonValueAccessors.add(f);
continue;
}
// @JsonAnySetter?
if (Boolean.TRUE.equals(ai.hasAnySetter(f))) {
if (_anySetterField == null) {
_anySetterField = new LinkedList<AnnotatedMember>();
// 12-October-2020, dominikrebhan: [databind#1458] Support @JsonAnyGetter on
// fields and allow @JsonAnySetter to be declared as well.
boolean anyGetter = Boolean.TRUE.equals(ai.hasAnyGetter(f));
boolean anySetter = Boolean.TRUE.equals(ai.hasAnySetter(f));
if (anyGetter || anySetter) {
// @JsonAnyGetter?
if (anyGetter) {
if (_anyGetterField == null) {
_anyGetterField = new LinkedList<>();
}
_anyGetterField.add(f);
}
// @JsonAnySetter?
if (anySetter) {
if (_anySetterField == null) {
_anySetterField = new LinkedList<>();
}
_anySetterField.add(f);
}
_anySetterField.add(f);
continue;
}
String implName = ai.findImplicitPropertyName(f);
Expand Down Expand Up @@ -685,7 +721,7 @@ protected void _addSetterMethod(Map<String, POJOPropertyBuilder> props,
}
// 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
implName = _checkRenameByField(implName);
boolean ignore = (ai == null) ? false : ai.hasIgnoreMarker(m);
boolean ignore = ai != null && ai.hasIgnoreMarker(m);
_property(props, implName).addSetter(m, pn, nameExplicit, visible, ignore);
}

Expand Down Expand Up @@ -721,7 +757,7 @@ protected void _doAddInjectable(JacksonInject.Value injectable, AnnotatedMember
if (prev.getClass() == m.getClass()) {
String type = id.getClass().getName();
throw new IllegalArgumentException("Duplicate injectable value with id '"
+String.valueOf(id)+"' (of type "+type+")");
+ id +"' (of type "+type+")");
}
}
}
Expand Down Expand Up @@ -1001,7 +1037,7 @@ protected void _sortProperties(Map<String, POJOPropertyBuilder> props)
{
// Then how about explicit ordering?
final AnnotationIntrospector intr = _annotationIntrospector;
Boolean alpha = intr.findSerializationSortAlphabetically((Annotated) _classDef);
Boolean alpha = intr.findSerializationSortAlphabetically(_classDef);
final boolean sort = (alpha == null)
? _config.shouldSortPropertiesAlphabetically()
: alpha.booleanValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ public void set(String name, String value) {
}
}

static class DynaFieldBean {
public int id;

@JsonAnyGetter
@JsonAnySetter
protected HashMap<String,String> other = new HashMap<String,String>();

public Map<String,String> any() {
return other;
}

public void set(String name, String value) {
other.put(name, value);
}
}

static class PrivateThing
{
@JsonAnyGetter
Expand Down Expand Up @@ -65,6 +81,18 @@ public void testDynaBean() throws Exception
assertEquals("Joe", result.other.get("name"));
}

public void testDynaFieldBean() throws Exception
{
DynaFieldBean b = new DynaFieldBean();
b.id = 123;
b.set("name", "Billy");
assertEquals("{\"id\":123,\"name\":\"Billy\"}", MAPPER.writeValueAsString(b));

DynaFieldBean result = MAPPER.readValue("{\"id\":2,\"name\":\"Joe\"}", DynaFieldBean.class);
assertEquals(2, result.id);
assertEquals("Joe", result.other.get("name"));
}

public void testPrivate() throws Exception
{
String json = MAPPER.writeValueAsString(new PrivateThing());
Expand Down