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; }
Advertisement
Answer
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.