如何在Java中强制JSON日期使用指定格式且拒绝整数输入
Got it, let's tackle this problem. The issue with @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy") alone is that it only controls how the date is serialized to JSON, and while it hints at the deserialization pattern, Jackson's default LocalDate deserializer still allows numeric values (like 1) by treating them as epoch days. To block numeric inputs entirely and enforce only your desired string format, you have a few solid options:
Option 1: Custom Deserializer (Per-Field Control)
Create a custom deserializer that explicitly checks for string input and rejects non-string types, then parses the string with your exact date pattern.
Step 1: Write the Custom Deserializer
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import java.io.IOException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; public class LocalDateStringOnlyDeserializer extends StdDeserializer<LocalDate> { private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd-MM-yyyy"); public LocalDateStringOnlyDeserializer() { super(LocalDate.class); } @Override public LocalDate deserialize(JsonParser parser, DeserializationContext context) throws IOException { // First, check if the input is a string if (parser.getCurrentToken() != com.fasterxml.jackson.core.JsonToken.VALUE_STRING) { throw context.weirdStringException( parser.getText(), LocalDate.class, "Expected a string in format 'dd-MM-yyyy', got a non-string value" ); } // Then parse the string with the required format String dateInput = parser.getText(); try { return LocalDate.parse(dateInput, DATE_FORMATTER); } catch (DateTimeParseException e) { throw context.weirdStringException( dateInput, LocalDate.class, "Invalid date format. Must be 'dd-MM-yyyy'" ); } } }
Step 2: Apply the Deserializer to Your Field
Keep the @JsonFormat for serialization consistency, and add @JsonDeserialize to use your custom logic:
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.time.LocalDate; public class User { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy") @JsonDeserialize(using = LocalDateStringOnlyDeserializer.class) private LocalDate birthday; // Constructors, getters, setters... }
Option 2: Global Configuration (App-Wide Rule)
If you want this behavior for all LocalDate fields in your application, register the custom deserializer globally with Jackson's ObjectMapper.
For Spring Boot apps, add this configuration class:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JacksonDateConfig { @Bean public ObjectMapper objectMapper() { JavaTimeModule timeModule = new JavaTimeModule(); // Replace the default LocalDate deserializer with our custom one timeModule.addDeserializer(LocalDate.class, new LocalDateStringOnlyDeserializer()); return new ObjectMapper() .registerModule(timeModule); } }
Now every LocalDate field in your app will reject numeric inputs and only accept strings in dd-MM-yyyy format.
Option 3: Add Bean Validation (Extra Format Check)
To ensure the string itself matches your pattern even more strictly (e.g., block invalid strings like 32-13-2024), combine the deserializer with Bean Validation's @Pattern annotation:
import jakarta.validation.constraints.Pattern; // Add this to your birthday field @Pattern( regexp = "^\\d{2}-\\d{2}-\\d{4}$", message = "Birthday must follow the format 'dd-MM-yyyy'" )
Just remember: @Pattern only runs during bean validation (when you use @Valid on your request body), so it's a complement to the deserializer, not a replacement.
Why the Original @JsonFormat Didn't Work
Jackson's default LocalDateDeserializer supports both string parsing (using your pattern) and numeric parsing (treating numbers as epoch days). The @JsonFormat annotation doesn't disable this numeric handling—it just tells Jackson which pattern to use for strings. By replacing the deserializer, you're explicitly removing the numeric parsing logic entirely.
内容的提问来源于stack exchange,提问作者Noureddine Ad

