JsonTypeInfo.java
package com.fasterxml.jackson.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation used for configuring details of if and how type information is
* used with JSON serialization and deserialization, to preserve information
* about actual class of Object instances. This is necessarily for polymorphic
* types, and may also be needed to link abstract declared types and matching
* concrete implementation.
*<p>
* Some examples of typical annotations:
*<pre>
* // Include Java class name ("com.myempl.ImplClass") as JSON property "class"
* @JsonTypeInfo(use=Id.CLASS, include=As.PROPERTY, property="class")
*
* // Include logical type name (defined in impl classes) as wrapper; 2 annotations
* @JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
* @JsonSubTypes({com.myemp.Impl1.class, com.myempl.Impl2.class})
*</pre>
* Alternatively you can also define fully customized type handling by using
* <code>@JsonTypeResolver</code> annotation (from databind package).
*<p>
* This annotation can be used both for types (classes) and properties.
* If both exist, annotation on property has precedence, as it is
* considered more specific.
*<p>
* When used for properties (fields, methods), this annotation applies
* to <b>values</b>: so when applied to structure types
* (like {@link java.util.Collection}, {@link java.util.Map}, arrays),
* will apply to contained values, not the container;
* for non-structured types there is no difference.
* This is identical to how JAXB handles type information
* annotations; and is chosen since it is the dominant use case.
* There is no per-property way to force type information to be included
* for type of container (structured type); for container types one has
* to use annotation for type declaration.
*<p>
* Note on visibility of type identifier: by default, deserialization
* (use during reading of JSON) of type identifier
* is completely handled by Jackson, and is <b>not passed to</b>
* deserializers. However, if so desired,
* it is possible to define property <code>visible = true</code>
* in which case property will be passed as-is to deserializers
* (and set via setter or field) on deserialization.
*<p>
* On serialization side, Jackson will generate type id by itself,
* except if there is a property with name that matches
* {@link #property()}, in which case value of that property is
* used instead.
*<p>
* NOTE: use of type id of "class name" with very general base type
* (such as {@link java.lang.Object} or {@link java.io.Serializable})
* can potentially open up security holes if deserializing content
* generated by untrusted sources. If content can not be trusted,
* it is necessary to either use "type name" as type id, or to
* limit possible types using other means.
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE,
ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonTypeInfo
{
/*
/**********************************************************
/* Value enumerations used for properties
/**********************************************************
*/
/**
* Definition of different type identifiers that can be included in JSON
* during serialization, and used for deserialization.
*/
public enum Id {
/**
* This means that no explicit type metadata is included, and typing is
* purely done using contextual information possibly augmented with other
* annotations.
*/
NONE(null),
/**
* Means that fully-qualified Java class name is used as the type identifier.
*/
CLASS("@class"),
/**
* Means that Java class name with minimal path is used as the type identifier.
* Minimal means that only the class name, and that part of preceding Java
* package name is included that is needed to construct fully-qualified name
* given fully-qualified name of the declared supertype; additionally a single
* leading dot ('.') must be used to indicate that partial class name is used.
* For example, for supertype {@code com.foo.Base}, and concrete type
* {@code com.foo.Impl}, only {@code .Impl} would be included;
* and for {@code com.foo.impl.Impl2} only {@code .impl.Impl2} would be included.
*<br>
* <b>NOTE</b>: leading dot ('.') MUST be used to denote partial (minimal) name;
* if it is missing, value is assumed to be fully-qualified name. Fully-qualified
* name is used in cases where subtypes are not in same package (or sub-package
* thereof) as base class.
*<p>
* If all related classes are in the same Java package, this option can reduce
* amount of type information overhead, especially for small types.
* However, please note that using this alternative is inherently risky since it
* assumes that the
* supertype can be reliably detected. Given that it is based on declared type
* (since ultimate supertype, <code>java.lang.Object</code> would not be very
* useful reference point), this may not always work as expected.
*/
MINIMAL_CLASS("@c"),
/**
* Means that logical type name is used as type information; name will then need
* to be separately resolved to actual concrete type (Class).
*/
NAME("@type"),
/**
* Means that the simple name of the Java class, equivalent to the value returned by {@link Class#getSimpleName()},
* is used as the default type identifier, unless explicit name is specified by annotation {@link JsonTypeName}.
*<br>
* For instance:
* <ul>
* <li>For a class "com.example.MyClass", only "MyClass" is used.</li>
* <li>For an inner class "com.example.MyClass$Inner", only "Inner" is used.</li>
* </ul>
* <b>Note:</b> This approach reduces verbosity but requires the simple names to be unique
* to avoid conflicts. If multiple classes share the same simple name, <b>the last declared one</b>
* will be used. This approach should be used with careful consideration of your type hierarchy.
*
* @since 2.16
*/
SIMPLE_NAME("@type"),
/**
* Means that no serialized typing-property is used. Types are <i>deduced</i> based
* on the Object properties available in the input.
* Deduction is limited to the <i>names</i> of properties
* (not their values or, consequently, any nested values). Exceptions will be
* thrown if not enough unique information is present to select a single sub-type.
* <br>If deduction is being used annotation properties {@code visible},
* {@code property} and {@code include} are ignored.
* <p>
* NOTE: being Property-based, will essentially only work for POJOs and not for
* other kinds of types (like arrays, {@link java.util.Collection}s, {@link java.util.Map}s,
* scalars, etc); similarly, serialized form must be (JSON) Object.
* <p>
* On serialization, no type Id is written, and only regular properties are included.
*
* @since 2.12
*/
DEDUCTION(null),
/**
* Means that typing mechanism uses customized handling, with possibly
* custom configuration. This means that semantics of other properties is
* not defined by Jackson package, but by the custom implementation.
*/
CUSTOM(null)
;
private final String _defaultPropertyName;
private Id(String defProp) {
_defaultPropertyName = defProp;
}
public String getDefaultPropertyName() { return _defaultPropertyName; }
}
/**
* Definition of standard type inclusion mechanisms for type metadata.
* Used for standard metadata types, except for {@link Id#NONE}.
* May or may not be used for custom types ({@link Id#CUSTOM}).
*/
public enum As {
/**
* Inclusion mechanism that uses a single configurable property, included
* along with actual data (POJO properties) as a separate meta-property.
* <p>
* Default choice for inclusion.
*/
PROPERTY,
/**
* Inclusion mechanism that wraps typed JSON value (POJO
* serialized as JSON) in
* a JSON Object that has a single entry,
* where field name is serialized type identifier,
* and value is the actual JSON value.
*<p>
* Note: can only be used if type information can be serialized as
* String. This is true for standard type metadata types, but not
* necessarily for custom types.
*/
WRAPPER_OBJECT,
/**
* Inclusion mechanism that wraps typed JSON value (POJO
* serialized as JSON) in
* a 2-element JSON array: first element is the serialized
* type identifier, and second element the serialized POJO
* as JSON Object.
*/
WRAPPER_ARRAY,
/**
* Inclusion mechanism similar to <code>PROPERTY</code>, except that
* property is included one-level higher in hierarchy, i.e. as sibling
* property at same level as JSON Object to type.
* Note that this mechanism <b>can only be used for properties</b>, not
* for types (classes). Trying to use it for classes will result in
* inclusion strategy of basic <code>PROPERTY</code> instead.
*<p>
* Note also that this mechanism <b>can not be used for container values</b>
* (arrays, {@link java.util.Collection}s, {@link java.util.Map}s); it only
* works for scalar and POJO values.
*/
EXTERNAL_PROPERTY,
/**
* Inclusion mechanism similar to <code>PROPERTY</code> with respect
* to deserialization; but one that is produced by a "regular" accessible
* property during serialization. This means that <code>TypeSerializer</code>
* will do nothing, and expects a property with defined name to be output
* using some other mechanism (like default POJO property serialization, or
* custom serializer).
*<p>
* Note that this behavior is quite similar to that of using {@link JsonTypeId}
* annotation;
* except that here <code>TypeSerializer</code> is basically suppressed;
* whereas with {@link JsonTypeId}, output of regular property is suppressed.
* This mostly matters with respect to output order; this choice is the only
* way to ensure specific placement of type id during serialization.
*
* @since 2.3 but databind <b>only since 2.5</b>.
*/
EXISTING_PROPERTY
;
}
/*
/**********************************************************
/* Annotation properties
/**********************************************************
*/
/**
* Specifies kind of type metadata to use when serializing
* type information for instances of annotated type
* and its subtypes; as well as what is expected during
* deserialization.
*/
public Id use();
/**
* Specifies mechanism to use for including type metadata (if any; for
* {@link Id#NONE} nothing is included); used when serializing,
* and expected when deserializing.
*<p>
* Note that for type metadata type of {@link Id#CUSTOM},
* this setting may or may not have any effect.
*/
public As include() default As.PROPERTY;
/**
* Property names used when type inclusion method ({@link As#PROPERTY}) is used
* (or possibly when using type metadata of type {@link Id#CUSTOM}).
* If POJO itself has a property with same name, value of property
* will be set with type id metadata: if no such property exists, type id
* is only used for determining actual type.
*<p>
* Default property name used if this property is not explicitly defined
* (or is set to empty <code>String</code>) is based on
* type metadata type ({@link #use}) used.
*/
public String property() default "";
/**
* Optional property that can be used to specify default implementation
* class to use for deserialization if type identifier is either not present,
* or can not be mapped to a registered type (which can occur for ids,
* but not when specifying explicit class to use).
* Property has no effect on choice of type id used for serialization;
* it is only used in deciding what to do for otherwise unmappable cases.
*<p>
* Note that while this property allows specification of the default
* implementation to use, it does not help with structural issues that
* may arise if type information is missing. This means that most often
* this is used with type-name -based resolution, to cover cases
* where new subtypes are added, but base type is not changed to
* reference new subtypes.
*<p>
* There are certain special values that indicate alternate behavior:
*<ul>
* <li>{@link java.lang.Void} means that objects with unmappable (or missing)
* type are to be mapped to null references.
* For backwards compatibility (2.5 and below), value of
* <code>com.fasterxml.jackson.databind.annotation.NoClass</code> is also allowed
* for such usage.
* </li>
* <li>Placeholder value of {@link JsonTypeInfo} (that is, this annotation type
* itself} means "there is no default implementation" (in which
* case an error results from unmappable type).
* For backwards compatibility with earlier versions (2.5 and below),
* value of {@link JsonTypeInfo.None} may also be used.
* </li>
* </ul>
*/
public Class<?> defaultImpl() default JsonTypeInfo.class;
/**
* Property that defines whether type identifier value will be passed
* as part of JSON stream to deserializer (true), or handled and
* removed by <code>TypeDeserializer</code> (false).
* Property has no effect on serialization.
*<p>
* Default value is false, meaning that Jackson handles and removes
* the type identifier from JSON content that is passed to
* <code>JsonDeserializer</code>.
*
* @since 2.0
*/
public boolean visible() default false;
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* This marker class that is only to be used with <code>defaultImpl</code>
* annotation property, to indicate that there is no default implementation
* specified.
*
* @deprecated Since 2.5, use any Annotation type (such as {@link JsonTypeInfo}),
* if such behavior is needed; this is rarely necessary.
*/
@Deprecated // since 2.5
public abstract static class None {}
/**
* Specifies whether the type ID should be strictly required during polymorphic
* deserialization of its subtypes.
* <p>
* If set to {@link OptBoolean#TRUE}, an {@code InvalidTypeIdException} will
* be thrown if no type information is provided.
* If set to {@link OptBoolean#FALSE}, deserialization may proceed without
* type information if the subtype is a legitimate target (non-abstract).
* If set to {@link OptBoolean#DEFAULT}, the global configuration of
* {@code MapperFeature#REQUIRE_TYPE_ID_FOR_SUBTYPES} is used for type ID handling.
* <p>
* NOTE: This setting is specific to this type and will <strong>always override</strong>
* the global configuration of {@code MapperFeature.REQUIRE_TYPE_ID_FOR_SUBTYPES}.
*
* @since 2.16
*/
public OptBoolean requireTypeIdForSubtypes() default OptBoolean.DEFAULT;
/*
/**********************************************************************
/* Value class used to enclose information, allow for
/* merging of layered configuration settings.
/**********************************************************************
*/
public static class Value
implements JacksonAnnotationValue<JsonTypeInfo>,
java.io.Serializable
{
private static final long serialVersionUID = 1L;
// should not really be needed usually but make sure defalts to `NONE`; other
// values of less interest
protected final static Value EMPTY = new Value(Id.NONE, As.PROPERTY, null, null, false, null);
protected final Id _idType;
protected final As _inclusionType;
protected final String _propertyName;
protected final Class<?> _defaultImpl;
protected final boolean _idVisible;
protected final Boolean _requireTypeIdForSubtypes;
/*
/**********************************************************************
/* Construction
/**********************************************************************
*/
protected Value(Id idType, As inclusionType,
String propertyName, Class<?> defaultImpl, boolean idVisible, Boolean requireTypeIdForSubtypes)
{
_defaultImpl = defaultImpl;
_idType = idType;
_inclusionType = inclusionType;
_propertyName = propertyName;
_idVisible = idVisible;
_requireTypeIdForSubtypes = requireTypeIdForSubtypes;
}
public static Value construct(Id idType, As inclusionType,
String propertyName, Class<?> defaultImpl, boolean idVisible, Boolean requireTypeIdForSubtypes)
{
// couple of overrides we need to apply here. First: if no propertyName specified,
// use Id-specific property name
if ((propertyName == null) || propertyName.isEmpty()) {
if (idType != null) {
propertyName = idType.getDefaultPropertyName();
} else {
propertyName = "";
}
}
// Although we can not do much here for special handling of `Void`, we can convert
// annotation types as `null` (== no default implementation)
if ((defaultImpl == null) || defaultImpl.isAnnotation()) {
defaultImpl = null;
}
return new Value(idType, inclusionType, propertyName, defaultImpl, idVisible, requireTypeIdForSubtypes);
}
public static Value from(JsonTypeInfo src) {
if (src == null) {
return null;
}
return construct(src.use(), src.include(),
src.property(), src.defaultImpl(), src.visible(), src.requireTypeIdForSubtypes().asBoolean());
}
/*
/**********************************************************************
/* Mutators
/**********************************************************************
*/
public Value withDefaultImpl(Class<?> impl) {
return (impl == _defaultImpl) ? this :
new Value(_idType, _inclusionType, _propertyName, impl, _idVisible, _requireTypeIdForSubtypes);
}
public Value withIdType(Id idType) {
return (idType == _idType) ? this :
new Value(idType, _inclusionType, _propertyName, _defaultImpl, _idVisible, _requireTypeIdForSubtypes);
}
public Value withInclusionType(As inclusionType) {
return (inclusionType == _inclusionType) ? this :
new Value(_idType, inclusionType, _propertyName, _defaultImpl, _idVisible, _requireTypeIdForSubtypes);
}
public Value withPropertyName(String propName) {
return (propName == _propertyName) ? this :
new Value(_idType, _inclusionType, propName, _defaultImpl, _idVisible, _requireTypeIdForSubtypes);
}
public Value withIdVisible(boolean visible) {
return (visible == _idVisible) ? this :
new Value(_idType, _inclusionType, _propertyName, _defaultImpl, visible, _requireTypeIdForSubtypes);
}
public Value withRequireTypeIdForSubtypes(Boolean requireTypeIdForSubtypes) {
return (_requireTypeIdForSubtypes == requireTypeIdForSubtypes) ? this :
new Value(_idType, _inclusionType, _propertyName, _defaultImpl, _idVisible, requireTypeIdForSubtypes);
}
/*
/**********************************************************************
/* Simple accessors
/**********************************************************************
*/
@Override
public Class<JsonTypeInfo> valueFor() {
return JsonTypeInfo.class;
}
public Class<?> getDefaultImpl() { return _defaultImpl; }
public Id getIdType() { return _idType; }
public As getInclusionType() { return _inclusionType; }
public String getPropertyName() { return _propertyName; }
public boolean getIdVisible() { return _idVisible; }
public Boolean getRequireTypeIdForSubtypes() { return _requireTypeIdForSubtypes; }
/**
* Static helper method for simple(r) checking of whether there's a Value instance
* that indicates that polymorphic handling is (to be) enabled.
*/
public static boolean isEnabled(JsonTypeInfo.Value v) {
return (v != null) &&
(v._idType != null) && (v._idType != Id.NONE);
}
/*
/**********************************************************************
/* Standard methods
/**********************************************************************
*/
@Override
public String toString() {
return String.format("JsonTypeInfo.Value(idType=%s,includeAs=%s,propertyName=%s,defaultImpl=%s,idVisible=%s"
+ ",requireTypeIdForSubtypes=%s)",
_idType, _inclusionType, _propertyName,
((_defaultImpl == null) ? "NULL" : _defaultImpl.getName()),
_idVisible, _requireTypeIdForSubtypes);
}
@Override
public int hashCode() {
int hashCode = 1;
hashCode = 31 * hashCode + (_idType != null ? _idType.hashCode() : 0);
hashCode = 31 * hashCode + (_inclusionType != null ? _inclusionType.hashCode() : 0);
hashCode = 31 * hashCode + (_propertyName != null ? _propertyName.hashCode() : 0);
hashCode = 31 * hashCode + (_defaultImpl != null ? _defaultImpl.hashCode() : 0);
hashCode = 31 * hashCode + (_requireTypeIdForSubtypes ? 11 : -17);
hashCode = 31 * hashCode + (_idVisible ? 11 : -17);
return hashCode;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (o == null) return false;
return (o.getClass() == getClass())
&& _equals(this, (Value) o);
}
private static boolean _equals(Value a, Value b)
{
return (a._idType == b._idType)
&& (a._inclusionType == b._inclusionType)
&& (a._defaultImpl == b._defaultImpl)
&& (a._idVisible == b._idVisible)
&& _equal(a._propertyName, b._propertyName)
&& _equal(a._requireTypeIdForSubtypes, b._requireTypeIdForSubtypes)
;
}
private static <T> boolean _equal(T value1, T value2)
{
if (value1 == null) {
return (value2 == null);
}
if (value2 == null) {
return false;
}
return value1.equals(value2);
}
}
}