Issue
What is the propper way to describe json body like this in spring-boot app?
{
"name": "name",
"releaseDate": "2000-01-01",
"description": "desc",
"duration": 10,
"rate": 1,
"mpa": { "id": 3},
"genres": [{ "id": 1}]
}
For now i have class like bellow, but i have problem with serialization of mpa
and genres
fields.
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Film extends Entity implements Comparable<Film> {
@Builder
public Film(long id, String name, @NonNull String description, @NonNull LocalDate releaseDate, @NonNull int duration, List<Genre> genres, Rating mpa, Set<Long> likes) {
super(id);
this.name = name;
this.description = description;
this.releaseDate = releaseDate;
this.duration = duration;
this.genres = genres;
this.mpa = mpa;
this.likes = likes;
}
@NotBlank
private final String name;
@NonNull
@Size(max = 200, message = "Description name longer than 200 symbols")
private final String description;
@NonNull
@Past
@JsonFormat(pattern = "yyyy-MM-dd", shape = JsonFormat.Shape.STRING)
private LocalDate releaseDate;
@NonNull
@Positive
private int duration;
private Rating mpa;
private List<Genre> genres;
@Setter(value = AccessLevel.PRIVATE)
private Set<Long> likes;
}
Genre
and Rating
:
@Data
public class Genre {
@Positive
private final long id;
}
@Data
public class Rating {
@Positive
private final long id;
}
Solution
Jackson ObjectMapper
cannot create the object, because neither default constructor exists nor any other creator is provided (e.g. @JsonCreator
, @ConstructorProperties
).
You also have a rate
property that is not defined in the Film
class, which will cause problems unless you use the @JsonIgnoreProperties
annotation (possibly with ignoreUnknown
attribute set to true
) or configure the ObjectMapper globally (DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
). It could also be that you wanted the likes
property to handle that - anyway, it should be fixed.
I've also stumbled upon lack of the jackson-datatype-jsr310
dependency, but maybe your project already has it (it's required for the Java 8 classes like LocalDate
).
There are different ways to solve the first problem described above, but generally you need to either provide a default constructor or define a creator for Jackson.
If you don't want to expose the default constructor, you can change the settings of the ObjectMapper (for Spring Boot read about Jackson2ObjectMapperBuilder
) to allow usage of private creators (setVisibility
method). In Lombok there's an annotation: @NoArgsConstructor
. To limit the visibility, use the access
annotation attribute.
The creator can be handled by annotating the constructor with ordered argument names:
@ConstructorProperties({"id", "name", "description", "releaseDate", "duration", "genres", "mpa", "likes"})
. It gets complicated with the Genre
and Rating
classes as you do not have an explicit access to their constructors. You could either create them and mark appropriately or create a lombok.config file in your project's root directory and inside the file define the property:
lombok.anyConstructor.addConstructorProperties = true
This way Lombok will automatically add the @ConstructorProperties
annotations to the classes.
I've created a GitHub repository with the tests for the solution - you can find it here. The lombok.config file is also included, as well as the fixed Film class.
Answered By - Jonasz
Answer Checked By - Terry (JavaFixing Volunteer)