Skip to content

Commit

Permalink
Fix #1428
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Nov 21, 2016
1 parent 2f76bc8 commit d0daf23
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 102 deletions.
1 change: 1 addition & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Project: jackson-databind
#1399: Add support for `@JsonSetter(merge=OptBoolean.TRUE`) to allow "deep update"
#1406: `ObjectMapper.readTree()` methods do not return `null` on end-of-input
(reported by Fabrizio C)
#1428: Allow `@JsonValue` on a field, not just getter
#1434: Explicitly pass null on invoke calls with no arguments
(contributed by Emiliano C)
#1433: `ObjectMapper.convertValue()` with null does not consider null conversions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ protected BeanDescription(JavaType type) {
* if any. If multiple ones are found,
* an error is reported by throwing {@link IllegalArgumentException}
*/
public abstract AnnotatedMember findJsonValueAccessor();

@Deprecated // since 2.9
public abstract AnnotatedMethod findJsonValueMethod();

public abstract AnnotatedMethod findMethod(String name, Class<?>[] paramTypes);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.fasterxml.jackson.databind.deser;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
Expand Down Expand Up @@ -1243,7 +1242,7 @@ public JsonDeserializer<?> createEnumDeserializer(DeserializationContext ctxt,
// Need to consider @JsonValue if one found
if (deser == null) {
deser = new EnumDeserializer(constructEnumResolver(enumClass,
config, beanDesc.findJsonValueMethod()));
config, beanDesc.findJsonValueAccessor()));
}
}

