Jackson-databind: Add support for @ConstructorProperties

Created on 24 Aug 2015  路  21Comments  路  Source: FasterXML/jackson-databind

ConstructorProperties provide a standard way to communicate constructor properties. Currently, you have to use JsonCreator with JsonProperty to achieve the same behavior with jackson.

Libraries like lombok generate constructors using the standard annotation, which makes it necessary to manually annotate the constructor when using jackson. There are already libraries that add support for this annotation but I believe it could be added to the main project without problems.

I believe ConstructorProperties is absent on android so it might be necessary to handle that gracefully.

Most helpful comment

For anyone else who stumbles upon this - as a result of this change, on upgrading to Jackson 2.7, trying to deserialize a class which is

  • annotated with Lombok's @AllArgsConstructor annotation, and
  • has a constructor annotated with @JsonCreator

I started having errors along the following lines:

com.fasterxml.jackson.databind.JsonMappingException: Conflicting property-based creators: already had explicitly marked
[constructor for com.package.class, annotations: {interface java.beans.[email protected](value=[all, my, instance, variables)}], encountered
[constructor for com.package.class, annotations: {interface com.fasterxml.jackson.annotation.[email protected](mode=DEFAULT)}]

This is because under the covers Lombok's constructors use the ConstructorProperties annotation by default. The problem can be fixed by setting the following value.
lombok.anyConstructor.suppressConstructorProperties = true.

Hope that's useful...

All 21 comments

Sounds like a useful improvement, thank you for suggestion. I wasn't aware of existence of @ConstructorProperties, which is the reason this feature doesn't yet exist. But it should be possible to add for 2.7.

Just wanted to let you know I am also looking forward to this feature.

Support added for 2.7.0.

Thanks, I'll try it out at least! :+1:

Official 2.7.0 will take some time, but code is in master branch now.

It seems, (in 2.7.3) that the constructorProperties are not correctly parsed when using a custom namingstrategy... still in the progress of figuring out if this is actually the case, but sure looks like it.

@Athaphian there are two possible interpretations for meaning of @ConstructorProperties:

  1. Assume they are same as "implicit names"; that is, similar to finding names of getters or fields, and may be renamed with naming strategy
  2. Assume they are "explicit names", similar to using @JsonProperty: these are NOT subject to renaming

My assumption was that (2) is the proper usage, since this is for @JsonCreator with @JsonProperty works. However, it seems that in many cases (1) would be desired instead.
I am not quite sure how this conflict could be resolved, given that there is no way to add anything in @ConstructorProperties (for example to explicitly allow choosing mode).

@cowtowncoder Maybe it can be made configurable as a config option on the mapper? setConstructorPropertiesImplicit or something... and later on ignore the explicitly set constructor properties names? I have not looked at the Jackson code, so don't know if this approach is even possible.

@Athaphian right, I am thinking along same lines. Could you file a new issue for such a MapperFeature (has to be there as it can not be enabled on per-call basis).

Fwtw, there is already MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING (added in 2.7), which does allow addressing this, but it has effect on all annotations, so not side-effect-free.

Actually, I think that I will make the change for 2.7.4, since this would solve #1122. The only (?) open question would be whether to:

  1. Make @ConstructorProperties indicate similarity to @JsonCreator in indicating that annotated constructor is to be used as Creator or not; either by default, or via configuration, and
  2. Allow use of names as explicit names, not implicit

I think I'm ok without (2), but (1) is an open question.

What would you do about multiple annotated constuctors then?

@Shredder121 good question. At this point, it'd likely be "throw an exception to indicate ambiguity". One could also consider simple resolution, and perhaps configurable resolution handler. But simplest heuristic would probably be "use one with most parameters".

"use one with most parameters"

That's indeed one heuristic.

Maybe with ambiguity require @JsonCreator again?
Although it sounds like that would create more problems than it solves.

This is a recurring concern, and aside from this annotation, also for full auto-detection.
In many other cases users can just take care not to annotate multiple ones, but with frameworks like Lombok and auto-values there isn't that control.

Yep, same problem here. Using lombok and Jackson suddenly started to use the all-arguments constructor even in the case not all fields are set in the JSON object. This results in the unset fields becoming null which overrides their default values which are set on object level default 馃憥

For anyone else who stumbles upon this - as a result of this change, on upgrading to Jackson 2.7, trying to deserialize a class which is

  • annotated with Lombok's @AllArgsConstructor annotation, and
  • has a constructor annotated with @JsonCreator

I started having errors along the following lines:

com.fasterxml.jackson.databind.JsonMappingException: Conflicting property-based creators: already had explicitly marked
[constructor for com.package.class, annotations: {interface java.beans.[email protected](value=[all, my, instance, variables)}], encountered
[constructor for com.package.class, annotations: {interface com.fasterxml.jackson.annotation.[email protected](mode=DEFAULT)}]

This is because under the covers Lombok's constructors use the ConstructorProperties annotation by default. The problem can be fixed by setting the following value.
lombok.anyConstructor.suppressConstructorProperties = true.

Hope that's useful...

Thank you @oliverlockwood. Also: it is really important to use latest 2.7 version; currently 2.7.8. There have been many fixes, and at least one directly affects handling of creator properties for static factory methods.

Any MapperFeature to disable using of ConstructorProperties ?
AUTO_DETECT_CREATORS seems not working. @cowtowncoder

Unfortunately, this breaks the @JsonSetter feature:

class Pojo {
    @JsonRawValue
    String json;

    @ConstructorProperties({"json"})
    public Pojo(String json) {
        this.json = json;
    }

    public Pojo(){}

    public String getJson() {
        return json;
    }

    @JsonSetter("json")
    public void setJson(JsonNode node) {
        this.json = node.toString();
    }

    public void setJson(String json) {
        this.json = json;
    }
}

public class JsonRawValueDeserializationTest {
    ObjectMapper mapper = new ObjectMapper();

    @Test
    public void test() throws IOException {
        Pojo pojo = new Pojo("{\"foo\":18}");

        String output = mapper.writeValueAsString(pojo);
        assertEquals("{\"json\":{\"foo\":18}}", output);

        // The following throws Exception
        Pojo deserialized = mapper.readValue(output, Pojo.class);
        assertEquals("{\"foo\":18}", deserialized.getJson());
    }
}

@tsabirgaliev If there is a problem, please file a new issue, referencing back to this issue as a root cause. Due to length of discussion it is not easy to see how things fit together.
In addition to test (which is useful, thank you!), issue should state version affected.

@ericxiong Yes, MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES -- auto-detection is somewhat orthogonal here.

Was this page helpful?
0 / 5 - 0 ratings