3

I need to validate date using jsr annotations / spring rest

@Email(regexp = ".+@.+\\..+")
private String email;
@NotNull
@JsonFormat(pattern="yyyy-MM-dd")
private LocalDate dateOfBirth;

But its accepting below json request

{ "email": "eerwer@gmail.com","dateOfBirth": 7,}

and its parsing the date as 1970-01-07 (adding 7 days from 1970)

even below annotation is allowing numbers

 @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")

How can I invalidate this request

hacker
  • 342
  • 1
  • 4
  • 14
  • Could it be that `dateOfBirth` is part of `@RequestBody` object? Is the controller parameter annotated with `@Valid` there to trigger validation? – Karol Dowbecki Feb 25 '19 at 15:33
  • yes @Valid is there and validations are working on date format as well. Only issue is its allowing numbers – hacker Feb 25 '19 at 15:34
  • if i pass 7 , its adding 7 ms to the date – hacker Feb 25 '19 at 15:35
  • Your annotation is useless. Spring is not parsing the JSON. Your JSON parser (probably Jackson) is. See https://github.com/FasterXML/jackson-modules-java8/blob/master/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java. AFAIK, you'll need to use a custom deserializer to forbid numbers to be successfuly parsed. – JB Nizet Feb 25 '19 at 15:38
  • spring 5.1.4 RELEASE – hacker Feb 25 '19 at 15:38
  • @JB Nizet then @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm a z") should have worked right..? but even this is allowing numbers – hacker Feb 25 '19 at 15:45
  • Maybe [lenient = Boolean.FALSE](https://github.com/FasterXML/jackson-databind/issues/1733) would change the behavior? – yegodm Feb 25 '19 at 16:26
  • @yegodm unfortunately this doesn't change the behaviour – hacker Feb 25 '19 at 16:35
  • Not sure what is different with your setup. Running `mapper.readValue("{\"dateOfBirth\" : \"7\"}", Person.class)` with jsr310 module on my local produces an exception `com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type java.time.LocalDate from String "7": Text '7' could not be parsed at index 0...`. The `Person` class has one field `@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") private LocalDate dateOfBirth;`. – yegodm Feb 25 '19 at 16:57
  • @yegodm please change this "{\"dateOfBirth\" : \"7\"}" to "{\"dateOfBirth\" : 7}" This validation os working with "7" but not number 7 – hacker Feb 25 '19 at 16:59
  • With integer the error becomes `com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (VALUE_NUMBER_INT), expected VALUE_STRING: Expected array or string.`. Jackson is of version 2.8.3 – yegodm Feb 25 '19 at 17:01
  • And with Jackson 2.9.8 integer passes without error - something has changed between 2.8.3 -> 2.9.8. – yegodm Feb 25 '19 at 17:07
  • yes I am using 2.9.8 :-( . Is there any way i can restrict numbers with the request attributes – hacker Feb 25 '19 at 17:09
  • It is all about [this issue](https://github.com/FasterXML/jackson-modules-java8/issues/20). – yegodm Feb 25 '19 at 17:13
  • yes, it changed the behaviour and there doesn't seem to have any flag to disable this feature. – hacker Feb 25 '19 at 17:22
  • If no chance to downgrade to 2.8.x, perhaps custom serializer/desrializer then? – yegodm Feb 25 '19 at 17:26
  • i think writing custom serializer is better option.. But even then i think it will read the date as 1970-01-07 instead of 7 – hacker Feb 25 '19 at 17:35

3 Answers3

1

Ended up writing my own deserializer

class LocalDateDeserializer extends StdDeserializer<LocalDate> {

protected LocalDateDeserializer(){
    super(LocalDate.class);
}

@Override
public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {
    return LocalDate.parse(jp.readValueAs(String.class));

}
}
hacker
  • 342
  • 1
  • 4
  • 14
1

An alternative:

Notice that LocalDateDeserializer and LocalDateTimeDeserializer support internally leniency features.

That being said, a clean solution that you can make available through a Bean:

@Bean
ObjectMapper objectMapper() {

    final ObjectMapper objectMapper = new ObjectMapper();

    // make sure you added jackson-datatype-jsr310 dependency
    objectMapper.registerModule(new JavaTimeModule());

    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

    // option 1
    objectMapper.setDefaultLeniency(FALSE);

    // option 2
    objectMapper.configOverride(LocalDate.class).setFormat(JsonFormat.Value.forLeniency(FALSE));
    objectMapper.configOverride(LocalDateTime.class).setFormat(JsonFormat.Value.forLeniency(FALSE));


    return objectMapper;
}

Another in-place alternative could be:

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", lenient = OptBoolean.FALSE)

on the field/method itself

migo
  • 233
  • 1
  • 2
  • 8
  • Is there a way to do this with the Jackson2ObjectMapperBuilder or with a customizer? I could not find any ways to do that. – user6761124 Nov 24 '20 at 04:18
0

Validate date using jsr notationspring rest.

Add dependency

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

Entity field like this.

    @JsonFormat(pattern="yyyy-MM-dd")
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
    private LocalDate expireDate;

Json request like this.

{
    "expireDate" :"2015-01-01"
}
Sanjay
  • 2,481
  • 1
  • 13
  • 28