Expand Down Expand Up @@ -1432,7 +1431,7 @@ private KeyDeserializer _createEnumKeyDeserializer(DeserializationContext ctxt,
return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, valueDesForKey);
}
}
EnumResolver enumRes = constructEnumResolver(enumClass, config, beanDesc.findJsonValueMethod());
EnumResolver enumRes = constructEnumResolver(enumClass, config, beanDesc.findJsonValueAccessor());
// May have @JsonCreator for static factory method:
for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
if (_hasCreatorAnnotation(ctxt, factory)) {
Expand Down Expand Up @@ -1863,14 +1862,15 @@ protected JavaType resolveMemberAndTypeAnnotations(DeserializationContext ctxt,
}

protected EnumResolver constructEnumResolver(Class<?> enumClass,
DeserializationConfig config, AnnotatedMethod jsonValueMethod)
DeserializationConfig config, AnnotatedMember jsonValueAccessor)
{
if (jsonValueMethod != null) {
Method accessor = jsonValueMethod.getAnnotated();
if (jsonValueAccessor != null) {
if (config.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(accessor, config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
ClassUtil.checkAndFixAccess(jsonValueAccessor.getMember(),
config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
return EnumResolver.constructUnsafeUsingMethod(enumClass, accessor, config.getAnnotationIntrospector());
return EnumResolver.constructUnsafeUsingMethod(enumClass,
jsonValueAccessor, config.getAnnotationIntrospector());
}
// 14-Mar-2016, tatu: We used to check `DeserializationFeature.READ_ENUMS_USING_TO_STRING`
// here, but that won't do: it must be dynamically changeable...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,18 @@ public List<BeanPropertyDefinition> findProperties() {
}

@Override
@Deprecated // since 2.9
public AnnotatedMethod findJsonValueMethod() {
return (_propCollector == null) ? null
: _propCollector.getJsonValueMethod();
}

@Override // since 2.9
public AnnotatedMember findJsonValueAccessor() {
return (_propCollector == null) ? null
: _propCollector.getJsonValueAccessor();
}

@Override
public Set<String> getIgnoredPropertyNames() {
Set<String> ign = (_propCollector == null) ? null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ public class POJOPropertiesCollector

/**
* Method(s) marked with 'JsonValue' annotation
*<p>
* NOTE: before 2.9, was `AnnotatedMethod`; with 2.9 allows fields too
*/
protected LinkedList<AnnotatedMethod> _jsonValueGetters;
protected LinkedList<AnnotatedMember> _jsonValueAccessors;

/**
* Lazily collected list of properties that can be implicitly
Expand Down Expand Up @@ -166,20 +168,32 @@ public Map<Object, AnnotatedMember> getInjectables() {
}
return _injectables;
}

public AnnotatedMethod getJsonValueMethod()

@Deprecated // since 2.9
public AnnotatedMethod getJsonValueMethod() {
AnnotatedMember m = getJsonValueAccessor();
if (m instanceof AnnotatedMethod) {
return (AnnotatedMethod) m;
}
return null;
}

/**
* @since 2.9
*/
public AnnotatedMember getJsonValueAccessor()
{
if (!_collected) {
collectAll();
}
// If @JsonValue defined, must have a single one
if (_jsonValueGetters != null) {
if (_jsonValueGetters.size() > 1) {
reportProblem("Multiple value properties defined ("+_jsonValueGetters.get(0)+" vs "
+_jsonValueGetters.get(1)+")");
if (_jsonValueAccessors != null) {
if (_jsonValueAccessors.size() > 1) {
reportProblem("Multiple 'as-value' properties defined ("+_jsonValueAccessors.get(0)+" vs "
+_jsonValueAccessors.get(1)+")");
}
// otherwise we won't greatly care
return _jsonValueGetters.get(0);
return _jsonValueAccessors.get(0);
}
return null;
}
Expand Down Expand Up @@ -367,7 +381,29 @@ protected void _addFields(Map<String, POJOPropertyBuilder> props)
final boolean transientAsIgnoral = _config.isEnabled(MapperFeature.PROPAGATE_TRANSIENT_MARKER);

for (AnnotatedField f : _classDef.fields()) {
String implName = (ai == null) ? null : ai.findImplicitPropertyName(f);
String implName;
if (ai == null) {
implName = null;
} else {
implName = ai.findImplicitPropertyName(f);
// @JsonValue?
if (Boolean.TRUE.equals(ai.findAsValueAnnotation(f))) {
if (_jsonValueAccessors == null) {
_jsonValueAccessors = new LinkedList<>();
}
_jsonValueAccessors.add(f);
continue;
}
// @JsonAnySetter?
// !!! 20-Nov-2016, tatu: This is wrong; needs to go via AnnotationIntrospector!
if (f.hasAnnotation(JsonAnySetter.class)) {
if (_anySetterField == null) {
_anySetterField = new LinkedList<AnnotatedMember>();
}
_anySetterField.add(f);
continue;
}
}
if (implName == null) {
implName = f.getName();
}
Expand Down Expand Up @@ -417,18 +453,10 @@ protected void _addFields(Map<String, POJOPropertyBuilder> props)
* Also: if 'ignored' is set, need to included until a later point, to
* avoid losing ignoral information.
*/
if (pruneFinalFields && (pn == null) && !ignored && Modifier.isFinal(f.getModifiers())) {
if (pruneFinalFields && (pn == null) && !ignored
&& Modifier.isFinal(f.getModifiers())) {
continue;
}

//if field has annotation @JsonAnySetter
if(f.hasAnnotation(JsonAnySetter.class)) {
if (_anySetterField == null) {
_anySetterField = new LinkedList<AnnotatedMember>();
}
_anySetterField.add(f);
}

_property(props, implName).addField(f, pn, nameExplicit, visible, ignored);
}
}
Expand Down Expand Up @@ -550,11 +578,11 @@ protected void _addGetterMethod(Map<String, POJOPropertyBuilder> props,
return;
}
// @JsonValue?
if (ai.hasAsValueAnnotation(m)) {
if (_jsonValueGetters == null) {
_jsonValueGetters = new LinkedList<AnnotatedMethod>();
if (Boolean.TRUE.equals(ai.findAsValueAnnotation(m))) {
if (_jsonValueAccessors == null) {
_jsonValueAccessors = new LinkedList<>();
}
_jsonValueGetters.add(m);
_jsonValueAccessors.add(m);
return;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.fasterxml.jackson.databind.ser;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
Expand Down Expand Up @@ -224,14 +223,14 @@ public JsonSerializer<Object> createKeySerializer(SerializationConfig config,
// As per [databind#47], also need to support @JsonValue
if (ser == null) {
beanDesc = config.introspect(keyType);
AnnotatedMethod am = beanDesc.findJsonValueMethod();
AnnotatedMember am = beanDesc.findJsonValueAccessor();
if (am != null) {
final Class<?> rawType = am.getRawReturnType();
final Class<?> rawType = am.getRawType();
JsonSerializer<?> delegate = StdKeySerializers.getStdKeySerializer(config,
rawType, true);
Method m = am.getAnnotated();
if (config.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(m, config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
ClassUtil.checkAndFixAccess(am.getMember(),
config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
ser = new JsonValueSerializer(am, delegate);
} else {
Expand Down Expand Up @@ -345,14 +344,14 @@ protected final JsonSerializer<?> findSerializerByAnnotations(SerializerProvider
return SerializableSerializer.instance;
}
// Second: @JsonValue for any type
AnnotatedMethod valueMethod = beanDesc.findJsonValueMethod();
if (valueMethod != null) {
Method m = valueMethod.getAnnotated();
AnnotatedMember valueAccessor = beanDesc.findJsonValueAccessor();
if (valueAccessor != null) {
if (prov.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(m, prov.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
ClassUtil.checkAndFixAccess(valueAccessor.getMember(),
prov.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
JsonSerializer<Object> ser = findSerializerFromAnnotation(prov, valueMethod);
return new JsonValueSerializer(valueMethod, ser);
JsonSerializer<Object> ser = findSerializerFromAnnotation(prov, valueAccessor);
return new JsonValueSerializer(valueAccessor, ser);
}
// No well-known annotations...
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor;
Expand Down Expand Up @@ -38,11 +38,11 @@
public class JsonValueSerializer
extends StdSerializer<Object>
implements ContextualSerializer, JsonFormatVisitable, SchemaAware
{
{
/**
* @since 2.8 (was "plain" method before)
* @since 2.9
*/
protected final AnnotatedMethod _accessorMethod;
protected final AnnotatedMember _accessor;

protected final JsonSerializer<Object> _valueSerializer;

Expand Down Expand Up @@ -72,10 +72,10 @@ public class JsonValueSerializer
* to information we need
*/
@SuppressWarnings("unchecked")
public JsonValueSerializer(AnnotatedMethod valueMethod, JsonSerializer<?> ser)
public JsonValueSerializer(AnnotatedMember accessor, JsonSerializer<?> ser)
{
super(valueMethod.getType());
_accessorMethod = valueMethod;
super(accessor.getType());
_accessor = accessor;
_valueSerializer = (JsonSerializer<Object>) ser;
_property = null;
_forceTypeInformation = true; // gets reconsidered when we are contextualized
Expand All @@ -86,7 +86,7 @@ public JsonValueSerializer(JsonValueSerializer src, BeanProperty property,
JsonSerializer<?> ser, boolean forceTypeInfo)
{
super(_notNullClass(src.handledType()));
_accessorMethod = src._accessorMethod;
_accessor = src._accessor;
_valueSerializer = (JsonSerializer<Object>) ser;
_property = property;
_forceTypeInformation = forceTypeInfo;
Expand Down Expand Up @@ -128,7 +128,7 @@ public JsonSerializer<?> createContextual(SerializerProvider provider,
* if not, we don't really know the actual type until we get the instance.
*/
// 10-Mar-2010, tatu: Except if static typing is to be used
JavaType t = _accessorMethod.getType();
JavaType t = _accessor.getType();
if (provider.isEnabled(MapperFeature.USE_STATIC_TYPING) || t.isFinal()) {
// false -> no need to cache
/* 10-Mar-2010, tatu: Ideally we would actually separate out type
Expand Down Expand Up @@ -162,7 +162,7 @@ public JsonSerializer<?> createContextual(SerializerProvider provider,
public void serialize(Object bean, JsonGenerator gen, SerializerProvider prov) throws IOException
{
try {
Object value = _accessorMethod.getValue(bean);
Object value = _accessor.getValue(bean);
if (value == null) {
prov.defaultSerializeNull(gen);
return;
Expand All @@ -179,7 +179,7 @@ public void serialize(Object bean, JsonGenerator gen, SerializerProvider prov) t
}
ser.serialize(value, gen, prov);
} catch (Exception e) {
wrapAndThrow(prov, e, bean, _accessorMethod.getName() + "()");
wrapAndThrow(prov, e, bean, _accessor.getName() + "()");
}
}

Expand All @@ -190,7 +190,7 @@ public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider
// Regardless of other parts, first need to find value to serialize:
Object value = null;
try {
value = _accessorMethod.getValue(bean);
value = _accessor.getValue(bean);
// and if we got null, can also just write it directly
if (value == null) {
provider.defaultSerializeNull(gen);
Expand All @@ -217,7 +217,7 @@ public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider
TypeSerializerRerouter rr = new TypeSerializerRerouter(typeSer0, bean);
ser.serializeWithType(value, gen, provider, rr);
} catch (Exception e) {
wrapAndThrow(provider, e, bean, _accessorMethod.getName() + "()");
wrapAndThrow(provider, e, bean, _accessor.getName() + "()");
}
}

Expand Down Expand Up @@ -245,8 +245,8 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t
*
* Note that meaning of JsonValue, then, is very different for Enums. Sigh.
*/
final JavaType type = _accessorMethod.getType();
Class<?> declaring = _accessorMethod.getDeclaringClass();
final JavaType type = _accessor.getType();
Class<?> declaring = _accessor.getDeclaringClass();
if ((declaring != null) && declaring.isEnum()) {
if (_acceptJsonFormatVisitorForEnum(visitor, typeHint, declaring)) {
return;
Expand Down Expand Up @@ -285,14 +285,14 @@ protected boolean _acceptJsonFormatVisitorForEnum(JsonFormatVisitorWrapper visit
// 21-Apr-2016, tatu: This is convoluted to the max, but essentially we
// call `@JsonValue`-annotated accessor method on all Enum members,
// so it all "works out". To some degree.
enums.add(String.valueOf(_accessorMethod.callOn(en)));
enums.add(String.valueOf(_accessor.getValue(en)));
} catch (Exception e) {
Throwable t = e;
while (t instanceof InvocationTargetException && t.getCause() != null) {
t = t.getCause();
}
ClassUtil.throwIfError(t);
throw JsonMappingException.wrapWithPath(t, en, _accessorMethod.getName() + "()");
throw JsonMappingException.wrapWithPath(t, en, _accessor.getName() + "()");
}
}
stringVisitor.enumTypes(enums);
Expand Down Expand Up @@ -324,7 +324,7 @@ protected boolean isNaturalTypeWithStdHandling(Class<?> rawType, JsonSerializer<

@Override
public String toString() {
return "(@JsonValue serializer for method " + _accessorMethod.getDeclaringClass() + "#" + _accessorMethod.getName() + ")";
return "(@JsonValue serializer for method " + _accessor.getDeclaringClass() + "#" + _accessor.getName() + ")";
}

/*
Expand Down
Loading

0 comments on commit d0daf23

Please sign in to comment.