Our App developers wanted to send slightly different JSON elements in a single list, in order to make the App side implementation far less complicated. In order to make it possible to, I decided to implement inheritance hierarchy for the JSON input and the related java DTO classes.
The other possibility would be to have a JSON object with union of the fields from all Java classes and using @JsonIgnoreProperties(ignoreUnknown = true) for them. In this way you would be able to parse only the relevant field into the given Java class.
Advantages of using hierarchy in JSON related classes:
- more object oriented implementation
- you can declare different type of objects in a single JSON list, so far they have the same Java super class
- automatic REST input validation of the Spring framework still works for the classes. Yo do not need to define checking logic with mixed fields and complicated conditions.
- easy to add new types in the future, without effecting the existing ones
Disadvantages:
- you need to consider if storing different elements in a single list is a god idea at all
- complicated structure
- error prone type definition. It is not possible to define enumeration or any other typed value for the name property of @Type (see below)
- unit testing of the hierarchy is more complicated
- difficult to change break the hierarchy structure in case modification is needed later on
I created a common super class for the root of the inheritance and defined the field "actionType" for discriminator.
@Data @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "actionType") @JsonSubTypes({ @Type(value = TriggeredNotificationActionDto.class, name = "NOTIFICATION"), @Type(value = TriggeredDeviceActionDto.class, name = "DEVICE") }) public static class TriggeredActionDto implements Serializable { private String actionType; }
I defined the actual sub-classes (using annotation of Lombok project for getter and setter generation). I also defined validators for the fields. They are used by the Spring framework when the objects are acting as input parameter of a REST call.
In the class, containing the TriggeredActionDto elements, I marked the list as @Valid, in order to force validation of the each elements of the list.
@Data @EqualsAndHashCode(callSuper = true) public static class TriggeredNotificationActionDto extends TriggeredActionDto { public enum NotificationActionType { WEBSOCKET, PUSH; } @JsonProperty("notificationType") @NotNull(message = "notificationType must not be blank!") private NotificationActionType notificationType; }
@Data @EqualsAndHashCode(callSuper = true) public static class TriggeredDeviceActionDto extends TriggeredActionDto { @ApiModelProperty(example = "swagger001") @JsonProperty("deviceId") @NotNull(message = "Device id must not be blank!") private String deviceId; @ApiModelProperty(example = "1") @JsonProperty("channelId") @NotNull(message = "Channel id must not be blank!") @Range(min = 1, message = "Channel id must be a positive number") private int channelId; @ApiModelProperty(example = "1") @JsonProperty("value") @NotNull(message = "Value must not be blank!") private int value; ... }
In the class, containing the TriggeredActionDto elements, I marked the list as @Valid, in order to force validation of the each elements of the list.
@JsonProperty("tasks") @Valid private List<TriggeredActionDto> tasks;