Home > Software design >  Accept Arabic date as query parameter in Spring Boot
Accept Arabic date as query parameter in Spring Boot

Time:07-31

I want to accept Arabic date in the query parameter

ex: ٢٠٢١-٠٧-٢٠ the English version of this date is 2021-07-20 I have found this soltion

DecimalStyle defaultDecimalStyle
        = DateTimeFormatter.ISO_LOCAL_DATE.getDecimalStyle();
DateTimeFormatter arabicDateFormatter = DateTimeFormatter.ISO_LOCAL_DATE
        .withDecimalStyle(defaultDecimalStyle.withZeroDigit('\u0660'));

String encodedArabicDateStr = "٢٠١٩-٠٤-١٥";
String arabicDateStr
        = URLDecoder.decode(encodedArabicDateStr, StandardCharsets.UTF_8);
LocalDate date = LocalDate.parse(arabicDateStr, arabicDateFormatter);
System.out.println("Parsed date: "   date);

And it works as expected

now, in my Spring Boot application, I handle the LocalDate with @DateTimeFormat with pattern

@DateTimeFormat(pattern = "yyyy-MM-dd") val fromDate: LocalDate

How I can tell the Spring Boot to use a custom method to parse the date? The original problem is when someone send Arabic date the application it will crash with the below error:

org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'dateRange' on field 'fromDate': rejected value [٢٠٢١-٠٧-٢٠];
codes [typeMismatch.dateRange.fromDate,typeMismatch.fromDate,typeMismatch.java.time.LocalDate,typeMismatch];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [dateRange.fromDate,fromDate];
arguments [];
default message [fromDate]];
default message [Failed to convert value of type 'java.lang.String[]' to required type 'java.time.LocalDate';
nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.format.annotation.DateTimeFormat java.time.LocalDate] for value '٢٠٢١-٠٧-٢٠'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [٢٠٢١-٠٧-٢٠]]

I have tried:

  • update request values by filter but it does not work.
  • FormattingConversionService like this sample.

CodePudding user response:

You can create your own custom deserializer for LocalDate which will contain the format field.

Creating of custom deserializer for LocalDate

public class CustomDateDeserializer extends JsonDeserializer<LocalDate> {
    @Override
    public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {

        DecimalStyle defaultDecimalStyle = DateTimeFormatter.ISO_LOCAL_DATE.getDecimalStyle();
        DateTimeFormatter arabicDateFormatter = DateTimeFormatter.ISO_LOCAL_DATE.withDecimalStyle(defaultDecimalStyle.withZeroDigit('\u0660'));

        String arabicDateStr = URLDecoder.decode(jsonParser.getText(), StandardCharsets.UTF_8);
        return LocalDate.parse(arabicDateStr, arabicDateFormatter);
    }
}

Using the custom deserializer for LocalDate

@JsonSerialize(using = CustomDateDeserializer.class)
private LocalDate fromDate;

CodePudding user response:

Since you need to parse the date from query parameter, you need to implement the logic as a Converter.

@Component
public class ArabicDateConverter implements Converter<String, LocalDate> {

  private final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE
          .withDecimalStyle(DateTimeFormatter.ISO_LOCAL_DATE.getDecimalStyle().withZeroDigit('\u0660'));

  @Override
  public LocalDate convert(String source) {
    return LocalDate.parse(source, formatter);
  }
}

Note @Component, it's used to register the converter.

Keep in mind that this converts only arabic dates, it will fail for other formats. If you need to parse others, you may need to keep a list of possible formats to iterate over until something matches.

  • Related