From 0cce1ca0da1995dc92d344103a450c0295138ebd Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 15 Sep 2016 14:47:55 -0700 Subject: [PATCH] Fix #1371: add MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES --- release-notes/VERSION | 2 + .../jackson/databind/MapperFeature.java | 181 ++++++++++-------- .../JacksonAnnotationIntrospector.java | 6 +- .../databind/jsontype/TypeSerializer.java | 46 ++--- .../jsontype/impl/AsArrayTypeSerializer.java | 144 +++++++------- .../impl/AsWrapperTypeSerializer.java | 2 +- .../creators/CreatorPropertiesTest.java | 34 +++- 7 files changed, 240 insertions(+), 175 deletions(-) diff --git a/release-notes/VERSION b/release-notes/VERSION index af7d4f06ab..5cdabb1eb2 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -10,6 +10,8 @@ Project: jackson-databind (contributed by Connor K) #1369: Improve `@JsonCreator` detection via `AnnotationIntrospector` by passing `MappingConfig` +#1371: Add `MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES` to allow + disabling use of `@CreatorProperties` as explicit `@JsonCreator` equivalent 2.8.3 (not yet released) diff --git a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java index e163b5f396..8cd07e2398 100644 --- a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java +++ b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java @@ -18,7 +18,7 @@ public enum MapperFeature implements ConfigFeature { /* /****************************************************** - /* Introspection features + /* General introspection features /****************************************************** */ @@ -32,6 +32,43 @@ public enum MapperFeature implements ConfigFeature */ USE_ANNOTATIONS(true), + /** + * Feature that determines whether otherwise regular "getter" + * methods (but only ones that handle Collections and Maps, + * not getters of other type) + * can be used for purpose of getting a reference to a Collection + * and Map to modify the property, without requiring a setter + * method. + * This is similar to how JAXB framework sets Collections and + * Maps: no setter is involved, just setter. + *

+ * Note that such getters-as-setters methods have lower + * precedence than setters, so they are only used if no + * setter is found for the Map/Collection property. + *

