diff --git a/release-notes/VERSION b/release-notes/VERSION index 4722e00d..e4b04c27 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -14,10 +14,11 @@ NOTE: Annotations module will never contain changes in patch versions, 2.9.0 (not yet released) #103: Add `JsonInclude.Include.CUSTOM`, properties for specifying filter(s) to use -#104: Add new properties in `@JsonSetter`: `merge`, `null`/`contentNulls` +#104: Add new properties in `@JsonSetter`: `nulls`/`contentNulls` #105: Add `@JsonFormat.lenient` to allow configuring lenience of date/time deserializers #108: Allow `@JsonValue` on fields #109: Add `enabled` for `@JsonAnyGetter`, `@JsonAnySetter`, to allow disabling via mix-ins +#113: Add `@JsonMerge` to support (deep) merging of properties - Allow use of `@JsonView` on classes, to specify Default View to use on non-annotated properties. diff --git a/src/main/java/com/fasterxml/jackson/annotation/JsonMerge.java b/src/main/java/com/fasterxml/jackson/annotation/JsonMerge.java new file mode 100644 index 00000000..1302d397 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/annotation/JsonMerge.java @@ -0,0 +1,51 @@ +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 to specify whether annotated property value should use "merging" approach, + * in which current value is first accessed (with a getter or field) and then modified + * with incoming data, or not: if not, assignment happens without considering current state. + *
+ * Merging is only option if there is a way to introspect current state:
+ * if there is accessor (getter, field) to use.
+ * Merging can not be enabled if no accessor exists
+ * or if assignment occurs using a Creator setter (constructor
+ * or factory method), since there is no instance with state to introspect.
+ * Merging also only has actual effect for structured types where there is an
+ * obvious way to update a state (for example, POJOs have default values for properties,
+ * and {@link java.util.Collection}s and {@link java.util.Map}s may have existing
+ * elements; whereas scalar types do not such state: an int
has a value,
+ * but no obvious and non-ambiguous way to merge state.
+ *
+ * Merging is applied by using a deserialization method that accepts existing state
+ * as an argument: it is then up to JsonDeserializer
implementation
+ * to use that base state in a way that makes sense without further configuration.
+ * For structured types this is usually obvious; and for scalar types not -- if
+ * no obvious method exists, merging is not allowed; deserializer may choose to
+ * either quietly ignore it, or throw an exception.
+ *
+ * Note that use of merging usually adds some processing overhead since it adds + * an extra step of accessing the current state before assignment. + *
+ * Note also that "root values" (values directly deserialized and not reached
+ * via POJO properties) can not use this annotation; instead, ObjectMapper
+ * and Object
have "updating reader" operations.
+ *
+ * Default value is {@link OptBoolean#TRUE}, that is, merging is enabled. + * + * @since 2.9 + */ +@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@JacksonAnnotation +public @interface JsonMerge +{ + /** + * Whether merging should or should not be enabled for the annotated property. + */ + OptBoolean value() default OptBoolean.TRUE; +} diff --git a/src/main/java/com/fasterxml/jackson/annotation/JsonSetter.java b/src/main/java/com/fasterxml/jackson/annotation/JsonSetter.java index 704d5643..40a5d173 100644 --- a/src/main/java/com/fasterxml/jackson/annotation/JsonSetter.java +++ b/src/main/java/com/fasterxml/jackson/annotation/JsonSetter.java @@ -9,8 +9,6 @@ * {@link JsonProperty} annotation; * or (as of 2.9 and later), specify additional aspects of the * assigning property a value during serialization. - *
- *
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
// ^^^ allowed on Fields, (constructor) parameters since 2.9
@@ -47,35 +45,6 @@
* is usually {@link Nulls#SET}, meaning that the `null` is included as usual.
*/
Nulls contentNulls() default Nulls.DEFAULT;
-
- /**
- * Specifies whether property value should use "merging" approach, in which
- * current value is first accessed (with a getter), or not; if not,
- * assignment happens without considering current state.
- * Merging is not an option if there is no way to introspect
- * current state: for example, if there is no
- * accessor to use, or if assignment occurs using a Creator setter (constructor
- * or factory method), since there is no instance with state to introspect.
- * Merging also only has actual effect for structured types where there is an
- * obvious way to update a state (for example, POJOs have default values for properties,
- * and {@link java.util.Collection}s and {@link java.util.Map}s may have existing
- * elements; whereas scalar types do not such state: an int
has a value,
- * but no obvious and non-ambiguous way to merge state.
- *
- * Merging is applied by using a deserialization method that accepts existing state
- * as an argument: it is then up to JsonDeserializer
implementation
- * to use that base state in a way that makes sense without further configuration.
- * For structured types this is usually obvious; and for scalar types not -- if
- * no obvious method exists, merging is not allowed; deserializer may choose to
- * either quietly ignore it, or throw an exception.
- *
- * Note also that use of merging usually adds some processing overhead since it adds - * an extra step of accessing the current state before assignment. - *
- * Default value is to "use defaults"; in absence of any explicit configuration - * this would mean {@link OptBoolean#FALSE}, that is, merging is not enabled. - */ - OptBoolean merge() default OptBoolean.DEFAULT; /* /********************************************************** @@ -144,8 +113,6 @@ public static class Value { private static final long serialVersionUID = 1L; - private final Boolean _merge; - private final Nulls _nulls; private final Nulls _contentNulls; @@ -153,17 +120,16 @@ public static class Value /** * Default instance used in place of "default settings". */ - protected final static Value EMPTY = new Value(null, Nulls.DEFAULT, Nulls.DEFAULT); + protected final static Value EMPTY = new Value(Nulls.DEFAULT, Nulls.DEFAULT); - protected Value(Boolean merge, Nulls nulls, Nulls contentNulls) { - _merge = merge; + protected Value(Nulls nulls, Nulls contentNulls) { _nulls = nulls; _contentNulls = contentNulls; } // for JDK serialization protected Object readResolve() { - if (_empty(_merge, _nulls, _contentNulls)) { + if (_empty(_nulls, _contentNulls)) { return EMPTY; } return this; @@ -173,8 +139,7 @@ public static Value from(JsonSetter src) { if (src == null) { return EMPTY; } - return construct(src.merge().asBoolean(), - src.nulls(), src.contentNulls()); + return construct(src.nulls(), src.contentNulls()); } /** @@ -184,24 +149,22 @@ public static Value from(JsonSetter src) { * methods, as this factory method may need to be changed if new properties * are added in {@link JsonIgnoreProperties} annotation. */ - public static Value construct(Boolean merge, Nulls nulls, Nulls contentNulls) { + public static Value construct(Nulls nulls, Nulls contentNulls) { if (nulls == null) { nulls = Nulls.DEFAULT; } if (contentNulls == null) { contentNulls = Nulls.DEFAULT; } - if (_empty(merge, nulls, contentNulls)) { + if (_empty(nulls, contentNulls)) { return EMPTY; } - return new Value(merge, nulls, contentNulls); + return new Value(nulls, contentNulls); } /** * Accessor for default instances which has "empty" settings; that is: *