com.fitbur.jackson.annotation.JsonInclude Maven / Gradle / Ivy
package com.fitbur.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.List} would
* exclude List
s with no Java elements, but would include List
s
* with `null` elements. To exclude Lists with only nulls, you would use both
* annotations like so:
*
*public class Bean {
* @JsonInclude(value=Include.NON_EMPTY, content=Include.NON_NULL)
* public List<String> entries;
*}
*
* Similarly you could further exclude Lists, Maps or arrays that only contain
* "empty" elements, or "non-default" values (see {@link Include#NON_EMPTY} and
* {@link Include#NON_DEFAULT} for more details).
*
* @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.
*/
public Include value() default Include.ALWAYS;
/**
* Inclusion rule to use for entries ("content") of annotated
* {@link java.util.Map}s; defaults to {@link Include#ALWAYS}.
*
* @since 2.5
*/
public Include content() default Include.ALWAYS;
/*
/**********************************************************
/* Value enumerations needed
/**********************************************************
*/
/**
* 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}.
*
* Additionally 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 java.lang.String}s,
length()
is called,
* and return value of 0 indicates empty String (note that String.isEmpty()
* was added in Java 1.6 and as such can not be used by Jackson
*
*
* 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 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,
/**
* 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 final static Value EMPTY = new Value(Include.USE_DEFAULTS, Include.USE_DEFAULTS);
protected final Include _valueInclusion;
protected final Include _contentInclusion;
public Value(JsonInclude src) {
this(src.value(), src.content());
}
protected Value(Include vi, Include ci) {
_valueInclusion = (vi == null) ? Include.USE_DEFAULTS : vi;
_contentInclusion = (ci == null) ? Include.USE_DEFAULTS : ci;
}
public static Value empty() {
return EMPTY;
}
// for JDK serialization
protected Object readResolve() {
if ((_valueInclusion == Include.USE_DEFAULTS)
&& (_contentInclusion == Include.USE_DEFAULTS)) {
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.
*/
public Value withOverrides(Value overrides) {
if ((overrides == null) || (overrides == EMPTY)) {
return this;
}
Include vi = overrides._valueInclusion;
Include ci = overrides._contentInclusion;
boolean viDiff = (vi != _valueInclusion) && (vi != Include.USE_DEFAULTS);
boolean ciDiff = (ci != _contentInclusion) && (ci != Include.USE_DEFAULTS);
if (viDiff) {
if (ciDiff) {
return new Value(vi, ci);
}
return new Value(vi, _contentInclusion);
} else if (ciDiff) {
return new Value(_valueInclusion, ci);
}
return this;
}
/**
* Factory method to use for constructing an instance for components
*/
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);
}
/**
* Factory method to use for constructing an instance from instance of
* {@link JsonInclude}
*/
public static Value from(JsonInclude src) {
if (src == null) { // should this return EMPTY?
return null;
}
Include vi = src.value();
Include ci = src.content();
if ((vi == Include.USE_DEFAULTS) && (ci == Include.USE_DEFAULTS)) {
return EMPTY;
}
return new Value(vi, ci);
}
public Value withValueInclusion(Include incl) {
return (incl == _valueInclusion) ? this : new Value(incl, _contentInclusion);
}
public Value withContentInclusion(Include incl) {
return (incl == _contentInclusion) ? this : new Value(_valueInclusion, incl);
}
@Override
public Class valueFor() {
return JsonInclude.class;
}
public Include getValueInclusion() {
return _valueInclusion;
}
public Include getContentInclusion() {
return _contentInclusion;
}
@Override
public String toString() {
return String.format("[value=%s,content=%s]", _valueInclusion, _contentInclusion);
}
@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);
}
}
}