How to create default constructor for immutable class
You can use a Jackson factory (method annotated with @JsonCreator) that reads fields off a map and calls your non-default constructor:
class School {
//fields
public School(String id, String name) {
this.schoolId = id;
this.schoolName = name;
}
@JsonCreator
public static School create(Map<String, Object> object) {
return new School((String) object.get("schoolId"),
(String) object.get("schoolName"));
}
//getters
}
Jackson will call the create
method with a Map
version of the json. And this effectively solves the problem.
I believe your question looks for a Jackson solution, rather than a new pattern/style.
TL;DR: using lombok and avoiding a default constructor
- make immutable data class using
@Value
- annotate all your fields with
@JsonProperty("name-of-property")
- add
lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty
to yourlombok.config
to copy those to generated constructors - create an all-args constructor annotated with
@JsonCreator
example:
@Value
@AllArgsConstructor(onConstructor_ = @JsonCreator)
class School {
@JsonProperty("schoolId")
String schoolId;
@JsonProperty("schoolName")
String schoolName;
}
long answer
There is an imo better alternative to a static factory method annotated with @JsonCreator
, and that is having a constructor for all Elements (as is required for immutable classes anyway). Annotate that with @JsonCreator
and also annotate all parameters with @JsonProperty
like this:
class School {
//fields
@JsonCreator
public School(
@JsonProperty("id") String id,
@JsonProperty("name") String name) {
this.schoolId = id;
this.schoolName = name;
}
//getters
}
Those are the options the @JsonCreator
annotation gives you. It describes them like this in its documentation:
- Single-argument constructor/factory method without JsonProperty annotation for the argument: if so, this is so-called "delegate creator", in which case Jackson first binds JSON into type of the argument, and then calls creator. This is often used in conjunction with JsonValue (used for serialization).
- Constructor/factory method where every argument is annotated with either JsonProperty or JacksonInject, to indicate name of property to bind to
You might not even need to explicitly specify the parameter name under some circumstances. The documentation regarding that for @JsonCreator
further states:
Also note that all JsonProperty annotations must specify actual name (NOT empty String for "default") unless you use one of extension modules that can detect parameter name; this because default JDK versions before 8 have not been able to store and/or retrieve parameter names from bytecode. But with JDK 8 (or using helper libraries such as Paranamer, or other JVM languages like Scala or Kotlin), specifying name is optional.
Alternatively this will also work nicely with lombok version 1.18.3
or up, where you can add lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty
to your lombok.config
and therefore have it copy the JsonProperty
annotations to the constructor, given that you do annotate all fields with it (which one should do anyway imo). To put the @JsonCreator
-annotation on the constructor, you can use the experimental onX feature. Using lombok's @Value
for immutable data classes, your DTO then might just look like this (untested):
@Value
//@AllArgsConstructor(onConstructor = @__(@JsonCreator)) // JDK7 or below
@AllArgsConstructor(onConstructor_ = @JsonCreator) // starting from JDK8
class School {
@JsonProperty("schoolId")
String schoolId;
@JsonProperty("schoolName")
String schoolName;
}