+ * Feature is enabled by default. + */ + USE_GETTERS_AS_SETTERS(true), + + /** + * Feature that determines how transient modifier for fields + * is handled: if disabled, it is only taken to mean exclusion of the field + * as accessor; if true, it is taken to imply removal of the whole property. + *

+ * Feature is disabled by default, meaning that existence of `transient` + * for a field does not necessarily lead to ignoral of getters or setters + * but just ignoring the use of field for access. + * + * @since 2.6 + */ + PROPAGATE_TRANSIENT_MARKER(false), + + /* + /****************************************************** + /* Introspection-based property auto-detection + /****************************************************** + */ + /** * Feature that determines whether "creator" methods are * automatically detected by consider public constructors, @@ -61,7 +98,7 @@ public enum MapperFeature implements ConfigFeature *

* Feature is enabled by default. */ - AUTO_DETECT_FIELDS(true), + AUTO_DETECT_FIELDS(true), /** * Feature that determines whether regular "getter" methods are @@ -98,22 +135,22 @@ public enum MapperFeature implements ConfigFeature */ AUTO_DETECT_IS_GETTERS(true), - /** - * Feature that determines whether "setter" methods are - * automatically detected based on standard Bean naming convention - * or not. If yes, then all public one-argument methods that - * start with prefix "set" - * are considered setters. If disabled, only methods explicitly - * annotated are considered setters. - *

- * Note that this feature has lower precedence than per-class - * annotations, and is only used if there isn't more granular - * configuration available. - *

- * Feature is enabled by default. - */ - AUTO_DETECT_SETTERS(true), - + /** + * Feature that determines whether "setter" methods are + * automatically detected based on standard Bean naming convention + * or not. If yes, then all public one-argument methods that + * start with prefix "set" + * are considered setters. If disabled, only methods explicitly + * annotated are considered setters. + *

+ * Note that this feature has lower precedence than per-class + * annotations, and is only used if there isn't more granular + * configuration available. + *

+ * Feature is enabled by default. + */ + AUTO_DETECT_SETTERS(true), + /** * Feature that determines whether getters (getter methods) * can be auto-detected if there is no matching mutator (setter, @@ -126,22 +163,61 @@ public enum MapperFeature implements ConfigFeature REQUIRE_SETTERS_FOR_GETTERS(false), /** - * Feature that determines whether otherwise regular "getter" - * methods (but only ones that handle Collections and Maps, - * not getters of other type) - * can be used for purpose of getting a reference to a Collection - * and Map to modify the property, without requiring a setter - * method. - * This is similar to how JAXB framework sets Collections and - * Maps: no setter is involved, just setter. + * Feature that determines whether member fields declared as 'final' may + * be auto-detected to be used mutators (used to change value of the logical + * property) or not. If enabled, 'final' access modifier has no effect, and + * such fields may be detected according to usual visibility and inference + * rules; if disabled, such fields are NOT used as mutators except if + * explicitly annotated for such use. *

- * Note that such getters-as-setters methods have lower - * precedence than setters, so they are only used if no - * setter is found for the Map/Collection property. + * Feature is enabled by default, for backwards compatibility reasons. + * + * @since 2.2 + */ + ALLOW_FINAL_FIELDS_AS_MUTATORS(true), + + /** + * Feature that determines whether member mutators (fields and + * setters) may be "pulled in" even if they are not visible, + * as long as there is a visible accessor (getter or field) with same name. + * For example: field "value" may be inferred as mutator, + * if there is visible or explicitly marked getter "getValue()". + * If enabled, inferring is enabled; otherwise (disabled) only visible and + * explicitly annotated accessors are ever used. + *

+ * Note that 'getters' are never inferred and need to be either visible (including + * bean-style naming) or explicitly annotated. *

* Feature is enabled by default. + * + * @since 2.2 + */ + INFER_PROPERTY_MUTATORS(true), + + /** + * Feature that determines handling of java.beans.ConstructorProperties + * annotation: when enabled, it is considered as alias of + * {@link com.fasterxml.jackson.annotation.JsonCreator}, to mean that constructor + * should be considered a property-based Creator; when disabled, only constructor + * parameter name information is used, but constructor is NOT considered an explicit + * Creator (although may be discovered as one using other annotations or heuristics). + *

+ * Feature is mostly used to help interoperability with frameworks like Lombok + * that may automatically generate ConstructorProperties annotation + * but without necessarily meaning that constructor should be used as Creator + * for deserialization. + *

+ * Feature is enabled by default. + * + * @since 2.7 + */ + INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES(true), + + /* + /****************************************************** + /* Access modifier handling + /****************************************************** */ - USE_GETTERS_AS_SETTERS(true), /** * Feature that determines whether method and field access @@ -187,51 +263,6 @@ public enum MapperFeature implements ConfigFeature */ OVERRIDE_PUBLIC_ACCESS_MODIFIERS(true), - /** - * Feature that determines whether member mutators (fields and - * setters) may be "pulled in" even if they are not visible, - * as long as there is a visible accessor (getter or field) with same name. - * For example: field "value" may be inferred as mutator, - * if there is visible or explicitly marked getter "getValue()". - * If enabled, inferring is enabled; otherwise (disabled) only visible and - * explicitly annotated accessors are ever used. - *

- * Note that 'getters' are never inferred and need to be either visible (including - * bean-style naming) or explicitly annotated. - *

- * Feature is enabled by default. - * - * @since 2.2 - */ - INFER_PROPERTY_MUTATORS(true), - - /** - * Feature that determines whether member fields declared as 'final' may - * be auto-detected to be used mutators (used to change value of the logical - * property) or not. If enabled, 'final' access modifier has no effect, and - * such fields may be detected according to usual visibility and inference - * rules; if disabled, such fields are NOT used as mutators except if - * explicitly annotated for such use. - *

- * Feature is enabled by default, for backwards compatibility reasons. - * - * @since 2.2 - */ - ALLOW_FINAL_FIELDS_AS_MUTATORS(true), - - /** - * Feature that determines how transient modifier for fields - * is handled: if disabled, it is only taken to mean exclusion of the field - * as accessor; if true, removal of the whole property. - *

- * Feature is disabled by default, meaning that existence of `transient` - * for a field does not necessarily lead to ignoral of getters or setters - * but just ignoring the use of field for access. - * - * @since 2.6 - */ - PROPAGATE_TRANSIENT_MARKER(false), - /* /****************************************************** /* Type-handling features diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java index 2994e3f0cd..44a1526725 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java @@ -1142,14 +1142,14 @@ public JsonCreator.Mode findCreatorAnnotation(MapperConfig config, Annotated } if (_cfgConstructorPropertiesImpliesCreator -// && config.isEnabled(MapperFeature.)) + && config.isEnabled(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES) ) { if (a instanceof AnnotatedConstructor) { if (_java7Helper != null) { Boolean b = _java7Helper.hasCreatorAnnotation(a); if ((b != null) && b.booleanValue()) { - // 13-Sep-2016, tatu: Judgment call, but I don't JDK ever intended use - // of delegate, but assumed as-properties implicitly + // 13-Sep-2016, tatu: Judgment call, but I don't think JDK ever implies + // use of delegate; assumes as-properties implicitly return JsonCreator.Mode.PROPERTIES; } } diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java index adb51393c9..f4ff596d58 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java @@ -73,9 +73,9 @@ public abstract class TypeSerializer * * @param value Value that will be serialized, for which type information is * to be written - * @param jgen Generator to use for writing type information + * @param g Generator to use for writing type information */ - public abstract void writeTypePrefixForScalar(Object value, JsonGenerator jgen) throws IOException; + public abstract void writeTypePrefixForScalar(Object value, JsonGenerator g) throws IOException; /** * Method called to write initial part of type information for given @@ -86,9 +86,9 @@ public abstract class TypeSerializer * * @param value Value that will be serialized, for which type information is * to be written - * @param jgen Generator to use for writing type information + * @param g Generator to use for writing type information */ - public abstract void writeTypePrefixForObject(Object value, JsonGenerator jgen) throws IOException; + public abstract void writeTypePrefixForObject(Object value, JsonGenerator g) throws IOException; /** * Method called to write initial part of type information for given @@ -99,9 +99,9 @@ public abstract class TypeSerializer * * @param value Value that will be serialized, for which type information is * to be written - * @param jgen Generator to use for writing type information + * @param g Generator to use for writing type information */ - public abstract void writeTypePrefixForArray(Object value, JsonGenerator jgen) throws IOException; + public abstract void writeTypePrefixForArray(Object value, JsonGenerator g) throws IOException; /** * Method called after value has been serialized, to close any scopes opened @@ -109,7 +109,7 @@ public abstract class TypeSerializer * Actual action to take may depend on various factors, but has to match with * action {@link #writeTypePrefixForScalar} did (close array or object; or do nothing). */ - public abstract void writeTypeSuffixForScalar(Object value, JsonGenerator jgen) throws IOException; + public abstract void writeTypeSuffixForScalar(Object value, JsonGenerator g) throws IOException; /** * Method called after value has been serialized, to close any scopes opened @@ -117,7 +117,7 @@ public abstract class TypeSerializer * It needs to write closing END_OBJECT marker, and any other decoration * that needs to be matched. */ - public abstract void writeTypeSuffixForObject(Object value, JsonGenerator jgen) throws IOException; + public abstract void writeTypeSuffixForObject(Object value, JsonGenerator g) throws IOException; /** * Method called after value has been serialized, to close any scopes opened @@ -125,15 +125,15 @@ public abstract class TypeSerializer * It needs to write closing END_ARRAY marker, and any other decoration * that needs to be matched. */ - public abstract void writeTypeSuffixForArray(Object value, JsonGenerator jgen) throws IOException; + public abstract void writeTypeSuffixForArray(Object value, JsonGenerator g) throws IOException; /** * Alternative version of the prefix-for-scalar method, which is given * actual type to use (instead of using exact type of the value); typically * a super type of actual value type */ - public void writeTypePrefixForScalar(Object value, JsonGenerator jgen, Class type) throws IOException { - writeTypePrefixForScalar(value, jgen); + public void writeTypePrefixForScalar(Object value, JsonGenerator g, Class type) throws IOException { + writeTypePrefixForScalar(value, g); } /** @@ -141,8 +141,8 @@ public void writeTypePrefixForScalar(Object value, JsonGenerator jgen, Class * actual type to use (instead of using exact type of the value); typically * a super type of actual value type */ - public void writeTypePrefixForObject(Object value, JsonGenerator jgen, Class type) throws IOException { - writeTypePrefixForObject(value, jgen); + public void writeTypePrefixForObject(Object value, JsonGenerator g, Class type) throws IOException { + writeTypePrefixForObject(value, g); } /** @@ -150,8 +150,8 @@ public void writeTypePrefixForObject(Object value, JsonGenerator jgen, Class * actual type to use (instead of using exact type of the value); typically * a super type of actual value type */ - public void writeTypePrefixForArray(Object value, JsonGenerator jgen, Class type) throws IOException { - writeTypePrefixForArray(value, jgen); + public void writeTypePrefixForArray(Object value, JsonGenerator g, Class type) throws IOException { + writeTypePrefixForArray(value, g); } /* @@ -170,10 +170,10 @@ public void writeTypePrefixForArray(Object value, JsonGenerator jgen, Class t * * @param value Value that will be serialized, for which type information is * to be written - * @param jgen Generator to use for writing type information + * @param g Generator to use for writing type information * @param typeId Exact type id to use */ - public abstract void writeCustomTypePrefixForScalar(Object value, JsonGenerator jgen, String typeId) throws IOException, JsonProcessingException; + public abstract void writeCustomTypePrefixForScalar(Object value, JsonGenerator g, String typeId) throws IOException, JsonProcessingException; /** * Method called to write initial part of type information for given @@ -185,16 +185,16 @@ public void writeTypePrefixForArray(Object value, JsonGenerator jgen, Class t * * @param value Value that will be serialized, for which type information is * to be written - * @param jgen Generator to use for writing type information + * @param g Generator to use for writing type information * @param typeId Exact type id to use */ - public abstract void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException; + public abstract void writeCustomTypePrefixForObject(Object value, JsonGenerator g, String typeId) throws IOException; - public abstract void writeCustomTypePrefixForArray(Object value, JsonGenerator jgen, String typeId) throws IOException; + public abstract void writeCustomTypePrefixForArray(Object value, JsonGenerator g, String typeId) throws IOException; - public abstract void writeCustomTypeSuffixForScalar(Object value, JsonGenerator jgen, String typeId) throws IOException; + public abstract void writeCustomTypeSuffixForScalar(Object value, JsonGenerator g, String typeId) throws IOException; - public abstract void writeCustomTypeSuffixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException; + public abstract void writeCustomTypeSuffixForObject(Object value, JsonGenerator g, String typeId) throws IOException; - public abstract void writeCustomTypeSuffixForArray(Object value, JsonGenerator jgen, String typeId) throws IOException; + public abstract void writeCustomTypeSuffixForArray(Object value, JsonGenerator g, String typeId) throws IOException; } diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java index 0291296d7c..adde36ecac 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java @@ -20,7 +20,7 @@ public AsArrayTypeSerializer(TypeIdResolver idRes, BeanProperty property) { @Override public AsArrayTypeSerializer forProperty(BeanProperty prop) { - return (_property == prop) ? this : new AsArrayTypeSerializer(this._idResolver, prop); + return (_property == prop) ? this : new AsArrayTypeSerializer(_idResolver, prop); } @Override @@ -33,88 +33,88 @@ public AsArrayTypeSerializer forProperty(BeanProperty prop) { */ @Override - public void writeTypePrefixForObject(Object value, JsonGenerator jgen) throws IOException { + public void writeTypePrefixForObject(Object value, JsonGenerator g) throws IOException { final String typeId = idFromValue(value); // NOTE: can not always avoid writing type id, even if null - if (jgen.canWriteTypeId()) { + if (g.canWriteTypeId()) { if (typeId != null) { - jgen.writeTypeId(typeId); + g.writeTypeId(typeId); } } else { - jgen.writeStartArray(); - jgen.writeString(typeId); + g.writeStartArray(); + g.writeString(typeId); } - jgen.writeStartObject(); + g.writeStartObject(); } @Override - public void writeTypePrefixForObject(Object value, JsonGenerator jgen, Class type) throws IOException { + public void writeTypePrefixForObject(Object value, JsonGenerator g, Class type) throws IOException { final String typeId = idFromValueAndType(value, type); // NOTE: can not always avoid writing type id, even if null - if (jgen.canWriteTypeId()) { + if (g.canWriteTypeId()) { if (typeId != null) { - jgen.writeTypeId(typeId); + g.writeTypeId(typeId); } } else { - jgen.writeStartArray(); - jgen.writeString(typeId); + g.writeStartArray(); + g.writeString(typeId); } - jgen.writeStartObject(); + g.writeStartObject(); } @Override - public void writeTypePrefixForArray(Object value, JsonGenerator jgen) throws IOException { + public void writeTypePrefixForArray(Object value, JsonGenerator g) throws IOException { final String typeId = idFromValue(value); - if (jgen.canWriteTypeId()) { + if (g.canWriteTypeId()) { if (typeId != null) { - jgen.writeTypeId(typeId); + g.writeTypeId(typeId); } } else { - jgen.writeStartArray(); - jgen.writeString(typeId); + g.writeStartArray(); + g.writeString(typeId); } - jgen.writeStartArray(); + g.writeStartArray(); } @Override - public void writeTypePrefixForArray(Object value, JsonGenerator jgen, Class type) throws IOException { + public void writeTypePrefixForArray(Object value, JsonGenerator g, Class type) throws IOException { final String typeId = idFromValueAndType(value, type); - if (jgen.canWriteTypeId()) { + if (g.canWriteTypeId()) { if (typeId != null) { - jgen.writeTypeId(typeId); + g.writeTypeId(typeId); } } else { - jgen.writeStartArray(); - jgen.writeString(typeId); + g.writeStartArray(); + g.writeString(typeId); } - jgen.writeStartArray(); + g.writeStartArray(); } @Override - public void writeTypePrefixForScalar(Object value, JsonGenerator jgen) throws IOException { + public void writeTypePrefixForScalar(Object value, JsonGenerator g) throws IOException { final String typeId = idFromValue(value); - if (jgen.canWriteTypeId()) { + if (g.canWriteTypeId()) { if (typeId != null) { - jgen.writeTypeId(typeId); + g.writeTypeId(typeId); } } else { // only need the wrapper array - jgen.writeStartArray(); - jgen.writeString(typeId); + g.writeStartArray(); + g.writeString(typeId); } } @Override - public void writeTypePrefixForScalar(Object value, JsonGenerator jgen, Class type) throws IOException { + public void writeTypePrefixForScalar(Object value, JsonGenerator g, Class type) throws IOException { final String typeId = idFromValueAndType(value, type); - if (jgen.canWriteTypeId()) { + if (g.canWriteTypeId()) { if (typeId != null) { - jgen.writeTypeId(typeId); + g.writeTypeId(typeId); } } else { // only need the wrapper array - jgen.writeStartArray(); - jgen.writeString(typeId); + g.writeStartArray(); + g.writeString(typeId); } } @@ -125,27 +125,27 @@ public void writeTypePrefixForScalar(Object value, JsonGenerator jgen, Class */ @Override - public void writeTypeSuffixForObject(Object value, JsonGenerator jgen) throws IOException { - jgen.writeEndObject(); - if (!jgen.canWriteTypeId()) { - jgen.writeEndArray(); + public void writeTypeSuffixForObject(Object value, JsonGenerator g) throws IOException { + g.writeEndObject(); + if (!g.canWriteTypeId()) { + g.writeEndArray(); } } @Override - public void writeTypeSuffixForArray(Object value, JsonGenerator jgen) throws IOException { + public void writeTypeSuffixForArray(Object value, JsonGenerator g) throws IOException { // first array caller needs to close, then wrapper array - jgen.writeEndArray(); - if (!jgen.canWriteTypeId()) { - jgen.writeEndArray(); + g.writeEndArray(); + if (!g.canWriteTypeId()) { + g.writeEndArray(); } } @Override - public void writeTypeSuffixForScalar(Object value, JsonGenerator jgen) throws IOException { - if (!jgen.canWriteTypeId()) { + public void writeTypeSuffixForScalar(Object value, JsonGenerator g) throws IOException { + if (!g.canWriteTypeId()) { // just the wrapper array to close - jgen.writeEndArray(); + g.writeEndArray(); } } @@ -156,61 +156,61 @@ public void writeTypeSuffixForScalar(Object value, JsonGenerator jgen) throws IO */ @Override - public void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException { - if (jgen.canWriteTypeId()) { + public void writeCustomTypePrefixForObject(Object value, JsonGenerator g, String typeId) throws IOException { + if (g.canWriteTypeId()) { if (typeId != null) { - jgen.writeTypeId(typeId); + g.writeTypeId(typeId); } } else { - jgen.writeStartArray(); - jgen.writeString(typeId); + g.writeStartArray(); + g.writeString(typeId); } - jgen.writeStartObject(); + g.writeStartObject(); } @Override - public void writeCustomTypePrefixForArray(Object value, JsonGenerator jgen, String typeId) throws IOException { - if (jgen.canWriteTypeId()) { + public void writeCustomTypePrefixForArray(Object value, JsonGenerator g, String typeId) throws IOException { + if (g.canWriteTypeId()) { if (typeId != null) { - jgen.writeTypeId(typeId); + g.writeTypeId(typeId); } } else { - jgen.writeStartArray(); - jgen.writeString(typeId); + g.writeStartArray(); + g.writeString(typeId); } - jgen.writeStartArray(); + g.writeStartArray(); } @Override - public void writeCustomTypePrefixForScalar(Object value, JsonGenerator jgen, String typeId) throws IOException { - if (jgen.canWriteTypeId()) { + public void writeCustomTypePrefixForScalar(Object value, JsonGenerator g, String typeId) throws IOException { + if (g.canWriteTypeId()) { if (typeId != null) { - jgen.writeTypeId(typeId); + g.writeTypeId(typeId); } } else { - jgen.writeStartArray(); - jgen.writeString(typeId); + g.writeStartArray(); + g.writeString(typeId); } } @Override - public void writeCustomTypeSuffixForObject(Object value, JsonGenerator jgen, String typeId) throws IOException { - if (!jgen.canWriteTypeId()) { - writeTypeSuffixForObject(value, jgen); // standard impl works fine + public void writeCustomTypeSuffixForObject(Object value, JsonGenerator g, String typeId) throws IOException { + if (!g.canWriteTypeId()) { + writeTypeSuffixForObject(value, g); // standard impl works fine } } @Override - public void writeCustomTypeSuffixForArray(Object value, JsonGenerator jgen, String typeId) throws IOException { - if (!jgen.canWriteTypeId()) { - writeTypeSuffixForArray(value, jgen); // standard impl works fine + public void writeCustomTypeSuffixForArray(Object value, JsonGenerator g, String typeId) throws IOException { + if (!g.canWriteTypeId()) { + writeTypeSuffixForArray(value, g); // standard impl works fine } } @Override - public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator jgen, String typeId) throws IOException { - if (!jgen.canWriteTypeId()) { - writeTypeSuffixForScalar(value, jgen); // standard impl works fine + public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator g, String typeId) throws IOException { + if (!g.canWriteTypeId()) { + writeTypeSuffixForScalar(value, g); // standard impl works fine } } } diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java index 647d031be4..feab592c53 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java @@ -24,7 +24,7 @@ public AsWrapperTypeSerializer(TypeIdResolver idRes, BeanProperty property) { @Override public AsWrapperTypeSerializer forProperty(BeanProperty prop) { - return (_property == prop) ? this : new AsWrapperTypeSerializer(this._idResolver, prop); + return (_property == prop) ? this : new AsWrapperTypeSerializer(_idResolver, prop); } @Override diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/CreatorPropertiesTest.java b/src/test/java/com/fasterxml/jackson/databind/creators/CreatorPropertiesTest.java index b3bc4c4cee..d415cc5e78 100644 --- a/src/test/java/com/fasterxml/jackson/databind/creators/CreatorPropertiesTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/creators/CreatorPropertiesTest.java @@ -25,7 +25,6 @@ public Issue905Bean(int a, int b) { // for [databind#1122] static class Ambiguity { - @JsonProperty("bar") private int foo; @@ -46,6 +45,19 @@ public String toString() { } } + // for [databind#1371] + static class Lombok1371Bean { + public int x, y; + + protected Lombok1371Bean() { } + + @ConstructorProperties({ "x", "y" }) + public Lombok1371Bean(int _x, int _y) { + x = _x + 1; + y = _y + 1; + } + } + /* /********************************************************** /* Test methods @@ -71,4 +83,24 @@ public void testPossibleNamingConflict() throws Exception assertNotNull(amb); assertEquals(3, amb.getFoo()); } + + // [databind#1371]: MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES + public void testConstructorPropertiesInference() throws Exception + { + final String JSON = aposToQuotes("{'x':3,'y':5}"); + + // by default, should detect and use arguments-taking constructor as creator + assertTrue(MAPPER.isEnabled(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES)); + Lombok1371Bean result = MAPPER.readValue(JSON, Lombok1371Bean.class); + assertEquals(4, result.x); + assertEquals(6, result.y); + + // but change if configuration changed + ObjectMapper mapper = new ObjectMapper(); + mapper.disable(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES); + // in which case fields are set directly: + result = mapper.readValue(JSON, Lombok1371Bean.class); + assertEquals(3, result.x); + assertEquals(5, result.y); + } }