1 package com.fasterxml.jackson.annotation; 2 3 import java.lang.annotation.*; 4 5 /** 6 * Annotation that can be used to define a non-static, 7 * single-argument method to be used as a "setter" for a logical property 8 * as an alternative to recommended 9 * {@link JsonProperty} annotation; 10 * or (as of 2.9 and later), specify additional aspects of the 11 * assigning property a value during serialization. 12 */ 13 @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) 14 // ^^^ allowed on Fields, (constructor) parameters since 2.9 15 @Retention(RetentionPolicy.RUNTIME) 16 @JacksonAnnotation 17 public @interface JsonSetter 18 { 19 /** 20 * Optional default argument that defines logical property this 21 * method is used to modify ("set"); this is the property 22 * name used in JSON content. 23 */ value()24 String value() default ""; 25 26 /** 27 * Specifies action to take when input contains explicit `null` value 28 * (if format has one). 29 * Default action, in absence of any explicit configuration, 30 * is usually {@link Nulls#SET}, meaning that the `null` is set as 31 * value using setter. 32 *<p> 33 * NOTE: is not usually used in case property value is missing, unless 34 * data format specifies that there is defaulting which would result 35 * in an explicit null assignment. 36 */ nulls()37 Nulls nulls() default Nulls.DEFAULT; 38 39 /** 40 * Specifies action to take when input to match into content value 41 * (of a {@link java.util.Collection}, {@link java.util.Map}, array, 42 * or referential value) contains explicit `null` value 43 * (if format has one) to bind. 44 * Default action, in absence of any explicit configuration, 45 * is usually {@link Nulls#SET}, meaning that the `null` is included as usual. 46 */ contentNulls()47 Nulls contentNulls() default Nulls.DEFAULT; 48 49 /* 50 /********************************************************** 51 /* Value class used to enclose information, allow for 52 /* merging of layered configuration settings. 53 /********************************************************** 54 */ 55 56 /** 57 * Helper class used to contain information from a single {@link JsonSetter} 58 * annotation, as well as to provide possible overrides from non-annotation sources. 59 * 60 * @since 2.9 61 */ 62 public static class Value 63 implements JacksonAnnotationValue<JsonSetter>, 64 java.io.Serializable 65 { 66 private static final long serialVersionUID = 1L; 67 68 private final Nulls _nulls; 69 70 private final Nulls _contentNulls; 71 72 /** 73 * Default instance used in place of "default settings". 74 */ 75 protected final static Value EMPTY = new Value(Nulls.DEFAULT, Nulls.DEFAULT); 76 Value(Nulls nulls, Nulls contentNulls)77 protected Value(Nulls nulls, Nulls contentNulls) { 78 _nulls = nulls; 79 _contentNulls = contentNulls; 80 } 81 82 @Override valueFor()83 public Class<JsonSetter> valueFor() { 84 return JsonSetter.class; 85 } 86 87 // for JDK serialization readResolve()88 protected Object readResolve() { 89 if (_empty(_nulls, _contentNulls)) { 90 return EMPTY; 91 } 92 return this; 93 } 94 from(JsonSetter src)95 public static Value from(JsonSetter src) { 96 if (src == null) { 97 return EMPTY; 98 } 99 return construct(src.nulls(), src.contentNulls()); 100 } 101 102 /** 103 * Factory method that may be used (although is NOT the recommended way) 104 * to construct an instance from a full set of properties. Most users would 105 * be better of starting by {@link #empty()} instance and using `withXxx`/`withoutXxx` 106 * methods, as this factory method may need to be changed if new properties 107 * are added in {@link JsonIgnoreProperties} annotation. 108 */ construct(Nulls nulls, Nulls contentNulls)109 public static Value construct(Nulls nulls, Nulls contentNulls) { 110 if (nulls == null) { 111 nulls = Nulls.DEFAULT; 112 } 113 if (contentNulls == null) { 114 contentNulls = Nulls.DEFAULT; 115 } 116 if (_empty(nulls, contentNulls)) { 117 return EMPTY; 118 } 119 return new Value(nulls, contentNulls); 120 } 121 122 /** 123 * Accessor for default instances which has "empty" settings; that is: 124 *<ul> 125 * <li>Null handling using global defaults, {@link Nulls#DEFAULT}. 126 * </li> 127 * </ul> 128 */ empty()129 public static Value empty() { 130 return EMPTY; 131 } 132 133 /** 134 * Helper method that will try to combine values from two {@link Value} 135 * instances, using one as base settings, and the other as overrides 136 * to use instead of base values when defined; base values are only 137 * use if override does not specify a value (matching value is null 138 * or logically missing). 139 * Note that one or both of value instances may be `null`, directly; 140 * if both are `null`, result will also be `null`; otherwise never null. 141 */ merge(Value base, Value overrides)142 public static Value merge(Value base, Value overrides) 143 { 144 return (base == null) ? overrides 145 : base.withOverrides(overrides); 146 } 147 forValueNulls(Nulls nulls)148 public static Value forValueNulls(Nulls nulls) { 149 return construct(nulls, Nulls.DEFAULT); 150 } 151 forValueNulls(Nulls nulls, Nulls contentNulls)152 public static Value forValueNulls(Nulls nulls, Nulls contentNulls) { 153 return construct(nulls, contentNulls); 154 } 155 forContentNulls(Nulls nulls)156 public static Value forContentNulls(Nulls nulls) { 157 return construct(Nulls.DEFAULT, nulls); 158 } 159 160 /** 161 * Mutant factory method that merges values of this value with given override 162 * values, so that any explicitly defined inclusion in overrides has precedence over 163 * settings of this value instance. If no overrides exist will return <code>this</code> 164 * instance; otherwise new {@link Value} with changed inclusion values. 165 */ withOverrides(Value overrides)166 public Value withOverrides(Value overrides) { 167 if ((overrides == null) || (overrides == EMPTY)) { 168 return this; 169 } 170 Nulls nulls = overrides._nulls; 171 Nulls contentNulls = overrides._contentNulls; 172 173 if (nulls == Nulls.DEFAULT) { 174 nulls = _nulls; 175 } 176 if (contentNulls == Nulls.DEFAULT) { 177 contentNulls = _contentNulls; 178 } 179 180 if ((nulls == _nulls) && (contentNulls == _contentNulls)) { 181 return this; 182 } 183 return construct(nulls, contentNulls); 184 } 185 withValueNulls(Nulls nulls)186 public Value withValueNulls(Nulls nulls) { 187 if (nulls == null) { 188 nulls = Nulls.DEFAULT; 189 } 190 if (nulls == _nulls) { 191 return this; 192 } 193 return construct(nulls, _contentNulls); 194 } 195 withValueNulls(Nulls valueNulls, Nulls contentNulls)196 public Value withValueNulls(Nulls valueNulls, Nulls contentNulls) { 197 if (valueNulls == null) { 198 valueNulls = Nulls.DEFAULT; 199 } 200 if (contentNulls == null) { 201 contentNulls = Nulls.DEFAULT; 202 } 203 if ((valueNulls == _nulls) && (contentNulls == _contentNulls)) { 204 return this; 205 } 206 return construct(valueNulls, contentNulls); 207 } 208 withContentNulls(Nulls nulls)209 public Value withContentNulls(Nulls nulls) { 210 if (nulls == null) { 211 nulls = Nulls.DEFAULT; 212 } 213 if (nulls == _contentNulls) { 214 return this; 215 } 216 return construct(_nulls, nulls); 217 } 218 getValueNulls()219 public Nulls getValueNulls() { return _nulls; } getContentNulls()220 public Nulls getContentNulls() { return _contentNulls; } 221 222 /** 223 * Returns same as {@link #getValueNulls()} unless value would be 224 * {@link Nulls#DEFAULT} in which case `null` is returned. 225 */ nonDefaultValueNulls()226 public Nulls nonDefaultValueNulls() { 227 return (_nulls == Nulls.DEFAULT) ? null : _nulls; 228 } 229 230 /** 231 * Returns same as {@link #getContentNulls()} unless value would be 232 * {@link Nulls#DEFAULT} in which case `null` is returned. 233 */ nonDefaultContentNulls()234 public Nulls nonDefaultContentNulls() { 235 return (_contentNulls == Nulls.DEFAULT) ? null : _contentNulls; 236 } 237 238 /* 239 /********************************************************** 240 /* Std method overrides 241 /********************************************************** 242 */ 243 244 @Override toString()245 public String toString() { 246 return String.format("JsonSetter.Value(valueNulls=%s,contentNulls=%s)", 247 _nulls, _contentNulls); 248 } 249 250 @Override hashCode()251 public int hashCode() { 252 return _nulls.ordinal() + (_contentNulls.ordinal() << 2); 253 } 254 255 @Override equals(Object o)256 public boolean equals(Object o) { 257 if (o == this) return true; 258 if (o == null) return false; 259 if (o.getClass() == getClass()) { 260 Value other = (Value) o; 261 return (other._nulls == _nulls) 262 && (other._contentNulls == _contentNulls); 263 } 264 return false; 265 } 266 267 /* 268 /********************************************************** 269 /* Internal methods 270 /********************************************************** 271 */ 272 _empty(Nulls nulls, Nulls contentNulls)273 private static boolean _empty(Nulls nulls, Nulls contentNulls) { 274 return (nulls == Nulls.DEFAULT) 275 && (contentNulls == Nulls.DEFAULT); 276 } 277 } 278 } 279