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;
import java.util.*;
/**
* Annotation that can be used to either suppress serialization of
* properties (during serialization), or ignore processing of
* JSON properties read (during deserialization).
*
* Example:
*
* // to prevent specified fields from being serialized or deserialized
* // (i.e. not include in JSON output; or being set even if they were included)
* @JsonIgnoreProperties({ "internalId", "secretKey" })
* // To ignore any unknown properties in JSON input without exception:
* @JsonIgnoreProperties(ignoreUnknown=true)
*
*
* Annotation can be applied both to classes and
* to properties. If used for both, actual set will be union of all
* ignorals: that is, you can only add properties to ignore, not remove
* or override. So you can not remove properties to ignore using
* per-property annotation.
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE,
ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonIgnoreProperties
{
/**
* Names of properties to ignore.
*/
public String[] value() default { };
/**
* Property that defines whether it is ok to just ignore any
* unrecognized properties during deserialization.
* If true, all properties that are unrecognized -- that is,
* there are no setters or creators that accept them -- are
* ignored without warnings (although handlers for unknown
* properties, if any, will still be called) without
* exception.
*
* Does not have any effect on serialization.
*
* @return True if any and all unknown properties are to be ignored without
* exceptions (or other special handling); false otherwise.
*/
public boolean ignoreUnknown() default false;
/**
* Property that can be enabled to allow "getters" to be used (that is,
* prevent ignoral of getters for properties listed in {@link #value()}).
* This is commonly set to support defining "read-only" properties; ones
* for which there is a getter, but no matching setter: in this case,
* properties should be ignored for deserialization but NOT serialization.
* Another way to think about this setting is that setting it to `true`
* will "disable" ignoring of getters.
*
* Default value is `false`, which means that getters with matching names
* will be ignored.
*
* @return True if getters should be allowed (i.e. NOT ignored); false if getters
* are to be ignored
*
* @since 2.6
*/
public boolean allowGetters() default false;
/**
* Property that can be enabled to allow "setters" to be used (that is,
* prevent ignoral of setters for properties listed in {@link #value()}).
* This could be used to specify "write-only" properties; ones
* that should not be serialized out, but that may be provided in for
* deserialization.
* Another way to think about this setting is that setting it to `true`
* will "disable" ignoring of setters.
*
* Default value is `false`, which means that setters with matching names
* will be ignored.
*
*
* @return True if setters should be allowed (i.e. NOT ignored); false if setters
* are to be ignored
* @since 2.6
*/
public boolean allowSetters() default false;
/*
/**********************************************************
/* Value class used to enclose information, allow for
/* merging of layered configuration settings.
/**********************************************************
*/
/**
* Helper class used to contain information from a single {@link JsonIgnoreProperties}
* annotation, as well as to provide possible overrides from non-annotation sources.
*
* @since 2.8
*/
public static class Value
implements JacksonAnnotationValue,
java.io.Serializable
{
private static final long serialVersionUID = 1L;
/**
* Default instance has no explicitly ignored fields, does not ignore unknowns,
* does not explicitly allow getters/setters (that is, ignorals apply to both),
* but does use merging for combining overrides with base settings
*/
protected final static Value EMPTY = new Value(Collections.emptySet(),
false, false, false, true);
/**
* Names of properties to ignore.
*/
protected final Set _ignored;
protected final boolean _ignoreUnknown;
protected final boolean _allowGetters;
protected final boolean _allowSetters;
protected final boolean _merge;
protected Value(Set ignored, boolean ignoreUnknown,
boolean allowGetters, boolean allowSetters,
boolean merge)
{
if (ignored == null) {
_ignored = Collections.emptySet();
} else {
_ignored = ignored;
}
_ignoreUnknown = ignoreUnknown;
_allowGetters = allowGetters;
_allowSetters = allowSetters;
_merge = merge;
}
public static Value from(JsonIgnoreProperties src) {
if (src == null) {
return EMPTY; // since 2.9
}
return construct(_asSet(src.value()),
src.ignoreUnknown(), src.allowGetters(), src.allowSetters(),
// 27-Apr-2016, tatu: No matching property in annotation because
// we don't know how to merge (so no point in pretending it's there)
// so choice is arbitrary. Probably will default to `false` fwtw:
false);
}
/**
* Factory method that may be used (although is NOT the recommended way)
* to construct an instance from a full set of properties. Most users would
* be better of starting by {@link #empty()} instance and using `withXxx()`/`withoutXxx()`
* methods, as this factory method may need to be changed if new properties
* are added in {@link JsonIgnoreProperties} annotation.
*/
public static Value construct(Set ignored, boolean ignoreUnknown,
boolean allowGetters, boolean allowSetters,
boolean merge) {
if (_empty(ignored, ignoreUnknown, allowGetters, allowSetters, merge)) {
return EMPTY;
}
return new Value(ignored, ignoreUnknown, allowGetters, allowSetters, merge);
}
/**
* Accessor for default instances which has "empty" settings; that is:
*
* - No explicitly defined fields to ignore
*
* - Does not ignore unknown fields
*
* - Does not "allow" getters if property ignored (that is, ignorals apply to both setter and getter)
*
* - Does not "allow" setters if property ignored (that is, ignorals apply to both setter and getter)
*
* - Does use merge when combining overrides to base settings, such that `true` settings
* for any of the properties results in `true`, and names of fields are combined (union)
*
*
*/
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.
*/
public static Value merge(Value base, Value overrides)
{
return (base == null) ? overrides
: base.withOverrides(overrides);
}
/**
* @since 2.8
*/
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;
}
public static Value forIgnoredProperties(Set propNames) {
return EMPTY.withIgnored(propNames);
}
public static Value forIgnoredProperties(String... propNames) {
if (propNames.length == 0) {
return EMPTY;
}
return EMPTY.withIgnored(_asSet(propNames));
}
public static Value forIgnoreUnknown(boolean state) {
return state ? EMPTY.withIgnoreUnknown()
: EMPTY.withoutIgnoreUnknown();
}
/**
* 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;
}
// if non merging, we'll actually end up with just the overrides don't we?
// (given there's no "use default" value for anything
if (!overrides._merge) {
return overrides;
}
if (_equals(this, overrides)) {
return this;
}
// Here's where mergeability needs to be checked
Set ignored = _merge(_ignored, overrides._ignored);
boolean ignoreUnknown = _ignoreUnknown || overrides._ignoreUnknown;
boolean allowGetters = _allowGetters || overrides._allowGetters;
boolean allowSetters = _allowSetters || overrides._allowSetters;
// must have 'merge=true' to get this far
return construct(ignored, ignoreUnknown, allowGetters, allowSetters, true);
}
public Value withIgnored(Set ignored) {
return construct(ignored, _ignoreUnknown, _allowGetters, _allowSetters, _merge);
}
public Value withIgnored(String... ignored) {
return construct(_asSet(ignored), _ignoreUnknown, _allowGetters, _allowSetters, _merge);
}
public Value withoutIgnored() {
return construct(null, _ignoreUnknown, _allowGetters, _allowSetters, _merge);
}
public Value withIgnoreUnknown() {
return _ignoreUnknown ? this :
construct(_ignored, true, _allowGetters, _allowSetters, _merge);
}
public Value withoutIgnoreUnknown() {
return !_ignoreUnknown ? this :
construct(_ignored, false, _allowGetters, _allowSetters, _merge);
}
public Value withAllowGetters() {
return _allowGetters ? this :
construct(_ignored, _ignoreUnknown, true, _allowSetters, _merge);
}
public Value withoutAllowGetters() {
return !_allowGetters ? this :
construct(_ignored, _ignoreUnknown, false, _allowSetters, _merge);
}
public Value withAllowSetters() {
return _allowSetters ? this :
construct(_ignored, _ignoreUnknown, _allowGetters, true, _merge);
}
public Value withoutAllowSetters() {
return !_allowSetters ? this :
construct(_ignored, _ignoreUnknown, _allowGetters, false, _merge);
}
public Value withMerge() {
return _merge ? this :
construct(_ignored, _ignoreUnknown, _allowGetters, _allowSetters, true);
}
public Value withoutMerge() {
return !_merge ? this :
construct(_ignored, _ignoreUnknown, _allowGetters, _allowSetters, false);
}
@Override
public Class valueFor() {
return JsonIgnoreProperties.class;
}
// for JDK serialization
protected Object readResolve() {
if (_empty(_ignored, _ignoreUnknown, _allowGetters, _allowSetters, _merge)) {
return EMPTY;
}
return this;
}
public Set getIgnored() {
return _ignored;
}
/**
* Method called to find names of properties to ignore when used for
* serialization: functionally
* same as {@link #getIgnored} if {@link #getAllowGetters()} is false
* (that is, there is "allowGetters=false" or equivalent),
* otherwise returns empty {@link java.util.Set}.
*/
public Set findIgnoredForSerialization() {
if (_allowGetters) {
return Collections.emptySet();
}
return _ignored;
}
/**
* Method called to find names of properties to ignore when used for
* serialization: functionally
* same as {@link #getIgnored} if {@link #getAllowSetters()} is false
* (that is, there is "allowSetters=false" or equivalent),
* otherwise returns empty {@link java.util.Set}.
*/
public Set findIgnoredForDeserialization() {
if (_allowSetters) {
return Collections.emptySet();
}
return _ignored;
}
public boolean getIgnoreUnknown() {
return _ignoreUnknown;
}
public boolean getAllowGetters() {
return _allowGetters;
}
public boolean getAllowSetters() {
return _allowSetters;
}
public boolean getMerge() {
return _merge;
}
@Override
public String toString() {
return String.format("JsonIgnoreProperties.Value(ignored=%s,ignoreUnknown=%s,allowGetters=%s,allowSetters=%s,merge=%s)",
_ignored, _ignoreUnknown, _allowGetters, _allowSetters, _merge);
}
@Override
public int hashCode() {
return (_ignored.size())
+ (_ignoreUnknown ? 1 : -3)
+ (_allowGetters ? 3 : -7)
+ (_allowSetters ? 7 : -11)
+ (_merge ? 11 : -13)
;
}
@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._ignoreUnknown == b._ignoreUnknown)
&& (a._merge == b._merge)
&& (a._allowGetters == b._allowGetters)
&& (a._allowSetters == b._allowSetters)
// this last just because it can be expensive
&& a._ignored.equals(b._ignored)
;
}
private static Set _asSet(String[] v) {
if (v == null || v.length == 0) {
return Collections.emptySet();
}
Set s = new HashSet(v.length);
for (String str : v) {
s.add(str);
}
return s;
}
private static Set _merge(Set s1, Set s2)
{
if (s1.isEmpty()) {
return s2;
} else if (s2.isEmpty()) {
return s1;
}
HashSet result = new HashSet(s1.size() + s2.size());
result.addAll(s1);
result.addAll(s2);
return result;
}
private static boolean _empty(Set ignored, boolean ignoreUnknown,
boolean allowGetters, boolean allowSetters, boolean merge)
{
if ((ignoreUnknown == EMPTY._ignoreUnknown)
&& (allowGetters == EMPTY._allowGetters)
&& (allowSetters == EMPTY._allowSetters)
&& (merge == EMPTY._merge)) {
return (ignored == null || ignored.size() == 0);
}
return false;
}
}
}