![JAR search and dependency download from the Maven repository](/logo.png)
com.fasterxml.jackson.annotation.JsonInclude Maven / Gradle / Ivy
/*
* Copyright © 2019 Dominokit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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 to indicate when value of the annotated property (when used for a field, method
* or constructor parameter), or all properties of the annotated class, is to be serialized. Without
* annotation property values are always included, but by using this annotation one can specify
* simple exclusion rules to reduce amount of properties to write out.
*
* Note that the main inclusion criteria (one annotated with {@link #value}) is checked on
* Java object level, for the annotated type, and NOT on JSON output -- so even with
* {@link Include#NON_NULL} it is possible that JSON null values are output, if object reference in
* question is not `null`. An example is {@link java.util.concurrent.atomic.AtomicReference}
* instance constructed to reference null
value: such a value would be serialized as
* JSON null, and not filtered out.
*
*
To base inclusion on value of contained value(s), you will typically also need to specify
* {@link #content()} annotation; for example, specifying only {@link #value} as {@link
* Include#NON_EMPTY} for a {link java.util.Map} would exclude Map
s with no values, but
* would include Map
s with `null` values. To exclude Map with only `null` value, you
* would use both annotations like so:
*
*
* public class Bean {
* {@literal @JsonInclude}(value=Include.NON_EMPTY, content=Include.NON_NULL)
* public Map<String,String> entries;
* }
*
*
* Similarly you could Maps that only contain "empty" elements, or "non-default" values (see {@link
* Include#NON_EMPTY} and {@link Include#NON_DEFAULT} for more details).
*
* In addition to `Map`s, `content` concept is also supported for referential types (like {@link
* java.util.concurrent.atomic.AtomicReference}). Note that `content` is NOT currently (as of
* Jackson 2.9) supported for arrays or {@link java.util.Collection}s, but supported may be added in
* future versions.
*
* @since 2.0
*/
@Target({
ElementType.ANNOTATION_TYPE,
ElementType.METHOD,
ElementType.FIELD,
ElementType.TYPE,
ElementType.PARAMETER
})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonInclude {
/**
* Inclusion rule to use for instances (values) of types (Classes) or properties annotated;
* defaults to {@link Include#ALWAYS}.
*
* @return {@link Include}
*/
public Include value() default Include.ALWAYS;
/**
* Inclusion rule to use for entries ("content") of annotated {@link java.util.Map}s and
* referential types (like {@link java.util.concurrent.atomic.AtomicReference}); defaults to
* {@link Include#ALWAYS}.
*
* @since 2.5
* @return {@link Include}
*/
public Include content() default Include.ALWAYS;
/**
* Specifies type of "Filter Object" to use in case {@link #value} is {@link Include#CUSTOM}: if
* so, an instance is created by calling HandlerInstantiator
(of ObjectMapper
*
), which by default simply calls zero-argument constructor of the Filter Class.
*
* @since 2.9
* @return {@link Class}
*/
public Class> valueFilter() default Void.class;
/**
* Specifies type of "Filter Object" to use in case {@link #content} is {@link Include#CUSTOM}: if
* so, an instance is created by calling HandlerInstantiator
(of ObjectMapper
*
), which by default simply calls zero-argument constructor of the Filter Class.
*
* @since 2.9
* @return {@link Class}
*/
public Class> contentFilter() default Void.class;
/*
/**********************************************************
/* Value enumerations
/**********************************************************
*/
/**
* Enumeration used with {@link JsonInclude} to define which properties of Java Beans are to be
* included in serialization.
*/
public enum Include {
/**
* Value that indicates that property is to be always included, independent of value of the
* property.
*/
ALWAYS,
/** Value that indicates that only properties with non-null values are to be included. */
NON_NULL,
/**
* Value that indicates that properties are included unless their value is:
*
*
* - null
*
- "absent" value of a referential type (like Java 8 `Optional`, or {link
* java.utl.concurrent.atomic.AtomicReference}); that is, something that would not
* deference to a non-null value.
*
*
* This option is mostly used to work with "Optional"s (Java 8, Guava).
*
* @since 2.6
*/
NON_ABSENT,
/**
* Value that indicates that only properties with null value, or what is considered empty, are
* not to be included. Definition of emptiness is data type specific; see below for details on
* actual handling.
*
* Default emptiness for all types includes:
*
*
* Null
values.
* - "Absent" values (see {@link #NON_ABSENT})
*
*
* so that as baseline, "empty" set includes values that would be excluded by both {@link
* #NON_NULL} and {@link #NON_ABSENT}.
* Beyond this base, following types have additional empty values:
*
*
* - For {@link java.util.Collection}s and {@link java.util.Map}s, method
isEmpty()
*
is called;
* - For Java arrays, empty arrays are ones with length of 0
*
- For Java {@link String}s,
length()
is called, and return value of 0
* indicates empty String
*
*
* and for other types, null values are excluded but other exclusions (if any).
*
* Note that this default handling can be overridden by custom JsonSerializer
* implementation: if method isEmpty()
is overridden, it will be called to see if
* non-null values are considered empty (null is always considered empty).
*
*
Compatibility note: Jackson 2.6 included a wider range of "empty" values than either
* earlier (up to 2.5) or later (2.7 and beyond) types; specifically:
*
*
* - Default values of primitive types (like
0
for `int`/`java.lang.Integer`
* and `false` for `bool`/`Boolean`)
* - Timestamp 0 for date/time types
*
*
* With 2.7, definition has been tightened back to only containing types explained above (null,
* absent, empty String, empty containers), and now extended definition may be specified using
* {@link #NON_DEFAULT}.
*/
NON_EMPTY,
/**
* Meaning of this setting depends on context: whether annotation is specified for POJO type
* (class), or not. In latter case annotation is either used as the global default, or as
* property override.
*
* When used for a POJO, definition is that only values that differ from the default values
* of POJO properties are included. This is done by creating an instance of POJO using
* zero-argument constructor, and accessing property values: value is used as the default value
* by using equals()
method, except for the case where property has `null` value in
* which case straight null check is used.
*
*
When NOT used for a POJO (that is, as a global default, or as property override),
* definition is such that:
*
*
* - All values considered "empty" (as per {@link #NON_EMPTY}) are excluded
*
- Primitive/wrapper default values are excluded
*
- Date/time values that have timestamp (`long` value of milliseconds since epoch, see
* {@link java.util.Date}) of `0L` are excluded
*
*/
NON_DEFAULT,
/**
* Value that indicates that separate `filter` Object (specified by {@link
* JsonInclude#valueFilter} for value itself, and/or {@link JsonInclude#contentFilter} for
* contents of structured types) is to be used for determining inclusion criteria. Filter
* object's equals()
method is called with value to serialize; if it returns
* true
value is excluded (that is, filtered out); if false
value is
* included.
*
* @since 2.9
*/
CUSTOM,
/**
* Pseudo-value used to indicate that the higher-level defaults make sense, to avoid overriding
* inclusion value. For example, if returned for a property this would use defaults for the
* class that contains property, if any defined; and if none defined for that, then global
* serialization inclusion details.
*
* @since 2.6
*/
USE_DEFAULTS;
}
/*
/**********************************************************
/* Value class used to enclose information
/**********************************************************
*/
/**
* Helper class used to contain information from a single {@link JsonInclude} annotation.
*
* @since 2.6
*/
public static class Value
implements JacksonAnnotationValue, // since 2.6
java.io.Serializable {
private static final long serialVersionUID = 1L;
protected static final Value EMPTY =
new Value(Include.USE_DEFAULTS, Include.USE_DEFAULTS, null, null);
protected final Include _valueInclusion;
protected final Include _contentInclusion;
/** @since 2.9 */
protected final Class> _valueFilter;
/** @since 2.9 */
protected final Class> _contentFilter;
public Value(JsonInclude src) {
this(src.value(), src.content(), src.valueFilter(), src.contentFilter());
}
protected Value(Include vi, Include ci, Class> valueFilter, Class> contentFilter) {
_valueInclusion = (vi == null) ? Include.USE_DEFAULTS : vi;
_contentInclusion = (ci == null) ? Include.USE_DEFAULTS : ci;
_valueFilter = (valueFilter == Void.class) ? null : valueFilter;
_contentFilter = (contentFilter == Void.class) ? null : contentFilter;
}
public static Value empty() {
return EMPTY;
}
/**
* Helper method that will try to combine values from two {@link Value} instances, using one as
* base settings, and the other as overrides to use instead of base values when defined; base
* values are only use if override does not specify a value (matching value is null or logically
* missing). Note that one or both of value instances may be `null`, directly; if both are
* `null`, result will also be `null`; otherwise never null.
*
* @since 2.8
* @param base {@link Value}
* @param overrides {@link Value}
* @return {@link Value}
*/
public static Value merge(Value base, Value overrides) {
return (base == null) ? overrides : base.withOverrides(overrides);
}
/**
* @since 2.8
* @param values {@link Value} varargs
* @return {@link Value}
*/
public static Value mergeAll(Value... values) {
Value result = null;
for (Value curr : values) {
if (curr != null) {
result = (result == null) ? curr : result.withOverrides(curr);
}
}
return result;
}
// for JDK serialization
protected Object readResolve() {
if ((_valueInclusion == Include.USE_DEFAULTS)
&& (_contentInclusion == Include.USE_DEFAULTS)
&& (_valueFilter == null)
&& (_contentFilter == null)) {
return EMPTY;
}
return this;
}
/**
* Mutant factory method that merges values of this value with given override values, so that
* any explicitly defined inclusion in overrides has precedence over settings of this value
* instance. If no overrides exist will return this
instance; otherwise new {@link
* Value} with changed inclusion values.
*
* @param overrides {@link Value}
* @return {@link Value}
*/
public Value withOverrides(Value overrides) {
if ((overrides == null) || (overrides == EMPTY)) {
return this;
}
Include vi = overrides._valueInclusion;
Include ci = overrides._contentInclusion;
Class> vf = overrides._valueFilter;
Class> cf = overrides._contentFilter;
boolean viDiff = (vi != _valueInclusion) && (vi != Include.USE_DEFAULTS);
boolean ciDiff = (ci != _contentInclusion) && (ci != Include.USE_DEFAULTS);
boolean filterDiff = (vf != _valueFilter) || (cf != _valueFilter);
if (viDiff) {
if (ciDiff) {
return new Value(vi, ci, vf, cf);
}
return new Value(vi, _contentInclusion, vf, cf);
} else if (ciDiff) {
return new Value(_valueInclusion, ci, vf, cf);
} else if (filterDiff) {
return new Value(_valueInclusion, _contentInclusion, vf, cf);
}
return this;
}
/**
* Factory method to use for constructing an instance for components
*
* @param valueIncl {@link Include}
* @param contentIncl {@link Include}
* @return {@link Value}
*/
public static Value construct(Include valueIncl, Include contentIncl) {
if (((valueIncl == Include.USE_DEFAULTS) || (valueIncl == null))
&& ((contentIncl == Include.USE_DEFAULTS) || (contentIncl == null))) {
return EMPTY;
}
return new Value(valueIncl, contentIncl, null, null);
}
/**
* Factory method to use for constructing an instance for components
*
* @since 2.9
* @param valueIncl {@link Include}
* @param contentIncl {@link Include}
* @param valueFilter {@link Class}
* @param contentFilter {@link Class}
* @return {@link Value}
*/
public static Value construct(
Include valueIncl, Include contentIncl, Class> valueFilter, Class> contentFilter) {
if (valueFilter == Void.class) {
valueFilter = null;
}
if (contentFilter == Void.class) {
contentFilter = null;
}
if (((valueIncl == Include.USE_DEFAULTS) || (valueIncl == null))
&& ((contentIncl == Include.USE_DEFAULTS) || (contentIncl == null))
&& (valueFilter == null)
&& (contentFilter == null)) {
return EMPTY;
}
return new Value(valueIncl, contentIncl, valueFilter, contentFilter);
}
/**
* Factory method to use for constructing an instance from instance of {@link JsonInclude}
*
* @param src {@link JsonInclude}
* @return {@link Value}
*/
public static Value from(JsonInclude src) {
if (src == null) {
return EMPTY;
}
Include vi = src.value();
Include ci = src.content();
if ((vi == Include.USE_DEFAULTS) && (ci == Include.USE_DEFAULTS)) {
return EMPTY;
}
Class> vf = src.valueFilter();
if (vf == Void.class) {
vf = null;
}
Class> cf = src.contentFilter();
if (cf == Void.class) {
cf = null;
}
return new Value(vi, ci, vf, cf);
}
public Value withValueInclusion(Include incl) {
return (incl == _valueInclusion)
? this
: new Value(incl, _contentInclusion, _valueFilter, _contentFilter);
}
/**
* Mutant factory that will either
*
*
* - Set
value
as USE_DEFAULTS
and valueFilter
to
* filter
(if filter not null); or
* - Set
value
as ALWAYS
(if filter null)
*
*
* @since 2.9
* @param filter {@link Class}
* @return {@link Value}
*/
public Value withValueFilter(Class> filter) {
Include incl;
if (filter == null || filter == Void.class) { // clear filter
incl = Include.USE_DEFAULTS;
filter = null;
} else {
incl = Include.CUSTOM;
}
return construct(incl, _contentInclusion, filter, _contentFilter);
}
/**
* Mutant factory that will either
*
*
* - Set
content
as USE_DEFAULTS
and contentFilter
to
* filter
(if filter not null); or
* - Set
content
as ALWAYS
(if filter null)
*
*
* @since 2.9
* @param filter {@link Class}
* @return {@link Value}
*/
public Value withContentFilter(Class> filter) {
Include incl;
if (filter == null || filter == Void.class) { // clear filter
incl = Include.USE_DEFAULTS;
filter = null;
} else {
incl = Include.CUSTOM;
}
return construct(_valueInclusion, incl, _valueFilter, filter);
}
public Value withContentInclusion(Include incl) {
return (incl == _contentInclusion)
? this
: new Value(_valueInclusion, incl, _valueFilter, _contentFilter);
}
@Override
public Class valueFor() {
return JsonInclude.class;
}
public Include getValueInclusion() {
return _valueInclusion;
}
public Include getContentInclusion() {
return _contentInclusion;
}
public Class> getValueFilter() {
return _valueFilter;
}
public Class> getContentFilter() {
return _contentFilter;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(80);
sb.append("JsonInclude.Value(value=")
.append(_valueInclusion)
.append(",content=")
.append(_contentInclusion);
if (_valueFilter != null) {
sb.append(",valueFilter=").append(_valueFilter.getName()).append(".class");
}
if (_contentFilter != null) {
sb.append(",contentFilter=").append(_contentFilter.getName()).append(".class");
}
return sb.append(')').toString();
}
@Override
public int hashCode() {
return (_valueInclusion.hashCode() << 2) + _contentInclusion.hashCode();
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (o == null) return false;
if (o.getClass() != getClass()) return false;
Value other = (Value) o;
return (other._valueInclusion == _valueInclusion)
&& (other._contentInclusion == _contentInclusion)
&& (other._valueFilter == _valueFilter)
&& (other._contentFilter == _contentFilter);
}
}
}