I have an input DTO in a Spring MVC request that I want to validate, specifically the ZonedDateTime field should at most contain 3 digits at millisecond level, i.e. it cannot be the full nanoseconds precision. Other than that the input request should follow the ISO datetime format. I could make the field a String and then just restrict it with a regex but I prefer to keep it as an ZonedDateTime so I don't need to parse it again.
The object looks like this:
@Data
public class MeasurementDTO {
private ZonedDateTime date;
@Digits(integer = 20, fraction = 8) private BigDecimal value;
}
And this is a nested DTO where the parent object comes in as a @RequestBody
with the @Valid
annotation.
I've tried @JsonFormat but I can't seem to restrict the millisecond part. Is there any way to do this or should I just parse it myself as a String and then deal with it? Or even just leave it at ZonedDateTime and then check the nanosecond component to see if it's there in a custom validator?
Thanks to Tim's answer I remembered Java Date
s don't have more precision than millis anyway, so I updated the question to use ZonedDateTime
s which do have up to nanosecond precision. I do want to be able to give the user a warning if they do attempt to pass more precision, if using Date this information would just get swallowed.
I did this the hard way with a custom validator. I am still welcoming answers that do this in a more concise way though.
Here's my solution:
I added an annotation @ValidMeasurementInput
@Documented
@Constraint(validatedBy = MeasurementValidator.class)
@Target({TYPE, FIELD, ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface ValidMeasurementInput {
String message() default "Invalid measurement input";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And implemented a custom validator
public class MeasurementValidator implements ConstraintValidator<ValidMeasurementInput, MetricsDTO> {
@Override
public boolean isValid(MetricsDTO metricsDTO, ConstraintValidatorContext context) {
...
}
}
Somewhere in this class among some other validations is this code:
int nano = measurementDTO.getDate().getNano();
int remainderAfterMillis = nano % 1000000;
if (remainderAfterMillis != 0)
valid = false;
And of course I added the @ValidMeasurementInput
to my DTO.