package com.fasterxml.jackson.databind.introspect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.*;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.ext.Java7Support;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.AttributePropertyWriter;
import com.fasterxml.jackson.databind.ser.std.RawSerializer;
import com.fasterxml.jackson.databind.type.MapLikeType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.*;

AnnotationIntrospector implementation that handles standard Jackson annotations.
/** * {@link AnnotationIntrospector} implementation that handles standard * Jackson annotations. */
public class JacksonAnnotationIntrospector extends AnnotationIntrospector implements java.io.Serializable { private static final long serialVersionUID = 1L; @SuppressWarnings("unchecked") private final static Class<? extends Annotation>[] ANNOTATIONS_TO_INFER_SER = (Class<? extends Annotation>[]) new Class<?>[] { JsonSerialize.class, JsonView.class, JsonFormat.class, JsonTypeInfo.class, JsonRawValue.class, JsonUnwrapped.class, JsonBackReference.class, JsonManagedReference.class }; @SuppressWarnings("unchecked") private final static Class<? extends Annotation>[] ANNOTATIONS_TO_INFER_DESER = (Class<? extends Annotation>[]) new Class<?>[] { JsonDeserialize.class, JsonView.class, JsonFormat.class, JsonTypeInfo.class, JsonUnwrapped.class, JsonBackReference.class, JsonManagedReference.class, JsonMerge.class // since 2.9 }; // NOTE: loading of Java7 dependencies is encapsulated by handlers in Java7Support, // here we do not really need any handling; but for extra-safety use try-catch private static final Java7Support _java7Helper; static { Java7Support x = null; try { x = Java7Support.instance(); } catch (Throwable t) { } _java7Helper = x; }
Since introspection of annotation types is a performance issue in some use cases (rare, but do exist), let's try a simple cache to reduce need for actual meta-annotation introspection.

Non-final only because it needs to be re-created after deserialization.

Since:2.7
/** * Since introspection of annotation types is a performance issue in some * use cases (rare, but do exist), let's try a simple cache to reduce * need for actual meta-annotation introspection. *<p> * Non-final only because it needs to be re-created after deserialization. * * @since 2.7 */
protected transient LRUMap<Class<?>,Boolean> _annotationsInside = new LRUMap<Class<?>,Boolean>(48, 48); /* /********************************************************** /* Local configuration settings /********************************************************** */
See setConstructorPropertiesImpliesCreator(boolean) for explanation.

Defaults to true.

Since:2.7.4
/** * See {@link #setConstructorPropertiesImpliesCreator(boolean)} for * explanation. *<p> * Defaults to true. * * @since 2.7.4 */
protected boolean _cfgConstructorPropertiesImpliesCreator = true; /* /********************************************************** /* Life-cycle /********************************************************** */ public JacksonAnnotationIntrospector() { } @Override public Version version() { return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; } protected Object readResolve() { if (_annotationsInside == null) { _annotationsInside = new LRUMap<Class<?>,Boolean>(48, 48); } return this; } /* /********************************************************** /* Configuration /********************************************************** */
Method for changing behavior of ConstructorProperties: if set to `true`, existence DOES indicate that the given constructor should be considered a creator; `false` that it should NOT be considered a creator without explicit use of JsonCreator annotation.

Default setting is `true`

Since:2.7.4
/** * Method for changing behavior of {@link java.beans.ConstructorProperties}: * if set to `true`, existence DOES indicate that the given constructor should * be considered a creator; `false` that it should NOT be considered a creator * without explicit use of <code>JsonCreator</code> annotation. *<p> * Default setting is `true` * * @since 2.7.4 */
public JacksonAnnotationIntrospector setConstructorPropertiesImpliesCreator(boolean b) { _cfgConstructorPropertiesImpliesCreator = b; return this; } /* /********************************************************** /* General annotation properties /********************************************************** */
Annotations with meta-annotation JacksonAnnotationsInside are considered bundles.
/** * Annotations with meta-annotation {@link JacksonAnnotationsInside} * are considered bundles. */
@Override public boolean isAnnotationBundle(Annotation ann) { // 22-Sep-2015, tatu: Caching here has modest effect on JavaSE, and only // mostly in degenerate cases where introspection used more often than // it should (like recreating ObjectMapper once per read/write). // But it may be more beneficial on platforms like Android (should verify) Class<?> type = ann.annotationType(); Boolean b = _annotationsInside.get(type); if (b == null) { b = type.getAnnotation(JacksonAnnotationsInside.class) != null; _annotationsInside.putIfAbsent(type, b); } return b.booleanValue(); } /* /********************************************************** /* General annotations /********************************************************** */
Since 2.6, we have supported use of JsonProperty for specifying explicit serialized name
/** * Since 2.6, we have supported use of {@link JsonProperty} for specifying * explicit serialized name */
@Override @Deprecated // since 2.8 public String findEnumValue(Enum<?> value) { // 11-Jun-2015, tatu: As per [databind#677], need to allow explicit naming. // Unfortunately cannot quite use standard AnnotatedClass here (due to various // reasons, including odd representation JVM uses); has to do for now try { // We know that values are actually static fields with matching name so: Field f = value.getDeclaringClass().getField(value.name()); if (f != null) { JsonProperty prop = f.getAnnotation(JsonProperty.class); if (prop != null) { String n = prop.value(); if (n != null && !n.isEmpty()) { return n; } } } } catch (SecurityException e) { // 17-Sep-2015, tatu: Anything we could/should do here? } catch (NoSuchFieldException e) { // 17-Sep-2015, tatu: should not really happen. But... can we do anything? } return value.name(); } @Override // since 2.7 public String[] findEnumValues(Class<?> enumType, Enum<?>[] enumValues, String[] names) { HashMap<String,String> expl = null; for (Field f : enumType.getDeclaredFields()) { if (!f.isEnumConstant()) { continue; } JsonProperty prop = f.getAnnotation(JsonProperty.class); if (prop == null) { continue; } String n = prop.value(); if (n.isEmpty()) { continue; } if (expl == null) { expl = new HashMap<String,String>(); } expl.put(f.getName(), n); } // and then stitch them together if and as necessary if (expl != null) { for (int i = 0, end = enumValues.length; i < end; ++i) { String defName = enumValues[i].name(); String explValue = expl.get(defName); if (explValue != null) { names[i] = explValue; } } } return names; } @Override // since 2.11 public void findEnumAliases(Class<?> enumType, Enum<?>[] enumValues, String[][] aliasList) { // Main complication: discrepancy between Field that represent enum value, // Enum abstraction; joint by name but not reference for (Field f : enumType.getDeclaredFields()) { if (f.isEnumConstant()) { JsonAlias aliasAnnotation = f.getAnnotation(JsonAlias.class); if (aliasAnnotation != null) { String[] aliases = aliasAnnotation.value(); if (aliases.length != 0) { final String name = f.getName(); // Find matching enum (could create Ma for (int i = 0, end = enumValues.length; i < end; ++i) { if (name.equals(enumValues[i].name())) { aliasList[i] = aliases; } } } } } } }
Finds the Enum value that should be considered the default value, if possible.

This implementation relies on JsonEnumDefaultValue annotation to determine the default value if present.

Params:
  • enumCls – The Enum class to scan for the default value.
Returns:null if none found or it's not possible to determine one.
Since:2.8
/** * Finds the Enum value that should be considered the default value, if possible. * <p> * This implementation relies on {@link JsonEnumDefaultValue} annotation to determine the default value if present. * * @param enumCls The Enum class to scan for the default value. * @return null if none found or it's not possible to determine one. * @since 2.8 */
@Override public Enum<?> findDefaultEnumValue(Class<Enum<?>> enumCls) { return ClassUtil.findFirstAnnotatedEnumValue(enumCls, JsonEnumDefaultValue.class); } /* /********************************************************** /* General class annotations /********************************************************** */ @Override public PropertyName findRootName(AnnotatedClass ac) { JsonRootName ann = _findAnnotation(ac, JsonRootName.class); if (ann == null) { return null; } String ns = ann.namespace(); if (ns != null && ns.isEmpty()) { ns = null; } return PropertyName.construct(ann.value(), ns); } @Override public Boolean isIgnorableType(AnnotatedClass ac) { JsonIgnoreType ignore = _findAnnotation(ac, JsonIgnoreType.class); return (ignore == null) ? null : ignore.value(); } @Override // since 2.12 public JsonIgnoreProperties.Value findPropertyIgnoralByName(MapperConfig<?> config, Annotated a) { JsonIgnoreProperties v = _findAnnotation(a, JsonIgnoreProperties.class); if (v == null) { return JsonIgnoreProperties.Value.empty(); } return JsonIgnoreProperties.Value.from(v); } // Keep around until 3.x @Deprecated // since 2.12 @Override public JsonIgnoreProperties.Value findPropertyIgnorals(Annotated ac) { return findPropertyIgnoralByName(null, ac); } @Override public JsonIncludeProperties.Value findPropertyInclusionByName(MapperConfig<?> config, Annotated a) { JsonIncludeProperties v = _findAnnotation(a, JsonIncludeProperties.class); if (v == null) { return JsonIncludeProperties.Value.all(); } return JsonIncludeProperties.Value.from(v); } @Override public Object findFilterId(Annotated a) { JsonFilter ann = _findAnnotation(a, JsonFilter.class); if (ann != null) { String id = ann.value(); // Empty String is same as not having annotation, to allow overrides if (!id.isEmpty()) { return id; } } return null; } @Override public Object findNamingStrategy(AnnotatedClass ac) { JsonNaming ann = _findAnnotation(ac, JsonNaming.class); return (ann == null) ? null : ann.value(); } @Override public String findClassDescription(AnnotatedClass ac) { JsonClassDescription ann = _findAnnotation(ac, JsonClassDescription.class); return (ann == null) ? null : ann.value(); } /* /********************************************************** /* Property auto-detection /********************************************************** */ @Override public VisibilityChecker<?> findAutoDetectVisibility(AnnotatedClass ac, VisibilityChecker<?> checker) { JsonAutoDetect ann = _findAnnotation(ac, JsonAutoDetect.class); return (ann == null) ? checker : checker.with(ann); } /* /********************************************************** /* General member (field, method/constructor) annotations /********************************************************** */ @Override public String findImplicitPropertyName(AnnotatedMember m) { PropertyName n = _findConstructorName(m); return (n == null) ? null : n.getSimpleName(); } @Override public List<PropertyName> findPropertyAliases(Annotated m) { JsonAlias ann = _findAnnotation(m, JsonAlias.class); if (ann == null) { return null; } String[] strs = ann.value(); final int len = strs.length; if (len == 0) { return Collections.emptyList(); } List<PropertyName> result = new ArrayList<>(len); for (int i = 0; i < len; ++i) { result.add(PropertyName.construct(strs[i])); } return result; } @Override public boolean hasIgnoreMarker(AnnotatedMember m) { return _isIgnorable(m); } @Override public Boolean hasRequiredMarker(AnnotatedMember m) { JsonProperty ann = _findAnnotation(m, JsonProperty.class); if (ann != null) { return ann.required(); } return null; } @Override public JsonProperty.Access findPropertyAccess(Annotated m) { JsonProperty ann = _findAnnotation(m, JsonProperty.class); if (ann != null) { return ann.access(); } return null; } @Override public String findPropertyDescription(Annotated ann) { JsonPropertyDescription desc = _findAnnotation(ann, JsonPropertyDescription.class); return (desc == null) ? null : desc.value(); } @Override public Integer findPropertyIndex(Annotated ann) { JsonProperty prop = _findAnnotation(ann, JsonProperty.class); if (prop != null) { int ix = prop.index(); if (ix != JsonProperty.INDEX_UNKNOWN) { return Integer.valueOf(ix); } } return null; } @Override public String findPropertyDefaultValue(Annotated ann) { JsonProperty prop = _findAnnotation(ann, JsonProperty.class); if (prop == null) { return null; } String str = prop.defaultValue(); // Since annotations do not allow nulls, need to assume empty means "none" return str.isEmpty() ? null : str; } @Override public JsonFormat.Value findFormat(Annotated ann) { JsonFormat f = _findAnnotation(ann, JsonFormat.class); // NOTE: could also just call `JsonFormat.Value.from()` with `null` // too, but that returns "empty" instance return (f == null) ? null : JsonFormat.Value.from(f); } @Override public ReferenceProperty findReferenceType(AnnotatedMember member) { JsonManagedReference ref1 = _findAnnotation(member, JsonManagedReference.class); if (ref1 != null) { return AnnotationIntrospector.ReferenceProperty.managed(ref1.value()); } JsonBackReference ref2 = _findAnnotation(member, JsonBackReference.class); if (ref2 != null) { return AnnotationIntrospector.ReferenceProperty.back(ref2.value()); } return null; } @Override public NameTransformer findUnwrappingNameTransformer(AnnotatedMember member) { JsonUnwrapped ann = _findAnnotation(member, JsonUnwrapped.class); // if not enabled, just means annotation is not enabled; not necessarily // that unwrapping should not be done (relevant when using chained introspectors) if (ann == null || !ann.enabled()) { return null; } String prefix = ann.prefix(); String suffix = ann.suffix(); return NameTransformer.simpleTransformer(prefix, suffix); } @Override // since 2.9 public JacksonInject.Value findInjectableValue(AnnotatedMember m) { JacksonInject ann = _findAnnotation(m, JacksonInject.class); if (ann == null) { return null; } // Empty String means that we should use name of declared value class. JacksonInject.Value v = JacksonInject.Value.from(ann); if (!v.hasId()) { Object id; // slight complication; for setters, type if (!(m instanceof AnnotatedMethod)) { id = m.getRawType().getName(); } else { AnnotatedMethod am = (AnnotatedMethod) m; if (am.getParameterCount() == 0) { // getter id = m.getRawType().getName(); } else { // setter id = am.getRawParameterType(0).getName(); } } v = v.withId(id); } return v; } @Override @Deprecated // since 2.9 public Object findInjectableValueId(AnnotatedMember m) { JacksonInject.Value v = findInjectableValue(m); return (v == null) ? null : v.getId(); } @Override public Class<?>[] findViews(Annotated a) { JsonView ann = _findAnnotation(a, JsonView.class); return (ann == null) ? null : ann.value(); }
Specific implementation that will use following tie-breaker on given setter parameter types:
  1. If either one is primitive type then either return null (both primitives) or one that is primitive (when only primitive)
  2. If only one is of type String, return that setter
  3. Otherwise return null
Returning null will indicate that resolution could not be done.
/** * Specific implementation that will use following tie-breaker on * given setter parameter types: *<ol> * <li>If either one is primitive type then either return {@code null} * (both primitives) or one that is primitive (when only primitive) * </li> * <li>If only one is of type {@code String}, return that setter * </li> * <li>Otherwise return {@code null} * </li> * </ol> * Returning {@code null} will indicate that resolution could not be done. */
@Override // since 2.7 public AnnotatedMethod resolveSetterConflict(MapperConfig<?> config, AnnotatedMethod setter1, AnnotatedMethod setter2) { Class<?> cls1 = setter1.getRawParameterType(0); Class<?> cls2 = setter2.getRawParameterType(0); // First: prefer primitives over non-primitives // 11-Dec-2015, tatu: TODO, perhaps consider wrappers for primitives too? if (cls1.isPrimitive()) { if (!cls2.isPrimitive()) { return setter1; } // 10-May-2021, tatu: if both primitives cannot decide return null; } else if (cls2.isPrimitive()) { return setter2; } if (cls1 == String.class) { if (cls2 != String.class) { return setter1; } } else if (cls2 == String.class) { return setter2; } return null; } @Override // since 2.11 public PropertyName findRenameByField(MapperConfig<?> config, AnnotatedField f, PropertyName implName) { // Nothing to report, only used by modules. But define just as documentation return null; } /* /********************************************************** /* Annotations for Polymorphic Type handling /********************************************************** */ @Override public TypeResolverBuilder<?> findTypeResolver(MapperConfig<?> config, AnnotatedClass ac, JavaType baseType) { return _findTypeResolver(config, ac, baseType); } @Override public TypeResolverBuilder<?> findPropertyTypeResolver(MapperConfig<?> config, AnnotatedMember am, JavaType baseType) { // As per definition of @JsonTypeInfo, should only apply to contents of container // (collection, map) types, not container types themselves: // 17-Apr-2016, tatu: For 2.7.4 make sure ReferenceType also included if (baseType.isContainerType() || baseType.isReferenceType()) { return null; } // No per-member type overrides (yet) return _findTypeResolver(config, am, baseType); } @Override public TypeResolverBuilder<?> findPropertyContentTypeResolver(MapperConfig<?> config, AnnotatedMember am, JavaType containerType) { // First: let's ensure property is a container type: caller should have // verified but just to be sure if (containerType.getContentType() == null) { throw new IllegalArgumentException("Must call method with a container or reference type (got "+containerType+")"); } return _findTypeResolver(config, am, containerType); } @Override public List<NamedType> findSubtypes(Annotated a) { JsonSubTypes t = _findAnnotation(a, JsonSubTypes.class); if (t == null) return null; JsonSubTypes.Type[] types = t.value(); ArrayList<NamedType> result = new ArrayList<NamedType>(types.length); for (JsonSubTypes.Type type : types) { result.add(new NamedType(type.value(), type.name())); // [databind#2761]: alternative set of names to use for (String name : type.names()) { result.add(new NamedType(type.value(), name)); } } return result; } @Override public String findTypeName(AnnotatedClass ac) { JsonTypeName tn = _findAnnotation(ac, JsonTypeName.class); return (tn == null) ? null : tn.value(); } @Override public Boolean isTypeId(AnnotatedMember member) { return _hasAnnotation(member, JsonTypeId.class); } /* /********************************************************** /* Annotations for Object Id handling /********************************************************** */ @Override public ObjectIdInfo findObjectIdInfo(Annotated ann) { JsonIdentityInfo info = _findAnnotation(ann, JsonIdentityInfo.class); if (info == null || info.generator() == ObjectIdGenerators.None.class) { return null; } // In future may need to allow passing namespace? PropertyName name = PropertyName.construct(info.property()); return new ObjectIdInfo(name, info.scope(), info.generator(), info.resolver()); } @Override public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectIdInfo) { JsonIdentityReference ref = _findAnnotation(ann, JsonIdentityReference.class); if (ref == null) { return objectIdInfo; } if (objectIdInfo == null) { objectIdInfo = ObjectIdInfo.empty(); } return objectIdInfo.withAlwaysAsId(ref.alwaysAsId()); } /* /********************************************************** /* Serialization: general annotations /********************************************************** */ @Override public Object findSerializer(Annotated a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); if (ann != null) { @SuppressWarnings("rawtypes") Class<? extends JsonSerializer> serClass = ann.using(); if (serClass != JsonSerializer.None.class) { return serClass; } } /* 18-Oct-2010, tatu: [JACKSON-351] @JsonRawValue handled just here, for now; * if we need to get raw indicator from other sources need to add * separate accessor within {@link AnnotationIntrospector} interface. */ JsonRawValue annRaw = _findAnnotation(a, JsonRawValue.class); if ((annRaw != null) && annRaw.value()) { // let's construct instance with nominal type: Class<?> cls = a.getRawType(); return new RawSerializer<Object>(cls); } return null; } @Override public Object findKeySerializer(Annotated a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); if (ann != null) { @SuppressWarnings("rawtypes") Class<? extends JsonSerializer> serClass = ann.keyUsing(); if (serClass != JsonSerializer.None.class) { return serClass; } } return null; } @Override public Object findContentSerializer(Annotated a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); if (ann != null) { @SuppressWarnings("rawtypes") Class<? extends JsonSerializer> serClass = ann.contentUsing(); if (serClass != JsonSerializer.None.class) { return serClass; } } return null; } @Override public Object findNullSerializer(Annotated a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); if (ann != null) { @SuppressWarnings("rawtypes") Class<? extends JsonSerializer> serClass = ann.nullsUsing(); if (serClass != JsonSerializer.None.class) { return serClass; } } return null; } @Override public JsonInclude.Value findPropertyInclusion(Annotated a) { JsonInclude inc = _findAnnotation(a, JsonInclude.class); JsonInclude.Value value = (inc == null) ? JsonInclude.Value.empty() : JsonInclude.Value.from(inc); // only consider deprecated variant if we didn't have non-deprecated one: if (value.getValueInclusion() == JsonInclude.Include.USE_DEFAULTS) { value = _refinePropertyInclusion(a, value); } return value; } @SuppressWarnings("deprecation") private JsonInclude.Value _refinePropertyInclusion(Annotated a, JsonInclude.Value value) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); if (ann != null) { switch (ann.include()) { case ALWAYS: return value.withValueInclusion(JsonInclude.Include.ALWAYS); case NON_NULL: return value.withValueInclusion(JsonInclude.Include.NON_NULL); case NON_DEFAULT: return value.withValueInclusion(JsonInclude.Include.NON_DEFAULT); case NON_EMPTY: return value.withValueInclusion(JsonInclude.Include.NON_EMPTY); case DEFAULT_INCLUSION: default: } } return value; } @Override public JsonSerialize.Typing findSerializationTyping(Annotated a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); return (ann == null) ? null : ann.typing(); } @Override public Object findSerializationConverter(Annotated a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); return (ann == null) ? null : _classIfExplicit(ann.converter(), Converter.None.class); } @Override public Object findSerializationContentConverter(AnnotatedMember a) { JsonSerialize ann = _findAnnotation(a, JsonSerialize.class); return (ann == null) ? null : _classIfExplicit(ann.contentConverter(), Converter.None.class); } /* /********************************************************** /* Serialization: type refinements /********************************************************** */ @Override public JavaType refineSerializationType(final MapperConfig<?> config, final Annotated a, final JavaType baseType) throws JsonMappingException { JavaType type = baseType; final TypeFactory tf = config.getTypeFactory(); final JsonSerialize jsonSer = _findAnnotation(a, JsonSerialize.class); // Ok: start by refining the main type itself; common to all types final Class<?> serClass = (jsonSer == null) ? null : _classIfExplicit(jsonSer.as()); if (serClass != null) { if (type.hasRawClass(serClass)) { // 30-Nov-2015, tatu: As per [databind#1023], need to allow forcing of // static typing this way type = type.withStaticTyping(); } else { Class<?> currRaw = type.getRawClass(); try { // 11-Oct-2015, tatu: For deser, we call `TypeFactory.constructSpecializedType()`, // may be needed here too in future? if (serClass.isAssignableFrom(currRaw)) { // common case type = tf.constructGeneralizedType(type, serClass); } else if (currRaw.isAssignableFrom(serClass)) { // specialization, ok as well type = tf.constructSpecializedType(type, serClass); } else if (_primitiveAndWrapper(currRaw, serClass)) { // 27-Apr-2017, tatu: [databind#1592] ignore primitive<->wrapper refinements type = type.withStaticTyping(); } else { throw _databindException( String.format("Cannot refine serialization type %s into %s; types not related", type, serClass.getName())); } } catch (IllegalArgumentException iae) { throw _databindException(iae, String.format("Failed to widen type %s with annotation (value %s), from '%s': %s", type, serClass.getName(), a.getName(), iae.getMessage())); } } } // Then further processing for container types // First, key type (for Maps, Map-like types): if (type.isMapLikeType()) { JavaType keyType = type.getKeyType(); final Class<?> keyClass = (jsonSer == null) ? null : _classIfExplicit(jsonSer.keyAs()); if (keyClass != null) { if (keyType.hasRawClass(keyClass)) { keyType = keyType.withStaticTyping(); } else { Class<?> currRaw = keyType.getRawClass(); try { // 19-May-2016, tatu: As per [databind#1231], [databind#1178] may need to actually // specialize (narrow) type sometimes, even if more commonly opposite // is needed. if (keyClass.isAssignableFrom(currRaw)) { // common case keyType = tf.constructGeneralizedType(keyType, keyClass); } else if (currRaw.isAssignableFrom(keyClass)) { // specialization, ok as well keyType = tf.constructSpecializedType(keyType, keyClass); } else if (_primitiveAndWrapper(currRaw, keyClass)) { // 27-Apr-2017, tatu: [databind#1592] ignore primitive<->wrapper refinements keyType = keyType.withStaticTyping(); } else { throw _databindException( String.format("Cannot refine serialization key type %s into %s; types not related", keyType, keyClass.getName())); } } catch (IllegalArgumentException iae) { throw _databindException(iae, String.format("Failed to widen key type of %s with concrete-type annotation (value %s), from '%s': %s", type, keyClass.getName(), a.getName(), iae.getMessage())); } } type = ((MapLikeType) type).withKeyType(keyType); } } JavaType contentType = type.getContentType(); if (contentType != null) { // collection[like], map[like], array, reference // And then value types for all containers: final Class<?> contentClass = (jsonSer == null) ? null : _classIfExplicit(jsonSer.contentAs()); if (contentClass != null) { if (contentType.hasRawClass(contentClass)) { contentType = contentType.withStaticTyping(); } else { // 03-Apr-2016, tatu: As per [databind#1178], may need to actually // specialize (narrow) type sometimes, even if more commonly opposite // is needed. Class<?> currRaw = contentType.getRawClass(); try { if (contentClass.isAssignableFrom(currRaw)) { // common case contentType = tf.constructGeneralizedType(contentType, contentClass); } else if (currRaw.isAssignableFrom(contentClass)) { // specialization, ok as well contentType = tf.constructSpecializedType(contentType, contentClass); } else if (_primitiveAndWrapper(currRaw, contentClass)) { // 27-Apr-2017, tatu: [databind#1592] ignore primitive<->wrapper refinements contentType = contentType.withStaticTyping(); } else { throw _databindException( String.format("Cannot refine serialization content type %s into %s; types not related", contentType, contentClass.getName())); } } catch (IllegalArgumentException iae) { // shouldn't really happen throw _databindException(iae, String.format("Internal error: failed to refine value type of %s with concrete-type annotation (value %s), from '%s': %s", type, contentClass.getName(), a.getName(), iae.getMessage())); } } type = type.withContentType(contentType); } } return type; } @Override @Deprecated // since 2.7 public Class<?> findSerializationType(Annotated am) { return null; } @Override @Deprecated // since 2.7 public Class<?> findSerializationKeyType(Annotated am, JavaType baseType) { return null; } @Override @Deprecated // since 2.7 public Class<?> findSerializationContentType(Annotated am, JavaType baseType) { return null; } /* /********************************************************** /* Serialization: class annotations /********************************************************** */ @Override public String[] findSerializationPropertyOrder(AnnotatedClass ac) { JsonPropertyOrder order = _findAnnotation(ac, JsonPropertyOrder.class); return (order == null) ? null : order.value(); } @Override public Boolean findSerializationSortAlphabetically(Annotated ann) { return _findSortAlpha(ann); } private final Boolean _findSortAlpha(Annotated ann) { JsonPropertyOrder order = _findAnnotation(ann, JsonPropertyOrder.class); // 23-Jun-2015, tatu: as per [databind#840], let's only consider // `true` to have any significance. if ((order != null) && order.alphabetic()) { return Boolean.TRUE; } return null; } @Override public void findAndAddVirtualProperties(MapperConfig<?> config, AnnotatedClass ac, List<BeanPropertyWriter> properties) { JsonAppend ann = _findAnnotation(ac, JsonAppend.class); if (ann == null) { return; } final boolean prepend = ann.prepend(); JavaType propType = null; // First: any attribute-backed properties? JsonAppend.Attr[] attrs = ann.attrs(); for (int i = 0, len = attrs.length; i < len; ++i) { if (propType == null) { propType = config.constructType(Object.class); } BeanPropertyWriter bpw = _constructVirtualProperty(attrs[i], config, ac, propType); if (prepend) { properties.add(i, bpw); } else { properties.add(bpw); } } // Then: general-purpose virtual properties? JsonAppend.Prop[] props = ann.props(); for (int i = 0, len = props.length; i < len; ++i) { BeanPropertyWriter bpw = _constructVirtualProperty(props[i], config, ac); if (prepend) { properties.add(i, bpw); } else { properties.add(bpw); } } } protected BeanPropertyWriter _constructVirtualProperty(JsonAppend.Attr attr, MapperConfig<?> config, AnnotatedClass ac, JavaType type) { PropertyMetadata metadata = attr.required() ? PropertyMetadata.STD_REQUIRED : PropertyMetadata.STD_OPTIONAL; // could add Index, Description in future, if those matter String attrName = attr.value(); // allow explicit renaming; if none, default to attribute name PropertyName propName = _propertyName(attr.propName(), attr.propNamespace()); if (!propName.hasSimpleName()) { propName = PropertyName.construct(attrName); } // now, then, we need a placeholder for member (no real Field/Method): AnnotatedMember member = new VirtualAnnotatedMember(ac, ac.getRawType(), attrName, type); // and with that and property definition SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(config, member, propName, metadata, attr.include()); // can construct the property writer return AttributePropertyWriter.construct(attrName, propDef, ac.getAnnotations(), type); } protected BeanPropertyWriter _constructVirtualProperty(JsonAppend.Prop prop, MapperConfig<?> config, AnnotatedClass ac) { PropertyMetadata metadata = prop.required() ? PropertyMetadata.STD_REQUIRED : PropertyMetadata.STD_OPTIONAL; PropertyName propName = _propertyName(prop.name(), prop.namespace()); JavaType type = config.constructType(prop.type()); // now, then, we need a placeholder for member (no real Field/Method): AnnotatedMember member = new VirtualAnnotatedMember(ac, ac.getRawType(), propName.getSimpleName(), type); // and with that and property definition SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(config, member, propName, metadata, prop.include()); Class<?> implClass = prop.value(); HandlerInstantiator hi = config.getHandlerInstantiator(); VirtualBeanPropertyWriter bpw = (hi == null) ? null : hi.virtualPropertyWriterInstance(config, implClass); if (bpw == null) { bpw = (VirtualBeanPropertyWriter) ClassUtil.createInstance(implClass, config.canOverrideAccessModifiers()); } // one more thing: give it necessary contextual information return bpw.withConfig(config, ac, propDef, type); } /* /********************************************************** /* Serialization: property annotations /********************************************************** */ @Override public PropertyName findNameForSerialization(Annotated a) { boolean useDefault = false; JsonGetter jg = _findAnnotation(a, JsonGetter.class); if (jg != null) { String s = jg.value(); // 04-May-2018, tatu: Should allow for "nameless" `@JsonGetter` too if (!s.isEmpty()) { return PropertyName.construct(s); } useDefault = true; } JsonProperty pann = _findAnnotation(a, JsonProperty.class); if (pann != null) { // 14-Nov-2020, tatu: "namespace" added in 2.12 String ns = pann.namespace(); if (ns != null && ns.isEmpty()) { ns = null; } return PropertyName.construct(pann.value(), ns); } if (useDefault || _hasOneOf(a, ANNOTATIONS_TO_INFER_SER)) { return PropertyName.USE_DEFAULT; } return null; } @Override // since 2.12 public Boolean hasAsKey(MapperConfig<?> config, Annotated a) { JsonKey ann = _findAnnotation(a, JsonKey.class); if (ann == null) { return null; } return ann.value(); } @Override // since 2.9 public Boolean hasAsValue(Annotated a) { JsonValue ann = _findAnnotation(a, JsonValue.class); if (ann == null) { return null; } return ann.value(); } @Override // since 2.9 public Boolean hasAnyGetter(Annotated a) { JsonAnyGetter ann = _findAnnotation(a, JsonAnyGetter.class); if (ann == null) { return null; } return ann.enabled(); } @Override @Deprecated // since 2.9 public boolean hasAnyGetterAnnotation(AnnotatedMethod am) { // No dedicated disabling; regular @JsonIgnore used if needs to be ignored (handled separately) return _hasAnnotation(am, JsonAnyGetter.class); } @Override @Deprecated // since 2.9 public boolean hasAsValueAnnotation(AnnotatedMethod am) { JsonValue ann = _findAnnotation(am, JsonValue.class); // value of 'false' means disabled... return (ann != null) && ann.value(); } /* /********************************************************** /* Deserialization: general annotations /********************************************************** */ @Override public Object findDeserializer(Annotated a) { JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); if (ann != null) { @SuppressWarnings("rawtypes") Class<? extends JsonDeserializer> deserClass = ann.using(); if (deserClass != JsonDeserializer.None.class) { return deserClass; } } return null; } @Override public Object findKeyDeserializer(Annotated a) { JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); if (ann != null) { Class<? extends KeyDeserializer> deserClass = ann.keyUsing(); if (deserClass != KeyDeserializer.None.class) { return deserClass; } } return null; } @Override public Object findContentDeserializer(Annotated a) { JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); if (ann != null) { @SuppressWarnings("rawtypes") Class<? extends JsonDeserializer> deserClass = ann.contentUsing(); if (deserClass != JsonDeserializer.None.class) { return deserClass; } } return null; } @Override public Object findDeserializationConverter(Annotated a) { JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); return (ann == null) ? null : _classIfExplicit(ann.converter(), Converter.None.class); } @Override public Object findDeserializationContentConverter(AnnotatedMember a) { JsonDeserialize ann = _findAnnotation(a, JsonDeserialize.class); return (ann == null) ? null : _classIfExplicit(ann.contentConverter(), Converter.None.class); } /* /********************************************************** /* Deserialization: type modifications /********************************************************** */ @Override public JavaType refineDeserializationType(final MapperConfig<?> config, final Annotated a, final JavaType baseType) throws JsonMappingException { JavaType type = baseType; final TypeFactory tf = config.getTypeFactory(); final JsonDeserialize jsonDeser = _findAnnotation(a, JsonDeserialize.class); // Ok: start by refining the main type itself; common to all types final Class<?> valueClass = (jsonDeser == null) ? null : _classIfExplicit(jsonDeser.as()); if ((valueClass != null) && !type.hasRawClass(valueClass) && !_primitiveAndWrapper(type, valueClass)) { try { type = tf.constructSpecializedType(type, valueClass); } catch (IllegalArgumentException iae) { throw _databindException(iae, String.format("Failed to narrow type %s with annotation (value %s), from '%s': %s", type, valueClass.getName(), a.getName(), iae.getMessage())); } } // Then further processing for container types // First, key type (for Maps, Map-like types): if (type.isMapLikeType()) { JavaType keyType = type.getKeyType(); final Class<?> keyClass = (jsonDeser == null) ? null : _classIfExplicit(jsonDeser.keyAs()); if ((keyClass != null) && !_primitiveAndWrapper(keyType, keyClass)) { try { keyType = tf.constructSpecializedType(keyType, keyClass); type = ((MapLikeType) type).withKeyType(keyType); } catch (IllegalArgumentException iae) { throw _databindException(iae, String.format("Failed to narrow key type of %s with concrete-type annotation (value %s), from '%s': %s", type, keyClass.getName(), a.getName(), iae.getMessage())); } } } JavaType contentType = type.getContentType(); if (contentType != null) { // collection[like], map[like], array, reference // And then value types for all containers: final Class<?> contentClass = (jsonDeser == null) ? null : _classIfExplicit(jsonDeser.contentAs()); if ((contentClass != null) && !_primitiveAndWrapper(contentType, contentClass)) { try { contentType = tf.constructSpecializedType(contentType, contentClass); type = type.withContentType(contentType); } catch (IllegalArgumentException iae) { throw _databindException(iae, String.format("Failed to narrow value type of %s with concrete-type annotation (value %s), from '%s': %s", type, contentClass.getName(), a.getName(), iae.getMessage())); } } } return type; } @Override @Deprecated // since 2.7 public Class<?> findDeserializationContentType(Annotated am, JavaType baseContentType) { return null; } @Override @Deprecated // since 2.7 public Class<?> findDeserializationType(Annotated am, JavaType baseType) { return null; } @Override @Deprecated // since 2.7 public Class<?> findDeserializationKeyType(Annotated am, JavaType baseKeyType) { return null; } /* /********************************************************** /* Deserialization: Class annotations /********************************************************** */ @Override public Object findValueInstantiator(AnnotatedClass ac) { JsonValueInstantiator ann = _findAnnotation(ac, JsonValueInstantiator.class); // no 'null' marker yet, so: return (ann == null) ? null : ann.value(); } @Override public Class<?> findPOJOBuilder(AnnotatedClass ac) { JsonDeserialize ann = _findAnnotation(ac, JsonDeserialize.class); return (ann == null) ? null : _classIfExplicit(ann.builder()); } @Override public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac) { JsonPOJOBuilder ann = _findAnnotation(ac, JsonPOJOBuilder.class); return (ann == null) ? null : new JsonPOJOBuilder.Value(ann); } /* /********************************************************** /* Deserialization: property annotations /********************************************************** */ @Override public PropertyName findNameForDeserialization(Annotated a) { // @JsonSetter has precedence over @JsonProperty, being more specific boolean useDefault = false; JsonSetter js = _findAnnotation(a, JsonSetter.class); if (js != null) { String s = js.value(); // 04-May-2018, tatu: Need to allow for "nameless" `@JsonSetter` too if (s.isEmpty()) { useDefault = true; } else { return PropertyName.construct(s); } } JsonProperty pann = _findAnnotation(a, JsonProperty.class); if (pann != null) { // 14-Nov-2020, tatu: "namespace" added in 2.12 String ns = pann.namespace(); if (ns != null && ns.isEmpty()) { ns = null; } return PropertyName.construct(pann.value(), ns); } if (useDefault || _hasOneOf(a, ANNOTATIONS_TO_INFER_DESER)) { return PropertyName.USE_DEFAULT; } return null; } @Override public Boolean hasAnySetter(Annotated a) { JsonAnySetter ann = _findAnnotation(a, JsonAnySetter.class); return (ann == null) ? null : ann.enabled(); } @Override public JsonSetter.Value findSetterInfo(Annotated a) { return JsonSetter.Value.from(_findAnnotation(a, JsonSetter.class)); } @Override // since 2.9 public Boolean findMergeInfo(Annotated a) { JsonMerge ann = _findAnnotation(a, JsonMerge.class); return (ann == null) ? null : ann.value().asBoolean(); } @Override @Deprecated // since 2.9 public boolean hasAnySetterAnnotation(AnnotatedMethod am) { return _hasAnnotation(am, JsonAnySetter.class); } @Override @Deprecated // since 2.9 public boolean hasCreatorAnnotation(Annotated a) { /* No dedicated disabling; regular @JsonIgnore used if needs to be * ignored (and if so, is handled prior to this method getting called) */ JsonCreator ann = _findAnnotation(a, JsonCreator.class); if (ann != null) { return (ann.mode() != JsonCreator.Mode.DISABLED); } // 19-Apr-2016, tatu: As per [databind#1197], [databind#1122] (and some related), // may or may not consider it a creator if (_cfgConstructorPropertiesImpliesCreator ) { if (a instanceof AnnotatedConstructor) { if (_java7Helper != null) { Boolean b = _java7Helper.hasCreatorAnnotation(a); if (b != null) { return b.booleanValue(); } } } } return false; } @Override @Deprecated // since 2.9 public JsonCreator.Mode findCreatorBinding(Annotated a) { JsonCreator ann = _findAnnotation(a, JsonCreator.class); return (ann == null) ? null : ann.mode(); } @Override public JsonCreator.Mode findCreatorAnnotation(MapperConfig<?> config, Annotated a) { JsonCreator ann = _findAnnotation(a, JsonCreator.class); if (ann != null) { return ann.mode(); } if (_cfgConstructorPropertiesImpliesCreator && config.isEnabled(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES) ) { if (a instanceof AnnotatedConstructor) { if (_java7Helper != null) { Boolean b = _java7Helper.hasCreatorAnnotation(a); if ((b != null) && b.booleanValue()) { // 13-Sep-2016, tatu: Judgment call, but I don't think JDK ever implies // use of delegate; assumes as-properties implicitly return JsonCreator.Mode.PROPERTIES; } } } } return null; } /* /********************************************************** /* Helper methods /********************************************************** */ protected boolean _isIgnorable(Annotated a) { JsonIgnore ann = _findAnnotation(a, JsonIgnore.class); if (ann != null) { return ann.value(); } if (_java7Helper != null) { Boolean b = _java7Helper.findTransient(a); if (b != null) { return b.booleanValue(); } } return false; } protected Class<?> _classIfExplicit(Class<?> cls) { if (cls == null || ClassUtil.isBogusClass(cls)) { return null; } return cls; } protected Class<?> _classIfExplicit(Class<?> cls, Class<?> implicit) { cls = _classIfExplicit(cls); return (cls == null || cls == implicit) ? null : cls; } protected PropertyName _propertyName(String localName, String namespace) { if (localName.isEmpty()) { return PropertyName.USE_DEFAULT; } if (namespace == null || namespace.isEmpty()) { return PropertyName.construct(localName); } return PropertyName.construct(localName, namespace); } protected PropertyName _findConstructorName(Annotated a) { if (a instanceof AnnotatedParameter) { AnnotatedParameter p = (AnnotatedParameter) a; AnnotatedWithParams ctor = p.getOwner(); if (ctor != null) { if (_java7Helper != null) { PropertyName name = _java7Helper.findConstructorName(p); if (name != null) { return name; } } } } return null; }
Helper method called to construct and initialize instance of TypeResolverBuilder if given annotated element indicates one is needed.
/** * Helper method called to construct and initialize instance of {@link TypeResolverBuilder} * if given annotated element indicates one is needed. */
@SuppressWarnings("deprecation") protected TypeResolverBuilder<?> _findTypeResolver(MapperConfig<?> config, Annotated ann, JavaType baseType) { // First: maybe we have explicit type resolver? TypeResolverBuilder<?> b; JsonTypeInfo info = _findAnnotation(ann, JsonTypeInfo.class); JsonTypeResolver resAnn = _findAnnotation(ann, JsonTypeResolver.class); if (resAnn != null) { if (info == null) { return null; } // let's not try to force access override (would need to pass // settings through if we did, since that's not doable on some platforms) b = config.typeResolverBuilderInstance(ann, resAnn.value()); } else { // if not, use standard one, if indicated by annotations if (info == null) { return null; } // bit special; must return 'marker' to block use of default typing: if (info.use() == JsonTypeInfo.Id.NONE) { return _constructNoTypeResolverBuilder(); } b = _constructStdTypeResolverBuilder(); } // Does it define a custom type id resolver? JsonTypeIdResolver idResInfo = _findAnnotation(ann, JsonTypeIdResolver.class); TypeIdResolver idRes = (idResInfo == null) ? null : config.typeIdResolverInstance(ann, idResInfo.value()); if (idRes != null) { idRes.init(baseType); } b = b.init(info.use(), idRes); // 13-Aug-2011, tatu: One complication; external id only works for properties; // so if declared for a Class, we will need to map it to "PROPERTY" // instead of "EXTERNAL_PROPERTY" JsonTypeInfo.As inclusion = info.include(); if (inclusion == JsonTypeInfo.As.EXTERNAL_PROPERTY && (ann instanceof AnnotatedClass)) { inclusion = JsonTypeInfo.As.PROPERTY; } b = b.inclusion(inclusion); b = b.typeProperty(info.property()); Class<?> defaultImpl = info.defaultImpl(); // 08-Dec-2014, tatu: To deprecate `JsonTypeInfo.None` we need to use other placeholder(s); // and since `java.util.Void` has other purpose (to indicate "deser as null"), we'll instead // use `JsonTypeInfo.class` itself. But any annotation type will actually do, as they have no // valid use (cannot instantiate as default) if (defaultImpl != JsonTypeInfo.None.class && !defaultImpl.isAnnotation()) { b = b.defaultImpl(defaultImpl); } b = b.typeIdVisibility(info.visible()); return b; }
Helper method for constructing standard TypeResolverBuilder implementation.
/** * Helper method for constructing standard {@link TypeResolverBuilder} * implementation. */
protected StdTypeResolverBuilder _constructStdTypeResolverBuilder() { return new StdTypeResolverBuilder(); }
Helper method for dealing with "no type info" marker; can't be null (as it'd be replaced by default typing)
/** * Helper method for dealing with "no type info" marker; can't be null * (as it'd be replaced by default typing) */
protected StdTypeResolverBuilder _constructNoTypeResolverBuilder() { return StdTypeResolverBuilder.noTypeInfoBuilder(); } private boolean _primitiveAndWrapper(Class<?> baseType, Class<?> refinement) { if (baseType.isPrimitive()) { return baseType == ClassUtil.primitiveType(refinement); } if (refinement.isPrimitive()) { return refinement == ClassUtil.primitiveType(baseType); } return false; } private boolean _primitiveAndWrapper(JavaType baseType, Class<?> refinement) { if (baseType.isPrimitive()) { return baseType.hasRawClass(ClassUtil.primitiveType(refinement)); } if (refinement.isPrimitive()) { return refinement == ClassUtil.primitiveType(baseType.getRawClass()); } return false; } // @since 2.12 private JsonMappingException _databindException(String msg) { return new JsonMappingException(null, msg); } // @since 2.12 private JsonMappingException _databindException(Throwable t, String msg) { return new JsonMappingException(null, msg, t); } }